aboutsummaryrefslogtreecommitdiffstats
path: root/configserver/src/main/java/com/yahoo
diff options
context:
space:
mode:
authorHarald Musum <musum@yahooinc.com>2022-01-10 15:15:16 +0100
committerHarald Musum <musum@yahooinc.com>2022-01-10 15:15:16 +0100
commit7ddab351998d8e6d067dbd65dfb66714e8bfdf7a (patch)
tree6969dac4010b972d092d51f1df84aa145eb5d1b6 /configserver/src/main/java/com/yahoo
parentca9e75d4d99120a051b40900b9b2838b705faaad (diff)
Use an intermediate representation for service convergence
WAnt an intermediate representation that can be used e.g. for checking convergence before restarting. This is the first step, should have no changes for clients checking service/config convergence
Diffstat (limited to 'configserver/src/main/java/com/yahoo')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java20
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java116
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java100
3 files changed, 169 insertions, 67 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index 02ca4ce14c4..80194337daa 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -111,6 +111,8 @@ import java.util.stream.Collectors;
import static com.yahoo.config.model.api.container.ContainerServiceType.CONTAINER;
import static com.yahoo.config.model.api.container.ContainerServiceType.LOGSERVER_CONTAINER;
+import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceResponse;
+import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceListResponse;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.fileReferenceExistsOnDisk;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.getFileReferencesOnDisk;
import static com.yahoo.vespa.config.server.tenant.TenantRepository.HOSTED_VESPA_TENANT;
@@ -737,16 +739,22 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
// ---------------- Convergence ----------------------------------------------------------------
- public HttpResponse checkServiceForConfigConvergence(ApplicationId applicationId, String hostAndPort, URI uri,
- Duration timeout, Optional<Version> vespaVersion) {
- return convergeChecker.getServiceConfigGenerationResponse(getApplication(applicationId, vespaVersion), hostAndPort, uri, timeout);
+ public ServiceResponse checkServiceForConfigConvergence(ApplicationId applicationId,
+ String hostAndPort,
+ Duration timeout,
+ Optional<Version> vespaVersion) {
+ return convergeChecker.getServiceConfigGeneration(getApplication(applicationId, vespaVersion), hostAndPort, timeout);
}
- public HttpResponse servicesToCheckForConfigConvergence(ApplicationId applicationId, URI uri,
- Duration timeoutPerService, Optional<Version> vespaVersion) {
- return convergeChecker.getServiceConfigGenerationsResponse(getApplication(applicationId, vespaVersion), uri, timeoutPerService);
+ public ServiceListResponse servicesToCheckForConfigConvergence(ApplicationId applicationId,
+ URI uri,
+ Duration timeoutPerService,
+ Optional<Version> vespaVersion) {
+ return convergeChecker.getServiceConfigGenerations(getApplication(applicationId, vespaVersion), uri, timeoutPerService);
}
+ public ConfigConvergenceChecker configConvergenceChecker() { return convergeChecker; }
+
// ---------------- Logs ----------------------------------------------------------------
public HttpResponse getLogs(ApplicationId applicationId, Optional<String> hostname, String apiParams) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
index 24744a1b3b2..ad14cf4aab6 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
@@ -10,8 +10,6 @@ import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.PortInfo;
import com.yahoo.config.model.api.ServiceInfo;
-import com.yahoo.slime.Cursor;
-import com.yahoo.vespa.config.server.http.JSONResponse;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
@@ -92,27 +90,26 @@ public class ConfigConvergenceChecker extends AbstractComponent {
}
/** Check all services in given application. Returns the minimum current generation of all services */
- public JSONResponse getServiceConfigGenerationsResponse(Application application, URI requestUrl, Duration timeoutPerService) {
+ public ServiceListResponse getServiceConfigGenerations(Application application, URI uri, Duration timeoutPerService) {
Map<ServiceInfo, Long> currentGenerations = getServiceConfigGenerations(application, timeoutPerService);
long currentGeneration = currentGenerations.values().stream().mapToLong(Long::longValue).min().orElse(-1);
- return new ServiceListResponse(200, currentGenerations, requestUrl, application.getApplicationGeneration(),
- currentGeneration);
+ return new ServiceListResponse(currentGenerations, uri, application.getApplicationGeneration(), currentGeneration);
}
/** Check service identified by host and port in given application */
- public JSONResponse getServiceConfigGenerationResponse(Application application, String hostAndPortToCheck, URI requestUrl, Duration timeout) {
+ public ServiceResponse getServiceConfigGeneration(Application application, String hostAndPortToCheck, Duration timeout) {
Long wantedGeneration = application.getApplicationGeneration();
try (CloseableHttpAsyncClient client = createHttpClient()) {
client.start();
if ( ! hostInApplication(application, hostAndPortToCheck))
- return ServiceResponse.createHostNotFoundInAppResponse(requestUrl, hostAndPortToCheck, wantedGeneration);
+ return new ServiceResponse(ServiceResponse.Status.hostNotFound, wantedGeneration);
long currentGeneration = getServiceGeneration(client, URI.create("http://" + hostAndPortToCheck), timeout).get();
boolean converged = currentGeneration >= wantedGeneration;
- return ServiceResponse.createOkResponse(requestUrl, hostAndPortToCheck, wantedGeneration, currentGeneration, converged);
+ return new ServiceResponse(ServiceResponse.Status.ok, wantedGeneration, currentGeneration, converged);
} catch (InterruptedException | ExecutionException | CancellationException e) { // e.g. if we cannot connect to the service to find generation
- return ServiceResponse.createNotFoundResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage());
+ return new ServiceResponse(ServiceResponse.Status.notFound, wantedGeneration, e.getMessage());
} catch (Exception e) {
- return ServiceResponse.createErrorResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage());
+ return new ServiceResponse(ServiceResponse.Status.error, wantedGeneration, e.getMessage());
}
}
@@ -192,7 +189,7 @@ public class ConfigConvergenceChecker extends AbstractComponent {
return false;
}
- private static Optional<Integer> getStatePort(ServiceInfo service) {
+ public static Optional<Integer> getStatePort(ServiceInfo service) {
return service.getPorts().stream()
.filter(port -> port.getTags().contains("state"))
.map(PortInfo::getPort)
@@ -249,63 +246,70 @@ public class ConfigConvergenceChecker extends AbstractComponent {
.build();
}
- private static class ServiceListResponse extends JSONResponse {
-
- // Pre-condition: servicesToCheck has a state port
- private ServiceListResponse(int status, Map<ServiceInfo, Long> servicesToCheck, URI uri, long wantedGeneration,
- long currentGeneration) {
- super(status);
- Cursor serviceArray = object.setArray("services");
- servicesToCheck.forEach((service, generation) -> {
- Cursor serviceObject = serviceArray.addObject();
- String hostName = service.getHostName();
- int statePort = getStatePort(service).get();
- serviceObject.setString("host", hostName);
- serviceObject.setLong("port", statePort);
- serviceObject.setString("type", service.getServiceType());
- serviceObject.setString("url", uri.toString() + "/" + hostName + ":" + statePort);
- serviceObject.setLong("currentGeneration", generation);
- });
- object.setString("url", uri.toString());
- object.setLong("currentGeneration", currentGeneration);
- object.setLong("wantedGeneration", wantedGeneration);
- object.setBool("converged", currentGeneration >= wantedGeneration);
+ public static class ServiceResponse {
+
+ public enum Status { ok, notFound, hostNotFound, error }
+
+ public final Status status;
+ public final Long wantedGeneration;
+ public final Long currentGeneration;
+ public final boolean converged;
+ public final Optional<String> errorMessage;
+
+ public ServiceResponse(Status status, Long wantedGeneration) {
+ this(status, wantedGeneration, 0L);
}
- }
- private static class ServiceResponse extends JSONResponse {
+ public ServiceResponse(Status status, Long wantedGeneration, Long currentGeneration) {
+ this(status, wantedGeneration, currentGeneration, false);
+ }
- private ServiceResponse(int status, URI uri, String hostname, Long wantedGeneration) {
- super(status);
- object.setString("url", uri.toString());
- object.setString("host", hostname);
- object.setLong("wantedGeneration", wantedGeneration);
+ public ServiceResponse(Status status, Long wantedGeneration, Long currentGeneration, boolean converged) {
+ this(status, wantedGeneration, currentGeneration, converged, Optional.empty());
}
- static ServiceResponse createOkResponse(URI uri, String hostname, Long wantedGeneration, Long currentGeneration, boolean converged) {
- ServiceResponse serviceResponse = new ServiceResponse(200, uri, hostname, wantedGeneration);
- serviceResponse.object.setBool("converged", converged);
- serviceResponse.object.setLong("currentGeneration", currentGeneration);
- return serviceResponse;
+ public ServiceResponse(Status status, Long wantedGeneration, String errorMessage) {
+ this(status, wantedGeneration, 0L, false, Optional.ofNullable(errorMessage));
}
- static ServiceResponse createHostNotFoundInAppResponse(URI uri, String hostname, Long wantedGeneration) {
- ServiceResponse serviceResponse = new ServiceResponse(410, uri, hostname, wantedGeneration);
- serviceResponse.object.setString("problem", "Host:port (service) no longer part of application, refetch list of services.");
- return serviceResponse;
+ private ServiceResponse(Status status, Long wantedGeneration, Long currentGeneration, boolean converged, Optional<String> errorMessage) {
+ this.status = status;
+ this.wantedGeneration = wantedGeneration;
+ this.currentGeneration = currentGeneration;
+ this.converged = converged;
+ this.errorMessage = errorMessage;
}
- static ServiceResponse createErrorResponse(URI uri, String hostname, Long wantedGeneration, String error) {
- ServiceResponse serviceResponse = new ServiceResponse(500, uri, hostname, wantedGeneration);
- serviceResponse.object.setString("error", error);
- return serviceResponse;
+ }
+
+ public static class ServiceListResponse {
+
+ public final List<Service> services = new ArrayList<>();
+ public final URI uri;
+ public final long wantedGeneration;
+ public final long currentGeneration;
+
+ public ServiceListResponse(Map<ServiceInfo, Long> services, URI uri, long wantedGeneration, long currentGeneration) {
+ services.forEach((key, value) -> this.services.add(new Service(key, value)));
+ this.uri = uri;
+ this.wantedGeneration = wantedGeneration;
+ this.currentGeneration = currentGeneration;
}
- static ServiceResponse createNotFoundResponse(URI uri, String hostname, Long wantedGeneration, String error) {
- ServiceResponse serviceResponse = new ServiceResponse(404, uri, hostname, wantedGeneration);
- serviceResponse.object.setString("error", error);
- return serviceResponse;
+ public List<Service> services() { return services; }
+
+ public static class Service {
+
+ public final ServiceInfo serviceInfo;
+ public final Long currentGeneration;
+
+ public Service(ServiceInfo serviceInfo, Long currentGeneration) {
+ this.serviceInfo = serviceInfo;
+ this.currentGeneration = currentGeneration;
+ }
+
}
+
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
index 4dda141491c..0131517818d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
@@ -5,6 +5,7 @@ import com.google.inject.Inject;
import com.yahoo.component.Version;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.model.api.Model;
+import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostFilter;
import com.yahoo.config.provision.InstanceName;
@@ -16,13 +17,15 @@ import com.yahoo.jdisc.Response;
import com.yahoo.restapi.ErrorResponse;
import com.yahoo.restapi.MessageResponse;
import com.yahoo.restapi.Path;
+import com.yahoo.slime.Cursor;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.text.StringUtilities;
import com.yahoo.vespa.config.server.ApplicationRepository;
-import com.yahoo.vespa.config.server.application.ApplicationReindexing;
+import com.yahoo.vespa.config.server.application.ConfigConvergenceChecker;
import com.yahoo.vespa.config.server.http.ContentHandler;
import com.yahoo.vespa.config.server.http.ContentRequest;
import com.yahoo.vespa.config.server.http.HttpHandler;
+import com.yahoo.vespa.config.server.http.JSONResponse;
import com.yahoo.vespa.config.server.http.NotFoundException;
import com.yahoo.vespa.config.server.http.v2.request.ApplicationContentRequest;
import com.yahoo.vespa.config.server.http.v2.response.ApplicationSuspendedResponse;
@@ -33,6 +36,7 @@ import com.yahoo.vespa.config.server.http.v2.response.ReindexingResponse;
import com.yahoo.vespa.config.server.tenant.Tenant;
import java.io.IOException;
+import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
@@ -43,8 +47,11 @@ import java.util.StringJoiner;
import java.util.TreeMap;
import java.util.TreeSet;
+import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceListResponse;
+import static com.yahoo.vespa.config.server.application.ConfigConvergenceChecker.ServiceResponse;
import static com.yahoo.yolean.Exceptions.uncheck;
+
/**
* Operations on applications (delete, wait for config convergence, restart, application content etc.)
*
@@ -108,13 +115,21 @@ public class ApplicationHandler extends HttpHandler {
}
private HttpResponse listServiceConverge(ApplicationId applicationId, HttpRequest request) {
- return applicationRepository.servicesToCheckForConfigConvergence(applicationId, request.getUri(),
- getTimeoutFromRequest(request), getVespaVersionFromRequest(request));
+ ServiceListResponse response =
+ applicationRepository.servicesToCheckForConfigConvergence(applicationId,
+ request.getUri(),
+ getTimeoutFromRequest(request),
+ getVespaVersionFromRequest(request));
+ return new HttpServiceListResponse(response);
}
private HttpResponse checkServiceConverge(ApplicationId applicationId, String hostAndPort, HttpRequest request) {
- return applicationRepository.checkServiceForConfigConvergence(applicationId, hostAndPort, request.getUri(),
- getTimeoutFromRequest(request), getVespaVersionFromRequest(request));
+ ServiceResponse response =
+ applicationRepository.checkServiceForConfigConvergence(applicationId,
+ hostAndPort,
+ getTimeoutFromRequest(request),
+ getVespaVersionFromRequest(request));
+ return HttpServiceResponse.createResponse(response, hostAndPort, request.getUri());
}
private HttpResponse serviceStatusPage(ApplicationId applicationId, String service, String hostname, String pathSuffix) {
@@ -301,4 +316,79 @@ public class ApplicationHandler extends HttpHandler {
.map(Version::fromString);
}
+ static class HttpServiceResponse extends JSONResponse {
+
+ public static HttpServiceResponse createResponse(ConfigConvergenceChecker.ServiceResponse serviceResponse, String hostAndPort, URI uri) {
+ switch (serviceResponse.status) {
+ case ok:
+ return createOkResponse(uri, hostAndPort, serviceResponse.wantedGeneration, serviceResponse.currentGeneration, serviceResponse.converged);
+ case hostNotFound:
+ return createHostNotFoundInAppResponse(uri, hostAndPort, serviceResponse.wantedGeneration);
+ case notFound:
+ return createNotFoundResponse(uri, hostAndPort, serviceResponse.wantedGeneration, serviceResponse.errorMessage.orElse(""));
+ case error:
+ return createErrorResponse(uri, hostAndPort, serviceResponse.wantedGeneration, serviceResponse.errorMessage.orElse(""));
+ default:
+ throw new IllegalArgumentException("Unknown status " + serviceResponse.status);
+ }
+ }
+
+ private HttpServiceResponse(int status, URI uri, String hostname, Long wantedGeneration) {
+ super(status);
+ object.setString("url", uri.toString());
+ object.setString("host", hostname);
+ object.setLong("wantedGeneration", wantedGeneration);
+ }
+
+ private static HttpServiceResponse createOkResponse(URI uri, String hostname, Long wantedGeneration, Long currentGeneration, boolean converged) {
+ HttpServiceResponse serviceResponse = new HttpServiceResponse(200, uri, hostname, wantedGeneration);
+ serviceResponse.object.setBool("converged", converged);
+ serviceResponse.object.setLong("currentGeneration", currentGeneration);
+ return serviceResponse;
+ }
+
+ private static HttpServiceResponse createHostNotFoundInAppResponse(URI uri, String hostname, Long wantedGeneration) {
+ HttpServiceResponse serviceResponse = new HttpServiceResponse(410, uri, hostname, wantedGeneration);
+ serviceResponse.object.setString("problem", "Host:port (service) no longer part of application, refetch list of services.");
+ return serviceResponse;
+ }
+
+ private static HttpServiceResponse createErrorResponse(URI uri, String hostname, Long wantedGeneration, String error) {
+ HttpServiceResponse serviceResponse = new HttpServiceResponse(500, uri, hostname, wantedGeneration);
+ serviceResponse.object.setString("error", error);
+ return serviceResponse;
+ }
+
+ private static HttpServiceResponse createNotFoundResponse(URI uri, String hostname, Long wantedGeneration, String error) {
+ HttpServiceResponse serviceResponse = new HttpServiceResponse(404, uri, hostname, wantedGeneration);
+ serviceResponse.object.setString("error", error);
+ return serviceResponse;
+ }
+
+ }
+
+ static class HttpServiceListResponse extends JSONResponse {
+
+ // Pre-condition: servicesToCheck has a state port
+ public HttpServiceListResponse(ConfigConvergenceChecker.ServiceListResponse response) {
+ super(200);
+ Cursor serviceArray = object.setArray("services");
+ response.services().forEach((service) -> {
+ ServiceInfo serviceInfo = service.serviceInfo;
+ Cursor serviceObject = serviceArray.addObject();
+ String hostName = serviceInfo.getHostName();
+ int statePort = ConfigConvergenceChecker.getStatePort(serviceInfo).get();
+ serviceObject.setString("host", hostName);
+ serviceObject.setLong("port", statePort);
+ serviceObject.setString("type", serviceInfo.getServiceType());
+ serviceObject.setString("url", response.uri.toString() + "/" + hostName + ":" + statePort);
+ serviceObject.setLong("currentGeneration", service.currentGeneration);
+ });
+ object.setString("url", response.uri.toString());
+ object.setLong("currentGeneration", response.currentGeneration);
+ object.setLong("wantedGeneration", response.wantedGeneration);
+ object.setBool("converged", response.currentGeneration >= response.wantedGeneration);
+ }
+ }
+
}