diff options
87 files changed, 902 insertions, 378 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java index 05c736e050b..fc1572f6593 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java @@ -96,6 +96,7 @@ public interface ModelContext { @ModelFeatureFlag(owners = {"hmusum"}) default double resourceLimitMemory() { return 0.8; } @ModelFeatureFlag(owners = {"geirst", "vekterli"}) default double minNodeRatioPerGroup() { return 0.0; } @ModelFeatureFlag(owners = {"arnej"}) default boolean newLocationBrokerLogic() { return true; } + @ModelFeatureFlag(owners = {"bjorncs"}) default int maxConnectionLifeInHosted() { return 45; } } /** Warning: As elsewhere in this package, do not make backwards incompatible changes that will break old config models! */ diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java index 07b1de3fdad..2b9551c2858 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java @@ -17,7 +17,6 @@ public class Slobrok extends AbstractService implements StateserverConfig.Produc private static final long serialVersionUID = 1L; public final static int BASEPORT = 19099; - public final boolean useNewLogic; @Override public void getConfig(StateserverConfig.Builder builder) { @@ -32,7 +31,6 @@ public class Slobrok extends AbstractService implements StateserverConfig.Produc ModelContext.FeatureFlags featureFlags) { super(parent, "slobrok." + index); - this.useNewLogic = featureFlags.newLocationBrokerLogic(); portsMeta.on(0).tag("rpc").tag("admin").tag("status"); portsMeta.on(1).tag("http").tag("state"); setProp("index", index); @@ -50,10 +48,7 @@ public class Slobrok extends AbstractService implements StateserverConfig.Produc } public String getStartupCommand() { - if (useNewLogic) { - return "exec $ROOT/sbin/vespa-slobrok -N -p " + getRpcPort() + " -c " + getConfigId(); - } - return "exec $ROOT/sbin/vespa-slobrok -p " + getRpcPort() + " -c " + getConfigId(); + return "exec $ROOT/sbin/vespa-slobrok -N -p " + getRpcPort() + " -c " + getConfigId(); } @Override diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java index c91c4e92486..0638679b817 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java @@ -181,6 +181,8 @@ public class VespaMetricSet { metrics.add(new Metric("jdisc.deactivated_containers.with_retained_refs.last")); metrics.add(new Metric("athenz-tenant-cert.expiry.seconds.last")); + metrics.add(new Metric("node-certificate.expiry.seconds.last")); + metrics.add(new Metric("container-iam-role.expiry.seconds.last")); metrics.add(new Metric("jdisc.http.request.prematurely_closed.rate")); addMetric(metrics, "jdisc.http.request.requests_per_connection", List.of("sum", "count", "min", "max", "average")); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java index 9e1407ec93e..1c2da13738f 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java @@ -22,9 +22,6 @@ public class ContainerDocumentApi { public static final String DOCUMENT_V1_PREFIX = "/document/v1"; - private static final int FALLBACK_MAX_POOL_SIZE = 0; // Use fallback based on actual logical core count on host - private static final int FALLBACK_CORE_POOL_SIZE = 0; // Use fallback based on actual logical core count on host - public ContainerDocumentApi(ContainerCluster<?> cluster, Options options) { addRestApiHandler(cluster, options); addFeedHandler(cluster, options); @@ -102,22 +99,7 @@ public class ContainerDocumentApi { // User options overrides below configuration if (hasUserOptions()) return; - - builder.maxThreads(maxPoolSize()); - builder.minThreads(minPoolSize()); - builder.queueSize(500); - } - - private int maxPoolSize() { - double vcpu = cluster.vcpu().orElse(0); - if (vcpu == 0) return FALLBACK_MAX_POOL_SIZE; - return Math.max(2, (int)Math.ceil(vcpu * 4.0)); - } - - private int minPoolSize() { - double vcpu = cluster.vcpu().orElse(0); - if (vcpu == 0) return FALLBACK_CORE_POOL_SIZE; - return Math.max(1, (int)Math.ceil(vcpu * 2.0)); + builder.maxThreads(-4).minThreads(-4).queueSize(500); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java index f5b168958c0..d05650b10b5 100755 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java @@ -70,7 +70,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.OptionalDouble; import java.util.Set; /** @@ -652,12 +651,4 @@ public abstract class ContainerCluster<CONTAINER extends Container> public boolean getDeferChangesUntilRestart() { return deferChangesUntilRestart; } - /** Effective vcpu for the containers in cluster. Use this value as scale factor for performance/resource tuning. **/ - public OptionalDouble vcpu() { - return getContainers().stream() - .filter(c -> c.getHostResource() != null && c.getHostResource().realResources() != null) - .mapToDouble(c -> c.getHostResource().realResources().vcpu()) - .max(); // Use highest vcpu as scale factor - } - } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java index 4d665b0bb58..e74eac09558 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java @@ -46,13 +46,7 @@ class DefaultThreadpoolProvider extends SimpleComponent implements ThreadpoolCon return; } - double vcpu = cluster.vcpu().orElse(0); - if (vcpu == 0) return; - - // Configuration is currently identical to the search handler's threadpool - int workerThreads = Math.max(8, (int)Math.ceil(vcpu * 2.0)); - builder.maxthreads(workerThreads); - builder.corePoolSize(workerThreads); - builder.queueSize((int)(workerThreads * 40.0)); + if (!cluster.isHostedVespa()) return; + builder.corePoolSize(-2).maxthreads(-2).queueSize(-40); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java index fc41da43479..33e712feeb1 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java @@ -9,8 +9,6 @@ import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; -import java.util.OptionalInt; - /** * @author Tony Vaagenes * @author gjoranv @@ -32,11 +30,10 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL public AccessLogComponent(ContainerCluster<?> cluster, AccessLogType logType, CompressionType compressionType, String clusterName, boolean isHostedVespa) { - this(cluster, logType, compressionType, + this(logType, compressionType, String.format("logs/vespa/qrs/%s.%s.%s", capitalize(logType.name()), clusterName, "%Y%m%d%H%M%S"), null, null, isHostedVespa, - capitalize(logType.name()) + "." + clusterName, - queueSize(cluster).orElse(-1), + capitalize(logType.name()) + "." + clusterName, -1, ((cluster instanceof ApplicationContainerCluster) ? 4*1024*1024 : null)); } @@ -44,8 +41,7 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL return name.substring(0, 1).toUpperCase() + name.substring(1); } - public AccessLogComponent(ContainerCluster<?> cluster, - AccessLogType logType, + public AccessLogComponent(AccessLogType logType, CompressionType compressionType, String fileNamePattern, String rotationInterval, @@ -69,13 +65,6 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL throw new RuntimeException("File name pattern required when configuring access log."); } - private static OptionalInt queueSize(ContainerCluster<?> cluster) { - if (cluster == null) return OptionalInt.empty(); - double vcpu = cluster.vcpu().orElse(0); - if (vcpu <= 0) return OptionalInt.empty(); - return OptionalInt.of((int) Math.max(4096, Math.ceil(vcpu * 256.0))); - } - private static String accessLogClass(AccessLogType logType) { switch (logType) { case queryAccessLog: @@ -105,9 +94,7 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL } else if (isHostedVespa) { builder.compressOnRotation(true); } - if (queueSize >= 0) { - builder.queueSize(queueSize); - } + builder.queueSize(queueSize); if (bufferSize != null) { builder.bufferSize(bufferSize); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java index 0b51cd163a2..4afac252085 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java @@ -6,38 +6,25 @@ import com.yahoo.container.logging.ConnectionLog; import com.yahoo.container.logging.ConnectionLogConfig; import com.yahoo.vespa.model.container.ContainerCluster; -import java.util.OptionalInt; - public class ConnectionLogComponent extends SimpleComponent implements ConnectionLogConfig.Producer { private final String logDirectoryName; private final String clusterName; - private final int queueSize; public ConnectionLogComponent(ContainerCluster<?> cluster, Class<? extends ConnectionLog> cls, String logDirectoryName) { - this(cluster, cls, logDirectoryName, cluster.getName()); + this(cls, logDirectoryName, cluster.getName()); } - public ConnectionLogComponent(ContainerCluster<?> cluster, Class<? extends ConnectionLog> cls, String logDirectoryName, String clusterName) { + public ConnectionLogComponent(Class<? extends ConnectionLog> cls, String logDirectoryName, String clusterName) { super(cls.getName()); this.logDirectoryName = logDirectoryName; this.clusterName = clusterName; - this.queueSize = queueSize(cluster).orElse(-1); - } - - private static OptionalInt queueSize(ContainerCluster<?> cluster) { - if (cluster == null) return OptionalInt.empty(); - double vcpu = cluster.vcpu().orElse(0); - if (vcpu <= 0) return OptionalInt.empty(); - return OptionalInt.of((int) Math.max(4096, Math.ceil(vcpu * 512.0))); } @Override public void getConfig(ConnectionLogConfig.Builder builder) { builder.cluster(clusterName); builder.logDirectoryName(logDirectoryName); - if (queueSize >= 0) { - builder.queueSize(queueSize); - } + builder.queueSize(-1); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java index d13709114bf..a2181f317a6 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java @@ -70,18 +70,11 @@ public class JettyHttpServer extends SimpleComponent implements ServerConfig.Pro private void configureJettyThreadpool(ServerConfig.Builder builder) { if (cluster == null) return; if (cluster instanceof ApplicationContainerCluster) { - configureApplicationClusterJettyThreadPool(builder); + if (isHostedVespa) builder.minWorkerThreads(-1).maxWorkerThreads(-1); } else { builder.minWorkerThreads(4).maxWorkerThreads(4); } } - private void configureApplicationClusterJettyThreadPool(ServerConfig.Builder builder) { - double vcpu = cluster.vcpu().orElse(0); - if (vcpu > 0) { - int threads = 16 + (int) Math.ceil(vcpu); - builder.minWorkerThreads(threads).maxWorkerThreads(threads); - } - } static ComponentModel providerComponentModel(String parentId, String className) { final ComponentSpecification classNameSpec = new ComponentSpecification( diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java index b25463b8547..aab417db1e2 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java @@ -25,16 +25,17 @@ public class HostedSslConnectorFactory extends ConnectorFactory { private final boolean enforceClientAuth; private final boolean enforceHandshakeClientAuth; private final Collection<String> tlsCiphersOverride; + private final Duration maxConnectionLife; /** * Create connector factory that uses a certificate provided by the config-model / configserver and default hosted Vespa truststore. */ public static HostedSslConnectorFactory withProvidedCertificate( String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth, - Collection<String> tlsCiphersOverride) { + Collection<String> tlsCiphersOverride, Duration maxConnectionLife) { ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider( serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null, enforceHandshakeClientAuth); - return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride); + return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride, maxConnectionLife); } /** @@ -42,25 +43,28 @@ public class HostedSslConnectorFactory extends ConnectorFactory { */ public static HostedSslConnectorFactory withProvidedCertificateAndTruststore( String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates, - Collection<String> tlsCiphersOverride) { + Collection<String> tlsCiphersOverride, Duration maxConnectionLife) { ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider( serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false); - return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride); + return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride, maxConnectionLife); } /** * Create connector factory that uses the default certificate and truststore provided by Vespa (through Vespa-global TLS configuration). */ - public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName, Collection<String> tlsCiphersOverride) { - return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride); + public static HostedSslConnectorFactory withDefaultCertificateAndTruststore( + String serverName, Collection<String> tlsCiphersOverride, Duration maxConnectionLife) { + return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride, maxConnectionLife); } private HostedSslConnectorFactory(SslProvider sslProvider, boolean enforceClientAuth, - boolean enforceHandshakeClientAuth, Collection<String> tlsCiphersOverride) { + boolean enforceHandshakeClientAuth, Collection<String> tlsCiphersOverride, + Duration maxConnectionLife) { super(new Builder("tls4443", 4443).sslProvider(sslProvider)); this.enforceClientAuth = enforceClientAuth; this.enforceHandshakeClientAuth = enforceHandshakeClientAuth; this.tlsCiphersOverride = tlsCiphersOverride; + this.maxConnectionLife = maxConnectionLife; } private static ConfiguredDirectSslProvider createConfiguredDirectSslProvider( @@ -96,6 +100,6 @@ public class HostedSslConnectorFactory extends ConnectorFactory { connectorBuilder .proxyProtocol(new ConnectorConfig.ProxyProtocol.Builder().enabled(true).mixedMode(true)) .idleTimeout(Duration.ofSeconds(30).toSeconds()) - .maxConnectionLife(Duration.ofMinutes(10).toSeconds()); + .maxConnectionLife(maxConnectionLife.toSeconds()); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java index d7812e9b4ff..62f04edf0ae 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java @@ -55,7 +55,6 @@ public class AccessLogBuilder { @Override protected AccessLogComponent doBuild(DeployState deployState, AbstractConfigProducer<?> ancestor, Element spec) { return new AccessLogComponent( - (ContainerCluster<?>) ancestor, accessLogType, compressionType(spec, isHostedVespa), fileNamePattern(spec), diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java index a1f52cca9fd..75f11020c15 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java @@ -6,7 +6,6 @@ import com.yahoo.config.model.deploy.DeployState; import com.yahoo.container.logging.FileConnectionLog; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerModel; -import com.yahoo.vespa.model.container.ContainerModelEvaluation; import com.yahoo.vespa.model.container.component.AccessLogComponent; import com.yahoo.vespa.model.container.component.ConnectionLogComponent; import com.yahoo.vespa.model.container.configserver.ConfigserverCluster; @@ -48,7 +47,7 @@ public class ConfigServerContainerModelBuilder extends ContainerModelBuilder { if (isHosted()){ cluster.addComponent( new AccessLogComponent( - cluster, AccessLogComponent.AccessLogType.jsonAccessLog, AccessLogComponent.CompressionType.ZSTD, + AccessLogComponent.AccessLogType.jsonAccessLog, AccessLogComponent.CompressionType.ZSTD, "logs/vespa/configserver/access-json.log.%Y%m%d%H%M%S", null, true, true, "access-json.log", 1024,256*1024)); cluster.addComponent(new ConnectionLogComponent(cluster, FileConnectionLog.class, "configserver")); } else { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index c62dee68b2d..2622a9e50b7 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -92,6 +92,7 @@ import org.w3c.dom.Node; import java.net.URI; import java.security.cert.X509Certificate; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -436,6 +437,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { // If the deployment contains certificate/private key reference, setup TLS port HostedSslConnectorFactory connectorFactory; Collection<String> tlsCiphersOverride = deployState.getProperties().tlsCiphersOverride(); + Duration maxConnectionLife = Duration.ofSeconds(deployState.featureFlags().maxConnectionLifeInHosted()); if (deployState.endpointCertificateSecrets().isPresent()) { boolean authorizeClient = deployState.zone().system().isPublic(); if (authorizeClient && deployState.tlsClientAuthority().isEmpty()) { @@ -450,11 +452,11 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { connectorFactory = authorizeClient ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore( - serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState), tlsCiphersOverride) + serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState), tlsCiphersOverride, maxConnectionLife) : HostedSslConnectorFactory.withProvidedCertificate( - serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride); + serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride, maxConnectionLife); } else { - connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, tlsCiphersOverride); + connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, tlsCiphersOverride, maxConnectionLife); } cluster.getHttp().getAccessControl().ifPresent(accessControl -> accessControl.configureHostedConnector(connectorFactory)); server.addConnector(connectorFactory); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java index 95b333f4e52..d3001bd9100 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java @@ -48,22 +48,10 @@ class SearchHandler extends ProcessingHandler<SearchChains> { // User options overrides below configuration if (hasUserOptions()) return; - - double vcpu = cluster.vcpu().orElse(0); - if (vcpu == 0) { - builder.maxThreads(500); - builder.minThreads(500); - builder.queueSize(0); + if (cluster.isHostedVespa()) { + builder.maxThreads(-2).minThreads(-2).queueSize(-40); } else { - // Controls max number of concurrent requests per container - int workerThreads = Math.max(8, (int)Math.ceil(vcpu * 2.0)); - builder.maxThreads(workerThreads); - builder.minThreads(workerThreads); - - // This controls your burst handling capability. - // 0 => No extra burst handling beyond you max concurrent requests (maxthreads). - // N => N times max concurrent requests as a buffer for handling bursts - builder.queueSize((int)(workerThreads * 40.0)); + builder.maxThreads(500).minThreads(500).queueSize(0); } } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java index a66ea736a5b..a2887ae76c1 100755 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java @@ -277,45 +277,37 @@ public class ContainerClusterTest { } @Test - public void config_for_default_threadpool_provider_scales_with_node_resources() { - HostProvisionerWithCustomRealResource hostProvisioner = new HostProvisionerWithCustomRealResource(); + public void config_for_default_threadpool_provider_scales_with_node_resources_in_hosted() { MockRoot root = new MockRoot( "foo", new DeployState.Builder() + .properties(new TestProperties().setHostedVespa(true)) .applicationPackage(new MockApplicationPackage.Builder().build()) - .modelHostProvisioner(hostProvisioner) .build()); ApplicationContainerCluster cluster = createContainerCluster(root, false); - HostResource hostResource = new HostResource( - new Host(null, "host-c1"), - hostProvisioner.allocateHost("host-c1")); - addContainerWithHostResource(root, cluster, "c1", hostResource); + addContainer(root, cluster, "c1", "host-c1"); root.freezeModelTopology(); ThreadpoolConfig threadpoolConfig = root.getConfig(ThreadpoolConfig.class, "container0/component/default-threadpool"); - assertEquals(8, threadpoolConfig.maxthreads()); - assertEquals(320, threadpoolConfig.queueSize()); + assertEquals(-2, threadpoolConfig.maxthreads()); + assertEquals(-40, threadpoolConfig.queueSize()); } @Test - public void jetty_threadpool_scales_with_node_resources() { - HostProvisionerWithCustomRealResource hostProvisioner = new HostProvisionerWithCustomRealResource(12); + public void jetty_threadpool_scales_with_node_resources_in_hosted() { MockRoot root = new MockRoot( "foo", new DeployState.Builder() + .properties(new TestProperties().setHostedVespa(true)) .applicationPackage(new MockApplicationPackage.Builder().build()) - .modelHostProvisioner(hostProvisioner) .build()); ApplicationContainerCluster cluster = createContainerCluster(root, false); - HostResource hostResource = new HostResource( - new Host(null, "host-c1"), - hostProvisioner.allocateHost("host-c1")); - addContainerWithHostResource(root, cluster, "c1", hostResource); + addContainer(root, cluster, "c1", "host-c1"); root.freezeModelTopology(); ServerConfig cfg = root.getConfig(ServerConfig.class, "container0/c1/DefaultHttpServer"); - assertEquals(28, cfg.maxWorkerThreads()); - assertEquals(28, cfg.minWorkerThreads()); + assertEquals(-1, cfg.maxWorkerThreads()); // Scale with cpu count observed by JVM + assertEquals(-1, cfg.minWorkerThreads()); // Scale with cpu count observed by JVM } @Test diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java index 80aafdd4ec7..06281c7b57e 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java @@ -117,7 +117,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase { assertNotNull(connectionLogComponent); ConnectionLogConfig config = root.getConfig(ConnectionLogConfig.class, "default/component/com.yahoo.container.logging.FileConnectionLog"); assertEquals("default", config.cluster()); - assertEquals(10000, config.queueSize()); + assertEquals(-1, config.queueSize()); assertEquals(256*1024, config.bufferSize()); } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java index 3c733d5109d..0176d5f6ffc 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java @@ -6,7 +6,6 @@ import com.yahoo.config.model.test.MockApplicationPackage; import com.yahoo.config.model.test.MockRoot; import com.yahoo.container.handler.threadpool.ContainerThreadpoolConfig; import com.yahoo.vespa.model.container.ContainerCluster; -import com.yahoo.vespa.model.container.HostProvisionerWithCustomRealResource; import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.component.UserBindingPattern; @@ -98,7 +97,7 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa " <document-api />", nodesXml, "</container>"); - root = new MockRoot("root", new MockApplicationPackage.Builder().build(), new HostProvisionerWithCustomRealResource()); + root = new MockRoot("root", new MockApplicationPackage.Builder().build()); createModel(root, elem); Map<String, Handler<?>> handlers = getHandlers("cluster1"); Handler<?> feedApiHandler = handlers.get("com.yahoo.vespa.http.server.FeedHandler"); @@ -107,8 +106,8 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa ContainerThreadpoolConfig config = root.getConfig( ContainerThreadpoolConfig.class, "cluster1/component/com.yahoo.vespa.http.server.FeedHandler/threadpool@feedapi-handler"); - assertEquals(16, config.maxThreads()); - assertEquals(8, config.minThreads()); + assertEquals(-4, config.maxThreads()); + assertEquals(-4, config.minThreads()); } @Test diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java index 1dba56805a5..d32a866cfbd 100644 --- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java +++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java @@ -28,7 +28,7 @@ public class FileDistributionAndUrlDownload { public FileDistributionAndUrlDownload(Supervisor supervisor, ConfigSourceSet source) { fileDistributionRpcServer = new FileDistributionRpcServer(supervisor, - new FileDownloader(new JRTConnectionPool(source, "filedistribution-jrt-pool-"))); + new FileDownloader(new JRTConnectionPool(source, "filedistribution-jrt-pool"))); urlDownloadRpcServer = new UrlDownloadRpcServer(supervisor); cleanupExecutor.scheduleAtFixedRate(new CachedFilesMaintainer(), delay.toSeconds(), delay.toSeconds(), TimeUnit.SECONDS); } diff --git a/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java b/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java index e83fc7aefc5..f50ed694115 100644 --- a/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java +++ b/config/src/main/java/com/yahoo/config/subscription/impl/JRTConfigSubscription.java @@ -105,16 +105,11 @@ public class JRTConfigSubscription<T extends ConfigInstance> extends ConfigSubsc } protected void setNewConfig(JRTClientConfigRequest jrtReq) { - Exception badConfigE = null; - T configInstance = null; try { - configInstance = toConfigInstance(jrtReq); + T configInstance = toConfigInstance(jrtReq); + setConfig(jrtReq.getNewGeneration(), jrtReq.responseIsApplyOnRestart(), configInstance, jrtReq.getNewChecksums()); } catch (IllegalArgumentException e) { - badConfigE = e; - } - setConfig(jrtReq.getNewGeneration(), jrtReq.responseIsApplyOnRestart(), configInstance, jrtReq.getNewChecksums()); - if (badConfigE != null) { - throw new IllegalArgumentException("Bad config from jrt", badConfigE); + throw new IllegalArgumentException("Bad config in response", e); } } diff --git a/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java b/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java index b5147075972..78580328bf9 100644 --- a/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java +++ b/config/src/main/java/com/yahoo/vespa/config/JRTConnectionPool.java @@ -38,12 +38,12 @@ public class JRTConnectionPool implements ConnectionPool { private volatile JRTConnection currentConnection; public JRTConnectionPool(ConfigSourceSet sourceSet) { - this(sourceSet, "config-jrt-pool-" + sourceSet.hashCode()); + this(sourceSet, "config-jrt-pool" + sourceSet.hashCode()); } public JRTConnectionPool(ConfigSourceSet sourceSet, String poolName) { this.poolName = poolName; - supervisor = new Supervisor(new Transport(poolName)).setDropEmptyBuffers(true); + supervisor = new Supervisor(new Transport(poolName + "-")).setDropEmptyBuffers(true); addSources(sourceSet); } @@ -91,7 +91,7 @@ public class JRTConnectionPool implements ConnectionPool { List<JRTConnection> sourceCandidates = getSources(); sourceCandidates.remove(currentConnection); JRTConnection newConnection = pickNewConnectionRandomly(sourceCandidates); - log.log(Level.INFO, () -> "Switching from " + currentConnection + " to " + newConnection); + log.log(Level.INFO, () -> poolName + ": Switching from " + currentConnection + " to " + newConnection); return currentConnection = newConnection; } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java index 8a952f9523d..b583e0ea263 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java @@ -190,6 +190,7 @@ public class ModelContextImpl implements ModelContext { private final boolean newLocationBrokerLogic; private final boolean containerDumpHeapOnShutdownTimeout; private final double containerShutdownTimeout; + private final int maxConnectionLifeInHosted; public FeatureFlags(FlagSource source, ApplicationId appId) { this.defaultTermwiseLimit = flagValue(source, appId, Flags.DEFAULT_TERM_WISE_LIMIT); @@ -218,6 +219,7 @@ public class ModelContextImpl implements ModelContext { this.newLocationBrokerLogic = flagValue(source, appId, Flags.NEW_LOCATION_BROKER_LOGIC); this.containerDumpHeapOnShutdownTimeout = flagValue(source, appId, Flags.CONTAINER_DUMP_HEAP_ON_SHUTDOWN_TIMEOUT); this.containerShutdownTimeout = flagValue(source, appId,Flags.CONTAINER_SHUTDOWN_TIMEOUT); + this.maxConnectionLifeInHosted = flagValue(source, appId, Flags.MAX_CONNECTION_LIFE_IN_HOSTED); } @Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; } @@ -248,6 +250,7 @@ public class ModelContextImpl implements ModelContext { @Override public boolean newLocationBrokerLogic() { return newLocationBrokerLogic; } @Override public double containerShutdownTimeout() { return containerShutdownTimeout; } @Override public boolean containerDumpHeapOnShutdownTimeout() { return containerDumpHeapOnShutdownTimeout; } + @Override public int maxConnectionLifeInHosted() { return maxConnectionLifeInHosted; } private static <V> V flagValue(FlagSource source, ApplicationId appId, UnboundFlag<? extends V, ?, ?> flag) { return flag.bindTo(source) diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionUtil.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionUtil.java index cfe7349a1c6..1985fd534fb 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionUtil.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDistributionUtil.java @@ -50,7 +50,7 @@ public class FileDistributionUtil { .collect(Collectors.toList()); return configServers.size() > 0 - ? new JRTConnectionPool(new ConfigSourceSet(configServers), "filedistribution-jrt-pool-") + ? new JRTConnectionPool(new ConfigSourceSet(configServers), "filedistribution-jrt-pool") : emptyConnectionPool(); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java index 43a684c1fba..083cb535bfa 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java @@ -1,4 +1,4 @@ -// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.session; import com.google.common.collect.HashMultiset; @@ -236,14 +236,16 @@ public class SessionRepository { logger.log(Level.FINE, "Created application " + params.getApplicationId()); long sessionId = session.getSessionId(); SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(sessionId); - CompletionWaiter waiter = sessionZooKeeperClient.createPrepareWaiter(); + Optional<CompletionWaiter> waiter = params.isDryRun() + ? Optional.empty() + : Optional.of(sessionZooKeeperClient.createPrepareWaiter()); Optional<ApplicationSet> activeApplicationSet = getActiveApplicationSet(params.getApplicationId()); ConfigChangeActions actions = sessionPreparer.prepare(applicationRepo.getHostValidator(), logger, params, activeApplicationSet, now, getSessionAppDir(sessionId), session.getApplicationPackage(), sessionZooKeeperClient) .getConfigChangeActions(); setPrepared(session); - waiter.awaitCompletion(params.getTimeoutBudget().timeLeft()); + waiter.ifPresent(w -> w.awaitCompletion(params.getTimeoutBudget().timeLeft())); return actions; } diff --git a/container-core/src/main/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadpool.java b/container-core/src/main/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadpool.java index 6bed4a6f442..7f4d1462102 100644 --- a/container-core/src/main/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadpool.java +++ b/container-core/src/main/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadpool.java @@ -12,9 +12,10 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; /** - * Default implementation of {@link DefaultContainerThreadpool}. + * Default implementation of {@link ContainerThreadPool}. * * @author Steinar Knutsen * @author baldersheim @@ -23,6 +24,10 @@ import java.util.concurrent.TimeUnit; */ public class DefaultContainerThreadpool extends AbstractComponent implements AutoCloseable, ContainerThreadPool { + private static final Logger log = Logger.getLogger(DefaultContainerThreadpool.class.getName()); + private static final int MIN_QUEUE_SIZE = 650; + private static final int MIN_THREADS_WHEN_SCALE_FACTOR = 8; + private final ExecutorServiceWrapper threadpool; @Inject @@ -31,14 +36,23 @@ public class DefaultContainerThreadpool extends AbstractComponent implements Aut } public DefaultContainerThreadpool(ContainerThreadpoolConfig config, Metric metric, ProcessTerminator processTerminator) { - ThreadPoolMetric threadPoolMetric = new ThreadPoolMetric(metric, config.name()); - int maxNumThreads = computeMaximumThreadPoolSize(config.maxThreads()); - int coreNumThreads = computeCoreThreadPoolSize(config.minThreads(), maxNumThreads); + this(config, metric, processTerminator, Runtime.getRuntime().availableProcessors()); + } + + DefaultContainerThreadpool(ContainerThreadpoolConfig config, Metric metric, ProcessTerminator processTerminator, + int cpus) { + String name = config.name(); + int maxThreads = maxThreads(config, cpus); + int minThreads = minThreads(config, maxThreads, cpus); + int queueSize = queueSize(config, maxThreads); + log.info(String.format("Threadpool '%s': min=%d, max=%d, queue=%d", name, minThreads, maxThreads, queueSize)); + + ThreadPoolMetric threadPoolMetric = new ThreadPoolMetric(metric, name); WorkerCompletionTimingThreadPoolExecutor executor = - new WorkerCompletionTimingThreadPoolExecutor(coreNumThreads, maxNumThreads, + new WorkerCompletionTimingThreadPoolExecutor(minThreads, maxThreads, (int)config.keepAliveTime() * 1000, TimeUnit.MILLISECONDS, - createQ(config.queueSize(), maxNumThreads), - ThreadFactoryFactory.getThreadFactory(config.name()), + createQueue(queueSize), + ThreadFactoryFactory.getThreadFactory(name), threadPoolMetric); // Prestart needed, if not all threads will be created by the fist N tasks and hence they might also // get the dreaded thread locals initialized even if they will never run. @@ -46,7 +60,7 @@ public class DefaultContainerThreadpool extends AbstractComponent implements Aut executor.prestartAllCoreThreads(); threadpool = new ExecutorServiceWrapper( executor, threadPoolMetric, processTerminator, config.maxThreadExecutionTimeSeconds() * 1000L, - config.name(), config.queueSize()); + name, queueSize); } @Override public Executor executor() { return threadpool; } @@ -74,24 +88,26 @@ public class DefaultContainerThreadpool extends AbstractComponent implements Aut } } - private static BlockingQueue<Runnable> createQ(int queueSize, int maxThreads) { - return (queueSize == 0) - ? new SynchronousQueue<>(false) - : (queueSize < 0) - ? new ArrayBlockingQueue<>(maxThreads*4) - : new ArrayBlockingQueue<>(queueSize); + private static BlockingQueue<Runnable> createQueue(int size) { + return size == 0 ? new SynchronousQueue<>(false) : new ArrayBlockingQueue<>(size); + } + + private static int maxThreads(ContainerThreadpoolConfig config, int cpus) { + if (config.maxThreads() > 0) return config.maxThreads(); + else if (config.maxThreads() == 0) return 4 * cpus; + else return Math.max(MIN_THREADS_WHEN_SCALE_FACTOR, Math.abs(config.maxThreads()) * cpus); } - private static int computeMaximumThreadPoolSize(int maxNumThreads) { - return (maxNumThreads <= 0) - ? Runtime.getRuntime().availableProcessors() * 4 - : maxNumThreads; + private static int minThreads(ContainerThreadpoolConfig config, int max, int cpus) { + int threads; + if (config.minThreads() > 0) threads = config.minThreads(); + else if (config.minThreads() == 0) threads = 4 * cpus; + else threads = Math.max(MIN_THREADS_WHEN_SCALE_FACTOR, Math.abs(config.minThreads()) * cpus); + return Math.min(threads, max); } - private static int computeCoreThreadPoolSize(int corePoolSize, int maxNumThreads) { - return Math.min( - corePoolSize <= 0 ? Runtime.getRuntime().availableProcessors() * 2 : corePoolSize, - maxNumThreads); + private int queueSize(ContainerThreadpoolConfig config, int maxThreads) { + return config.queueSize() >= 0 ? config.queueSize() : Math.max(MIN_QUEUE_SIZE, Math.abs(config.queueSize()) * maxThreads); } } diff --git a/container-core/src/main/java/com/yahoo/container/logging/AccessLogHandler.java b/container-core/src/main/java/com/yahoo/container/logging/AccessLogHandler.java index f14479899f5..55c3ad2ca8e 100644 --- a/container-core/src/main/java/com/yahoo/container/logging/AccessLogHandler.java +++ b/container-core/src/main/java/com/yahoo/container/logging/AccessLogHandler.java @@ -13,13 +13,19 @@ class AccessLogHandler { AccessLogHandler(AccessLogConfig.FileHandler config, LogWriter<RequestLogEntry> logWriter) { logFileHandler = new LogFileHandler<>( toCompression(config), config.bufferSize(), config.pattern(), config.rotation(), - config.symlink(), config.queueSize(), "request-logger", logWriter); + config.symlink(), queueSize(config), "request-logger", logWriter); + } + + private static int queueSize(AccessLogConfig.FileHandler config) { + if (config.queueSize() != -1) return config.queueSize(); + return Math.max(4096, Runtime.getRuntime().availableProcessors() * 256); } public void log(RequestLogEntry entry) { logFileHandler.publish(entry); } + private LogFileHandler.Compression toCompression(AccessLogConfig.FileHandler config) { if (!config.compressOnRotation()) return LogFileHandler.Compression.NONE; switch (config.compressionFormat()) { diff --git a/container-core/src/main/java/com/yahoo/container/logging/FileConnectionLog.java b/container-core/src/main/java/com/yahoo/container/logging/FileConnectionLog.java index 749426d3da9..273d562d048 100644 --- a/container-core/src/main/java/com/yahoo/container/logging/FileConnectionLog.java +++ b/container-core/src/main/java/com/yahoo/container/logging/FileConnectionLog.java @@ -14,7 +14,13 @@ public class FileConnectionLog extends AbstractComponent implements ConnectionLo @Inject public FileConnectionLog(ConnectionLogConfig config) { - logHandler = new ConnectionLogHandler(config.logDirectoryName(), config.bufferSize(), config.cluster(), config.queueSize(), new JsonConnectionLogWriter()); + logHandler = new ConnectionLogHandler(config.logDirectoryName(), config.bufferSize(), config.cluster(), + queueSize(config), new JsonConnectionLogWriter()); + } + + private static int queueSize(ConnectionLogConfig config) { + if (config.queueSize() != -1) return config.queueSize(); + return Math.max(4096, Runtime.getRuntime().availableProcessors() * 512); } @Override diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java index 70f173b74e5..b44a32c4db1 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java @@ -132,9 +132,13 @@ public class JettyHttpServer extends AbstractServerProvider { } private static void configureJettyThreadpool(Server server, ServerConfig config) { + int cpus = Runtime.getRuntime().availableProcessors(); QueuedThreadPool pool = (QueuedThreadPool) server.getThreadPool(); - pool.setMaxThreads(config.maxWorkerThreads()); - pool.setMinThreads(config.minWorkerThreads()); + int maxThreads = config.maxWorkerThreads() > 0 ? config.maxWorkerThreads() : 16 + cpus; + pool.setMaxThreads(maxThreads); + int minThreads = config.minWorkerThreads() >= 0 ? config.minWorkerThreads() : 16 + cpus; + pool.setMinThreads(minThreads); + log.info(String.format("Threadpool size: min=%d, max=%d", minThreads, maxThreads)); } private static JMXServiceURL createJmxLoopbackOnlyServiceUrl(int port) { @@ -224,10 +228,9 @@ public class JettyHttpServer extends AbstractServerProvider { var sslConnectionFactory = serverConnector.getConnectionFactory(SslConnectionFactory.class); if (sslConnectionFactory != null) { var sslContextFactory = sslConnectionFactory.getSslContextFactory(); - log.info(String.format("Enabled SSL cipher suites for port '%d': %s", - localPort, Arrays.toString(sslContextFactory.getSelectedCipherSuites()))); - log.info(String.format("Enabled SSL protocols for port '%d': %s", - localPort, Arrays.toString(sslContextFactory.getSelectedProtocols()))); + String protocols = Arrays.toString(sslContextFactory.getSelectedProtocols()); + String cipherSuites = Arrays.toString(sslContextFactory.getSelectedCipherSuites()); + log.info(String.format("TLS for port '%d': %s with %s", localPort, protocols, cipherSuites)); } } } diff --git a/container-core/src/main/resources/configdefinitions/container.handler.threadpool.container-threadpool.def b/container-core/src/main/resources/configdefinitions/container.handler.threadpool.container-threadpool.def index 9248bf2e2bf..4ba14c2da89 100644 --- a/container-core/src/main/resources/configdefinitions/container.handler.threadpool.container-threadpool.def +++ b/container-core/src/main/resources/configdefinitions/container.handler.threadpool.container-threadpool.def @@ -4,16 +4,19 @@ namespace=container.handler.threadpool ## Maximum number of thread in the thread pool ## 0 is translated to vcpu*4 +## Negative value is interpreted as scale factor ( vcpu*abs(maxThreads) ) maxThreads int default=0 ## Minimum number of thread in the thread pool ## 0 is translated to vcpu*2 +## Negative value is interpreted as scale factor ( vcpu*abs(minThreads) ) minThreads int default=0 ## The number of seconds that excess idle threads will wait for new tasks before terminating keepAliveTime double default=5.0 ## Max queue size +## Negative value is interpreted as scale factor ( effectiveMaxThreads*abs(queueSize) ) queueSize int default=0 ## The max time the container tolerates having no threads available before it shuts down to diff --git a/container-core/src/main/resources/configdefinitions/container.handler.threadpool.def b/container-core/src/main/resources/configdefinitions/container.handler.threadpool.def index d966738ea9f..e73ee2254fc 100644 --- a/container-core/src/main/resources/configdefinitions/container.handler.threadpool.def +++ b/container-core/src/main/resources/configdefinitions/container.handler.threadpool.def @@ -3,10 +3,13 @@ namespace=container.handler ## Maximum number of thread in the thread pool -## Setting it to 0 or negative number will cause it to be set to #cores * 4 +## 0 is translated to vcpu*4 +## Negative value is interpreted as scale factor ( vcpu*abs(maxThreads) ) maxthreads int default=500 -# The number of threads to keep in the pool, even if they are idle +## The number of threads to keep in the pool, even if they are idle +## 0 is translated to vcpu*4 +## Negative value is interpreted as scale factor ( vcpu*abs(corePoolSize) ) corePoolSize int default=500 # The number of seconds that excess idle threads will wait for new tasks before terminating diff --git a/container-core/src/test/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadPoolTest.java b/container-core/src/test/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadPoolTest.java index 8b1ed12c796..1d9c4b367bd 100644 --- a/container-core/src/test/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadPoolTest.java +++ b/container-core/src/test/java/com/yahoo/container/handler/threadpool/DefaultContainerThreadPoolTest.java @@ -21,6 +21,9 @@ import static org.junit.Assert.fail; * @author bjorncs */ public class DefaultContainerThreadPoolTest { + + private static final int CPUS = 16; + @Test public final void testThreadPool() throws InterruptedException { ContainerThreadpoolConfig config = new ContainerThreadpoolConfig(new ContainerThreadpoolConfig.Builder().maxThreads(1)); @@ -55,8 +58,12 @@ public class DefaultContainerThreadPoolTest { } private ThreadPoolExecutor createPool(int maxThreads, int queueSize) { - ContainerThreadpoolConfig config = new ContainerThreadpoolConfig(new ContainerThreadpoolConfig.Builder().maxThreads(maxThreads).queueSize(queueSize)); - ContainerThreadPool threadPool = new DefaultContainerThreadpool(config, Mockito.mock(Metric.class)); + ContainerThreadpoolConfig config = new ContainerThreadpoolConfig(new ContainerThreadpoolConfig.Builder() + .maxThreads(maxThreads) + .minThreads(maxThreads) + .queueSize(queueSize)); + ContainerThreadPool threadPool = new DefaultContainerThreadpool( + config, Mockito.mock(Metric.class), new MockProcessTerminator(), CPUS); ExecutorServiceWrapper wrapper = (ExecutorServiceWrapper) threadPool.executor(); WorkerCompletionTimingThreadPoolExecutor executor = (WorkerCompletionTimingThreadPoolExecutor)wrapper.delegate(); return executor; @@ -64,27 +71,27 @@ public class DefaultContainerThreadPoolTest { @Test public void testThatThreadPoolSizeFollowsConfig() { - ThreadPoolExecutor executor = createPool(3, 9); + ThreadPoolExecutor executor = createPool(3, 1200); assertEquals(3, executor.getMaximumPoolSize()); - assertEquals(9, executor.getQueue().remainingCapacity()); + assertEquals(1200, executor.getQueue().remainingCapacity()); } @Test public void testThatThreadPoolSizeAutoDetected() { ThreadPoolExecutor executor = createPool(0, 0); - assertEquals(Runtime.getRuntime().availableProcessors()*4, executor.getMaximumPoolSize()); + assertEquals(CPUS*4, executor.getMaximumPoolSize()); assertEquals(0, executor.getQueue().remainingCapacity()); } @Test public void testThatQueueSizeAutoDetected() { - ThreadPoolExecutor executor = createPool(3, -1); - assertEquals(3, executor.getMaximumPoolSize()); - assertEquals(executor.getMaximumPoolSize()*4, executor.getQueue().remainingCapacity()); + ThreadPoolExecutor executor = createPool(24, -50); + assertEquals(24, executor.getMaximumPoolSize()); + assertEquals(24*50, executor.getQueue().remainingCapacity()); } @Test public void testThatThreadPoolSizeAndQueueSizeAutoDetected() { - ThreadPoolExecutor executor = createPool(0, -1); - assertEquals(Runtime.getRuntime().availableProcessors()*4, executor.getMaximumPoolSize()); - assertEquals(executor.getMaximumPoolSize()*4, executor.getQueue().remainingCapacity()); + ThreadPoolExecutor executor = createPool(0, -100); + assertEquals(CPUS*4, executor.getMaximumPoolSize()); + assertEquals(CPUS*4*100, executor.getQueue().remainingCapacity()); } private class FlipIt implements Runnable { diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java index a84d2521b8b..de595da07b6 100644 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java @@ -68,6 +68,7 @@ public final class ConfiguredApplication implements Application { private static final Logger log = Logger.getLogger(ConfiguredApplication.class.getName()); private static final Set<ClientProvider> startedClients = Collections.newSetFromMap(new WeakHashMap<>()); + static final String SANITIZE_FILENAME = "[/,;]"; private static final Set<ServerProvider> startedServers = Collections.newSetFromMap(new IdentityHashMap<>()); private final SubscriberFactory subscriberFactory; @@ -407,6 +408,12 @@ public final class ConfiguredApplication implements Application { } } + static String santizeFileName(String s) { + return s.trim() + .replace('\\', '.') + .replaceAll(SANITIZE_FILENAME, "."); + } + // Workaround for ApplicationLoader.stop not being able to shutdown private void startShutdownDeadlineExecutor() { shutdownDeadlineExecutor = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory("Shutdown deadline timer")); @@ -414,7 +421,7 @@ public final class ConfiguredApplication implements Application { long delayMillis = (long)(shudownTimeoutS.get() * 1000.0); shutdownDeadlineExecutor.schedule(() -> { if (dumpHeapOnShutdownTimeout.get()) { - String heapDumpName = Defaults.getDefaults().underVespaHome("var/crash/java_pid.") + ProcessHandle.current().pid() + ".hprof"; + String heapDumpName = Defaults.getDefaults().underVespaHome("var/crash/java_pid.") + santizeFileName(configId) + "." + ProcessHandle.current().pid() + ".hprof"; com.yahoo.protect.Process.dumpHeap(heapDumpName, true); } com.yahoo.protect.Process.logAndDie( diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/ConfiguredApplicationTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/ConfiguredApplicationTest.java new file mode 100644 index 00000000000..c8cf5c0ce63 --- /dev/null +++ b/container-disc/src/test/java/com/yahoo/container/jdisc/ConfiguredApplicationTest.java @@ -0,0 +1,14 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.jdisc; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ConfiguredApplicationTest { + @Test + public void testConfigId2FileName() { + assertEquals("admin.metrics.2088223-v6-1.ostk.bm2.prod.ne1.yahoo.com", ConfiguredApplication.santizeFileName("admin/metrics/2088223-v6-1.ostk.bm2.prod.ne1.yahoo.com")); + assertEquals("admin.standalone.cluster-controllers.1", ConfiguredApplication.santizeFileName("admin/standalone/cluster-controllers/1 ")); + } +} diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java index af731e3ade0..fc5bedf07c7 100644 --- a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java +++ b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java @@ -2,7 +2,6 @@ package com.yahoo.search.dispatch; import com.google.inject.Inject; -import com.yahoo.cloud.config.ClusterInfoConfig; import com.yahoo.component.AbstractComponent; import com.yahoo.component.ComponentId; import com.yahoo.compress.Compressor; @@ -26,11 +25,9 @@ import com.yahoo.search.query.profile.types.QueryProfileType; import com.yahoo.search.result.ErrorMessage; import com.yahoo.vespa.config.search.DispatchConfig; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.OptionalInt; import java.util.Set; import java.util.stream.Collectors; @@ -94,10 +91,9 @@ public class Dispatcher extends AbstractComponent { public Dispatcher(RpcResourcePool resourcePool, ComponentId clusterId, DispatchConfig dispatchConfig, - ClusterInfoConfig clusterInfoConfig, VipStatus vipStatus, Metric metric) { - this(resourcePool, new SearchCluster(clusterId.stringValue(), dispatchConfig,clusterInfoConfig.nodeCount(), + this(resourcePool, new SearchCluster(clusterId.stringValue(), dispatchConfig, vipStatus, new RpcPingFactory(resourcePool)), dispatchConfig, metric); diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java index 54d5dfc91af..b5fbede4701 100644 --- a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java +++ b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java @@ -53,7 +53,7 @@ public class SearchCluster implements NodeManager<Node> { */ private final Optional<Node> localCorpusDispatchTarget; - public SearchCluster(String clusterId, DispatchConfig dispatchConfig, int containerClusterSize, + public SearchCluster(String clusterId, DispatchConfig dispatchConfig, VipStatus vipStatus, PingFactory pingFactory) { this.clusterId = clusterId; this.dispatchConfig = dispatchConfig; @@ -81,17 +81,7 @@ public class SearchCluster implements NodeManager<Node> { this.nodesByHost = nodesByHostBuilder.build(); hitEstimator = new TopKEstimator(30.0, dispatchConfig.topKProbability(), SKEW_FACTOR); - this.localCorpusDispatchTarget = findLocalCorpusDispatchTarget(HostName.getLocalhost(), - size, - containerClusterSize, - nodesByHost, - groups); - } - - /* Testing only */ - public SearchCluster(String clusterId, DispatchConfig dispatchConfig, - VipStatus vipStatus, PingFactory pingFactory) { - this(clusterId, dispatchConfig, 1, vipStatus, pingFactory); + this.localCorpusDispatchTarget = findLocalCorpusDispatchTarget(HostName.getLocalhost(), nodesByHost, groups); } public void addMonitoring(ClusterMonitor<Node> clusterMonitor) { @@ -102,8 +92,6 @@ public class SearchCluster implements NodeManager<Node> { } private static Optional<Node> findLocalCorpusDispatchTarget(String selfHostname, - int searchClusterSize, - int containerClusterSize, ImmutableMultimap<String, Node> nodesByHost, ImmutableMap<Integer, Group> groups) { // A search node in the search cluster in question is configured on the same host as the currently running container. @@ -120,12 +108,6 @@ public class SearchCluster implements NodeManager<Node> { // Only use direct dispatch if the local search node has the entire corpus if (localSearchGroup.nodes().size() != 1) return Optional.empty(); - // Only use direct dispatch if this container cluster has at least as many nodes as the search cluster - // to avoid load skew/preserve fanout in the case where a subset of the search nodes are also containers. - // This disregards the case where the search and container clusters are partially overlapping. - // Such configurations produce skewed load in any case. - if (containerClusterSize < searchClusterSize) return Optional.empty(); - return Optional.of(localSearchNode); } diff --git a/container-search/src/test/java/com/yahoo/prelude/cluster/ClusterSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/cluster/ClusterSearcherTestCase.java index ca98b6a1a77..38c231cb1bb 100644 --- a/container-search/src/test/java/com/yahoo/prelude/cluster/ClusterSearcherTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/cluster/ClusterSearcherTestCase.java @@ -2,7 +2,6 @@ package com.yahoo.prelude.cluster; import com.google.common.collect.ImmutableList; -import com.yahoo.cloud.config.ClusterInfoConfig; import com.yahoo.component.ComponentId; import com.yahoo.component.provider.ComponentRegistry; import com.yahoo.container.QrConfig; @@ -518,7 +517,6 @@ public class ClusterSearcherTestCase { Dispatcher dispatcher = new Dispatcher(new RpcResourcePool(dispatchConfig), ComponentId.createAnonymousComponentId("test-id"), dispatchConfig, - createClusterInfoConfig(), vipStatus, new MockMetric()); ComponentRegistry<Dispatcher> dispatchers = new ComponentRegistry<>(); @@ -534,13 +532,6 @@ public class ClusterSearcherTestCase { null); } - private static ClusterInfoConfig createClusterInfoConfig() { - ClusterInfoConfig.Builder clusterInfoConfigBuilder = new ClusterInfoConfig.Builder(); - clusterInfoConfigBuilder.clusterId("containerCluster1"); - clusterInfoConfigBuilder.nodeCount(1); - return new ClusterInfoConfig(clusterInfoConfigBuilder); - } - private static class QueryTimeoutFixture { ClusterSearcher searcher; Execution exec; diff --git a/container-search/src/test/java/com/yahoo/search/dispatch/searchcluster/SearchClusterTest.java b/container-search/src/test/java/com/yahoo/search/dispatch/searchcluster/SearchClusterTest.java index f46717ce180..22cc783967d 100644 --- a/container-search/src/test/java/com/yahoo/search/dispatch/searchcluster/SearchClusterTest.java +++ b/container-search/src/test/java/com/yahoo/search/dispatch/searchcluster/SearchClusterTest.java @@ -58,7 +58,7 @@ public class SearchClusterTest { numDocsPerNode.add(new AtomicInteger(1)); pingCounts.add(new AtomicInteger(0)); } - searchCluster = new SearchCluster(clusterId, MockSearchCluster.createDispatchConfig(nodes), nodes.size() / nodesPerGroup, + searchCluster = new SearchCluster(clusterId, MockSearchCluster.createDispatchConfig(nodes), vipStatus, new Factory(nodesPerGroup, numDocsPerNode, pingCounts)); clusterMonitor = new ClusterMonitor(searchCluster, false); searchCluster.addMonitoring(clusterMonitor); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java index 850e14b25e8..367b258e7b6 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/systemflags/v1/SystemFlagsDataArchive.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -125,9 +126,9 @@ public class SystemFlagsDataArchive { uncheck(zipOut::flush); } - public Set<FlagData> flagData(FlagsTarget target) { + public List<FlagData> flagData(FlagsTarget target) { List<String> filenames = target.flagDataFilesPrioritized(); - Set<FlagData> targetData = new HashSet<>(); + List<FlagData> targetData = new ArrayList<>(); files.forEach((flagId, fileMap) -> { for (String filename : filenames) { FlagData data = fileMap.get(filename); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java index 4076013b2c5..d3615c942b9 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java @@ -475,18 +475,18 @@ public class JobController { /** Orders a run of the given type, or throws an IllegalStateException if that job type is already running. */ public void start(ApplicationId id, JobType type, Versions versions, boolean isRedeployment) { - start(id, type, versions, isRedeployment, JobProfile.of(type), false); + start(id, type, versions, isRedeployment, JobProfile.of(type)); } /** Orders a run of the given type, or throws an IllegalStateException if that job type is already running. */ - public void start(ApplicationId id, JobType type, Versions versions, boolean isRedeployment, JobProfile profile, boolean dryRun) { + public void start(ApplicationId id, JobType type, Versions versions, boolean isRedeployment, JobProfile profile) { locked(id, type, __ -> { Optional<Run> last = last(id, type); if (last.flatMap(run -> active(run.id())).isPresent()) throw new IllegalStateException("Can not start " + type + " for " + id + "; it is already running!"); RunId newId = new RunId(id, type, last.map(run -> run.id().number()).orElse(0L) + 1); - curator.writeLastRun(Run.initial(newId, versions, isRedeployment, controller.clock().instant(), profile, dryRun)); + curator.writeLastRun(Run.initial(newId, versions, isRedeployment, controller.clock().instant(), profile)); metric.jobStarted(newId.job()); }); } @@ -531,8 +531,7 @@ public class JobController { lastRun.map(run -> run.versions().targetPlatform()), lastRun.map(run -> run.versions().targetApplication())), false, - JobProfile.development, - dryRun); + dryRun ? JobProfile.developmentDryRun : JobProfile.development); }); locked(id, type, __ -> { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java index 1c1d60a2cf0..75dae8d77df 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java @@ -68,7 +68,9 @@ public enum JobProfile { development(EnumSet.of(deployReal, installReal, - copyVespaLogs)); + copyVespaLogs)), + + developmentDryRun(EnumSet.of(deployReal)); private final Set<Step> steps; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java index b3768178b1e..98a8a49ec06 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java @@ -60,11 +60,11 @@ public class Run { this.dryRun = dryRun; } - public static Run initial(RunId id, Versions versions, boolean isRedeployment, Instant now, JobProfile profile, boolean dryRun) { + public static Run initial(RunId id, Versions versions, boolean isRedeployment, Instant now, JobProfile profile) { EnumMap<Step, StepInfo> steps = new EnumMap<>(Step.class); profile.steps().forEach(step -> steps.put(step, StepInfo.initial(step))); return new Run(id, steps, requireNonNull(versions), isRedeployment, requireNonNull(now), Optional.empty(), running, - -1, Instant.EPOCH, Optional.empty(), Optional.empty(), Optional.empty(), dryRun); + -1, Instant.EPOCH, Optional.empty(), Optional.empty(), Optional.empty(), profile == JobProfile.developmentDryRun); } /** Returns a new Run with the status of the given completed step set accordingly. */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResult.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResult.java index d169cd97df7..529e892ced9 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResult.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResult.java @@ -251,6 +251,13 @@ class SystemFlagsDeployResult { return new OperationError(message, Set.of(), OperationType.VALIDATE_ARCHIVE, null, null); } + static OperationError dataForUndefinedFlag(FlagsTarget target, FlagId id) { + return new OperationError("Flag data present for undefined flag. Remove flag data files if flag's definition " + + "is already removed from Flags / PermanentFlags. Consult ModelContext.FeatureFlags " + + "for safe removal of flag used by config-model.", + Set.of(), OperationType.DATA_FOR_UNDEFINED_FLAG, id, null); + } + String message() { return message; } Set<FlagsTarget> targets() { return targets; } OperationType operation() { return operation; } @@ -284,7 +291,8 @@ class SystemFlagsDeployResult { } enum OperationType { - CREATE("create"), DELETE("delete"), UPDATE("update"), LIST("list"), VALIDATE_ARCHIVE("validate-archive"); + CREATE("create"), DELETE("delete"), UPDATE("update"), LIST("list"), VALIDATE_ARCHIVE("validate-archive"), + DATA_FOR_UNDEFINED_FLAG("data-for-undefined-flag"); private final String stringValue; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployer.java index 21a429b59ad..e0b65b0834d 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployer.java @@ -78,7 +78,7 @@ class SystemFlagsDeployer { return SystemFlagsDeployResult.merge(results); } - private SystemFlagsDeployResult deployFlags(FlagsTarget target, Set<FlagData> flagData, boolean dryRun) { + private SystemFlagsDeployResult deployFlags(FlagsTarget target, List<FlagData> flagData, boolean dryRun) { Map<FlagId, FlagData> wantedFlagData = lookupTable(flagData); Map<FlagId, FlagData> currentFlagData; List<FlagId> definedFlags; @@ -98,7 +98,7 @@ class SystemFlagsDeployer { updateExistingFlagData(target, dryRun, wantedFlagData, currentFlagData, results, errors); removeOldFlagData(target, dryRun, wantedFlagData, currentFlagData, results, errors); failOnNewFlagDataForUndefinedFlags(target, wantedFlagData, currentFlagData, definedFlags, errors); - warnOnExistingFlagDataForUndefinedFlags(target, wantedFlagData, currentFlagData, definedFlags, warnings); + failOnFlagDataForUndefinedFlags(target, wantedFlagData, currentFlagData, definedFlags, errors); return new SystemFlagsDeployResult(results, errors, warnings); } @@ -191,14 +191,14 @@ class SystemFlagsDeployer { } } - private static void warnOnExistingFlagDataForUndefinedFlags(FlagsTarget target, - Map<FlagId, FlagData> wantedFlagData, - Map<FlagId, FlagData> currentFlagData, - List<FlagId> definedFlags, - List<Warning> warnings) { + private static void failOnFlagDataForUndefinedFlags(FlagsTarget target, + Map<FlagId, FlagData> wantedFlagData, + Map<FlagId, FlagData> currentFlagData, + List<FlagId> definedFlags, + List<OperationError> errors) { for (FlagId flagId : currentFlagData.keySet()) { if (wantedFlagData.containsKey(flagId) && !definedFlags.contains(flagId)) { - warnings.add(Warning.dataForUndefinedFlag(target, flagId)); + errors.add(OperationError.dataForUndefinedFlag(target, flagId)); } } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java index 8bd22b5217f..eca2b17a5f1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java @@ -153,7 +153,7 @@ public class RunSerializerTest { assertEquals(run.steps(), phoenix.steps()); assertEquals(run.isDryRun(), phoenix.isDryRun()); - Run initial = Run.initial(id, run.versions(), run.isRedeployment(), run.start(), JobProfile.production, true); + Run initial = Run.initial(id, run.versions(), run.isRedeployment(), run.start(), JobProfile.production); assertEquals(initial, serializer.runFromSlime(serializer.toSlime(initial))); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java index 35a13cdeeec..549dd1ed253 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java @@ -166,8 +166,8 @@ public class SystemFlagsDeployerTest { .build(); SystemFlagsDeployer deployer = new SystemFlagsDeployer(flagsClient, SYSTEM, Set.of(prodUsEast3Target)); SystemFlagsDeployResult result = deployer.deployFlags(archive, true); - assertThat(result.warnings()) - .containsOnly(SystemFlagsDeployResult.Warning.dataForUndefinedFlag(prodUsEast3Target, new FlagId("my-flag"))); + assertThat(result.errors()) + .containsOnly(OperationError.dataForUndefinedFlag(prodUsEast3Target, new FlagId("my-flag"))); } private static FlagData flagData(String filename) throws IOException { diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt index 8a7cd4f66bd..59ad1a530e1 100644 --- a/eval/CMakeLists.txt +++ b/eval/CMakeLists.txt @@ -45,6 +45,7 @@ vespa_define_module( src/tests/eval/value_type src/tests/gp/ponder_nov2017 src/tests/instruction/add_trivial_dimension_optimizer + src/tests/instruction/best_similarity_function src/tests/instruction/dense_dot_product_function src/tests/instruction/dense_hamming_distance src/tests/instruction/dense_inplace_join_function diff --git a/eval/src/tests/eval/tensor_function/tensor_function_test.cpp b/eval/src/tests/eval/tensor_function/tensor_function_test.cpp index c457f68a614..3d4e2d41cb5 100644 --- a/eval/src/tests/eval/tensor_function/tensor_function_test.cpp +++ b/eval/src/tests/eval/tensor_function/tensor_function_test.cpp @@ -510,4 +510,18 @@ TEST("require that tensor function can be dumped for debugging") { fprintf(stderr, "function dump -->[[%s]]<-- function dump\n", root.as_string().c_str()); } +TEST("require that full tensor reduce expands dimension list") { + Stash stash; + const auto &num = inject(ValueType::from_spec("double"), 0, stash); + const auto &mat = inject(ValueType::from_spec("tensor(x[5],y[5])"), 1, stash); + const auto *reduce_num = as<Reduce>(reduce(num, Aggr::SUM, {}, stash)); + const auto *reduce_mat = as<Reduce>(reduce(mat, Aggr::SUM, {}, stash)); + ASSERT_TRUE(reduce_num); + ASSERT_TRUE(reduce_mat); + EXPECT_EQUAL(reduce_num->dimensions().size(), 0u); + ASSERT_EQUAL(reduce_mat->dimensions().size(), 2u); + EXPECT_EQUAL(reduce_mat->dimensions()[0], "x"); + EXPECT_EQUAL(reduce_mat->dimensions()[1], "y"); +} + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/eval/src/tests/instruction/best_similarity_function/CMakeLists.txt b/eval/src/tests/instruction/best_similarity_function/CMakeLists.txt new file mode 100644 index 00000000000..dd83f4c72a8 --- /dev/null +++ b/eval/src/tests/instruction/best_similarity_function/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(eval_best_similarity_function_test_app TEST + SOURCES + best_similarity_function_test.cpp + DEPENDS + vespaeval + GTest::GTest +) +vespa_add_test(NAME eval_best_similarity_function_test_app COMMAND eval_best_similarity_function_test_app) diff --git a/eval/src/tests/instruction/best_similarity_function/best_similarity_function_test.cpp b/eval/src/tests/instruction/best_similarity_function/best_similarity_function_test.cpp new file mode 100644 index 00000000000..b461dc756d7 --- /dev/null +++ b/eval/src/tests/instruction/best_similarity_function/best_similarity_function_test.cpp @@ -0,0 +1,155 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/eval/eval/fast_value.h> +#include <vespa/eval/eval/tensor_function.h> +#include <vespa/eval/eval/test/eval_fixture.h> +#include <vespa/eval/eval/test/gen_spec.h> +#include <vespa/eval/instruction/best_similarity_function.h> +#include <vespa/vespalib/gtest/gtest.h> + +using namespace vespalib; +using namespace vespalib::eval; +using namespace vespalib::eval::test; + +const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get(); + +//----------------------------------------------------------------------------- + +void verify_impl(const TensorSpec &a, const TensorSpec &b, const vespalib::string &expr, bool optimized) { + EvalFixture::ParamRepo param_repo; + param_repo.add("a", a).add("b", b); + EvalFixture fast_fixture(prod_factory, expr, param_repo, true); + EXPECT_EQ(fast_fixture.result(), EvalFixture::ref(expr, param_repo)); + EXPECT_EQ(fast_fixture.find_all<BestSimilarityFunction>().size(), optimized ? 1 : 0); +} + +void verify(const TensorSpec &a, const TensorSpec &b, const vespalib::string &expr, bool optimized = true) { + verify_impl(a, b, expr, optimized); + verify_impl(b, a, expr, optimized); +} + +//----------------------------------------------------------------------------- + +GenSpec gen_double(const vespalib::string &desc, int bias) { + return GenSpec::from_desc(desc).cells(CellType::DOUBLE).seq(N(bias)); +} + +GenSpec gen_float(const vespalib::string &desc, int bias) { + return GenSpec::from_desc(desc).cells(CellType::FLOAT).seq(N(bias)); +} + +GenSpec gen_int8(const vespalib::string &desc, int bias) { + return GenSpec::from_desc(desc).cells(CellType::INT8).seq(N(bias)); +} + +vespalib::string max_sim = "reduce(reduce(a*b,sum,d),max,b)"; +vespalib::string min_hamming = "reduce(reduce(hamming(a,b),sum,d),min,b)"; + +//----------------------------------------------------------------------------- + +TEST(BestSimilarityFunctionTest, result_is_mutable) { + tensor_function::Inject child(ValueType::double_type(), 0); + BestSimilarityFunction node(ValueType::double_type(), child, child, nullptr, 1); + EXPECT_TRUE(node.result_is_mutable()); +} + +TEST(BestSimilarityFunctionTest, max_sim_can_be_optimized) { + verify(gen_float("A3_2B3d8", 3), gen_float("b5d8", 7), max_sim); + verify(gen_float("A3_2B3d8", 3), gen_float("b5_2d8", 7), max_sim); +} + +TEST(BestSimilarityFunctionTest, min_hamming_can_be_optimized) { + verify(gen_int8("A3_2B3d8", 3), gen_int8("b5d8", 7), min_hamming); + verify(gen_int8("A3_2B3d8", 3), gen_int8("b5_2d8", 7), min_hamming); +} + +TEST(BestSimilarityFunctionTest, result_can_be_sparse) { + verify(gen_float("A3_2d8", 3), gen_float("b5d8", 7), max_sim); + verify(gen_int8("A3_2d8", 3), gen_int8("b5_2d8", 7), min_hamming); +} + +TEST(BestSimilarityFunctionTest, result_can_be_dense) { + verify(gen_float("B3d8", 3), gen_float("b5d8", 7), max_sim); + verify(gen_int8("B3d8", 3), gen_int8("b5_2d8", 7), min_hamming); +} + +TEST(BestSimilarityFunctionTest, result_can_be_double) { + verify(gen_float("d8", 3), gen_float("b5d8", 7), max_sim); + verify(gen_int8("d8", 3), gen_int8("b5_2d8", 7), min_hamming); +} + +TEST(BestSimilarityFunctionTest, primary_dimensions_can_be_trivial) { + verify(gen_float("d1", 3), gen_float("b1d1", 7), max_sim); + verify(gen_int8("d1", 3), gen_int8("b1d1", 7), min_hamming); +} + +TEST(BestSimilarityFunctionTest, extra_trivial_dimensions_are_allowed) { + verify(gen_float("A1a1d8x1z1", 3), gen_float("a1b5c1d8x1y1", 7), max_sim); +} + +TEST(BestSimilarityFunctionTest, allow_full_reduce_for_outer_dimension) { + vespalib::string my_max_sim = "reduce(reduce(a*b,sum,d),max)"; + vespalib::string my_min_hamming = "reduce(reduce(hamming(a,b),sum,d),min)"; + verify(gen_float("d8", 3), gen_float("b5d8", 7), my_max_sim); + verify(gen_int8("d8", 3), gen_int8("b5_2d8", 7), my_min_hamming); +} + +vespalib::string inv_max_sim = "reduce(reduce(a*b,sum,b),max,d)"; + +TEST(BestSimilarityFunctionTest, dimensions_can_be_inverted_if_best_dimension_is_sparse) { + verify(gen_float("b8", 3), gen_float("b8d5_2", 7), inv_max_sim); +} + +//----------------------------------------------------------------------------- + +TEST(BestSimilarityFunctionTest, cell_type_must_match_operation) { + verify(gen_double("d8", 3), gen_double("b5d8", 7), max_sim, false); + verify(gen_float("d8", 3), gen_float("b5_2d8", 7), min_hamming, false); +} + +TEST(BestSimilarityFunctionTest, similarity_must_use_1d_vector) { + vespalib::string max_sim_2d_dist = "reduce(reduce(a*b,sum,d,e),max,b)"; + verify(gen_float("d8_1", 3), gen_float("b5d8_1", 7), max_sim, false); + verify(gen_float("d8e1", 3), gen_float("b5d8e1", 7), max_sim_2d_dist, false); +} + +TEST(BestSimilarityFunctionTest, similarity_dimension_must_be_inner) { + verify(gen_float("d8e3", 3), gen_float("b5d8", 7), max_sim, false); + verify(gen_float("b8", 3), gen_float("b8d5", 7), inv_max_sim, false); +} + +TEST(BestSimilarityFunctionTest, alternatives_must_use_a_single_dimension) { + vespalib::string max_sim_2d_best = "reduce(reduce(a*b,sum,d),max,a,b)"; + verify(gen_float("d8", 3), gen_float("a1b5d8", 7), max_sim_2d_best, false); +} + +TEST(BestSimilarityFunctionTest, alternatives_dimension_can_not_be_common) { + verify(gen_float("b5d8", 3), gen_float("b5d8", 7), max_sim, false); +} + +TEST(BestSimilarityFunctionTest, extra_common_nontrivial_dimensions_not_allowed) { + verify(gen_float("a3d8", 3), gen_float("a3b5d8", 7), max_sim, false); + verify(gen_float("a3_2d8", 3), gen_float("a3_2b5d8", 7), max_sim, false); +} + +TEST(BestSimilarityFunctionTest, secondary_tensor_must_not_contain_extra_nontrivial_dimensions) { + verify(gen_float("d8", 3), gen_float("a2b5d8", 7), max_sim, false); + verify(gen_float("d8", 3), gen_float("a2_1b5d8", 7), max_sim, false); +} + +//----------------------------------------------------------------------------- + +TEST(BestSimilarityFunctionTest, similar_expressions_are_not_optimized) { + vespalib::string other_join = "reduce(reduce(a+b,sum,d),max,b)"; + vespalib::string other_reduce = "reduce(reduce(a*b,min,d),max,b)"; + vespalib::string mismatch_best_sim = "reduce(reduce(a*b,sum,d),min,b)"; + vespalib::string mismatch_best_hamming = "reduce(reduce(hamming(a,b),sum,d),max,b)"; + verify(gen_float("d8", 3), gen_float("b5d8", 7), other_join, false); + verify(gen_float("d8", 3), gen_float("b5d8", 7), other_reduce, false); + verify(gen_float("d8", 3), gen_float("b5d8", 7), mismatch_best_sim, false); + verify(gen_int8("d8", 3), gen_int8("b5d8", 7), mismatch_best_hamming, false); +} + +//----------------------------------------------------------------------------- + +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp b/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp index f68c089e784..4998885c6a6 100644 --- a/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp +++ b/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp @@ -115,11 +115,9 @@ TEST(SumMaxDotProduct, similar_expressions_are_not_optimized) { vespalib::string max_sum_expr = "reduce(reduce(reduce(a*b,sum,z),sum,y),max,x)"; vespalib::string not_dp_expr1 = "reduce(reduce(reduce(a+b,sum,z),max,y),sum,x)"; vespalib::string not_dp_expr2 = "reduce(reduce(reduce(a*b,min,z),max,y),sum,x)"; - vespalib::string sum_all_expr = "reduce(reduce(reduce(a*b,sum,z),max,y),sum)"; assert_not_optimized(query, document, max_sum_expr); assert_not_optimized(query, document, not_dp_expr1); assert_not_optimized(query, document, not_dp_expr2); - assert_not_optimized(query, document, sum_all_expr); } //----------------------------------------------------------------------------- diff --git a/eval/src/vespa/eval/eval/optimize_tensor_function.cpp b/eval/src/vespa/eval/eval/optimize_tensor_function.cpp index c2e8d886fde..90849e55a71 100644 --- a/eval/src/vespa/eval/eval/optimize_tensor_function.cpp +++ b/eval/src/vespa/eval/eval/optimize_tensor_function.cpp @@ -11,6 +11,7 @@ #include <vespa/eval/instruction/sparse_full_overlap_join_function.h> #include <vespa/eval/instruction/mixed_inner_product_function.h> #include <vespa/eval/instruction/sum_max_dot_product_function.h> +#include <vespa/eval/instruction/best_similarity_function.h> #include <vespa/eval/instruction/dense_xw_product_function.h> #include <vespa/eval/instruction/dense_matmul_function.h> #include <vespa/eval/instruction/dense_multi_matmul_function.h> @@ -37,54 +38,59 @@ namespace vespalib::eval { namespace { -const TensorFunction &optimize_for_factory(const ValueBuilderFactory &, const TensorFunction &expr, Stash &stash) { - using Child = TensorFunction::Child; - Child root(expr); - { - std::vector<Child::CREF> nodes({root}); - for (size_t i = 0; i < nodes.size(); ++i) { - nodes[i].get().get().push_children(nodes); - } - while (!nodes.empty()) { - const Child &child = nodes.back().get(); - child.set(SumMaxDotProductFunction::optimize(child.get(), stash)); - child.set(DenseDotProductFunction::optimize(child.get(), stash)); - child.set(SparseDotProductFunction::optimize(child.get(), stash)); - child.set(DenseXWProductFunction::optimize(child.get(), stash)); - child.set(DenseMatMulFunction::optimize(child.get(), stash)); - child.set(DenseMultiMatMulFunction::optimize(child.get(), stash)); - child.set(MixedInnerProductFunction::optimize(child.get(), stash)); - child.set(DenseHammingDistance::optimize(child.get(), stash)); - nodes.pop_back(); - } +using Child = TensorFunction::Child; + +void run_optimize_pass(const Child &root, auto optimize_node) { + std::vector<Child::CREF> nodes({root}); + for (size_t i = 0; i < nodes.size(); ++i) { + nodes[i].get().get().push_children(nodes); } - { - std::vector<Child::CREF> nodes({root}); - for (size_t i = 0; i < nodes.size(); ++i) { - nodes[i].get().get().push_children(nodes); - } - while (!nodes.empty()) { - const Child &child = nodes.back().get(); - child.set(DenseSimpleExpandFunction::optimize(child.get(), stash)); - child.set(AddTrivialDimensionOptimizer::optimize(child.get(), stash)); - child.set(RemoveTrivialDimensionOptimizer::optimize(child.get(), stash)); - child.set(VectorFromDoublesFunction::optimize(child.get(), stash)); - child.set(DenseTensorCreateFunction::optimize(child.get(), stash)); - child.set(DenseTensorPeekFunction::optimize(child.get(), stash)); - child.set(DenseLambdaPeekOptimizer::optimize(child.get(), stash)); - child.set(UnpackBitsFunction::optimize(child.get(), stash)); - child.set(FastRenameOptimizer::optimize(child.get(), stash)); - child.set(PowAsMapOptimizer::optimize(child.get(), stash)); - child.set(InplaceMapFunction::optimize(child.get(), stash)); - child.set(MixedSimpleJoinFunction::optimize(child.get(), stash)); - child.set(JoinWithNumberFunction::optimize(child.get(), stash)); - child.set(DenseSingleReduceFunction::optimize(child.get(), stash)); - child.set(SparseMergeFunction::optimize(child.get(), stash)); - child.set(SparseNoOverlapJoinFunction::optimize(child.get(), stash)); - child.set(SparseFullOverlapJoinFunction::optimize(child.get(), stash)); - nodes.pop_back(); - } + while (!nodes.empty()) { + optimize_node(nodes.back().get()); + nodes.pop_back(); } +} + +const TensorFunction &optimize_for_factory(const ValueBuilderFactory &, const TensorFunction &expr, Stash &stash) { + Child root(expr); + run_optimize_pass(root, [&stash](const Child &child) + { + child.set(SumMaxDotProductFunction::optimize(child.get(), stash)); + }); + run_optimize_pass(root, [&stash](const Child &child) + { + child.set(BestSimilarityFunction::optimize(child.get(), stash)); + }); + run_optimize_pass(root, [&stash](const Child &child) + { + child.set(DenseDotProductFunction::optimize(child.get(), stash)); + child.set(SparseDotProductFunction::optimize(child.get(), stash)); + child.set(DenseXWProductFunction::optimize(child.get(), stash)); + child.set(DenseMatMulFunction::optimize(child.get(), stash)); + child.set(DenseMultiMatMulFunction::optimize(child.get(), stash)); + child.set(MixedInnerProductFunction::optimize(child.get(), stash)); + child.set(DenseHammingDistance::optimize(child.get(), stash)); + }); + run_optimize_pass(root, [&stash](const Child &child) + { + child.set(DenseSimpleExpandFunction::optimize(child.get(), stash)); + child.set(AddTrivialDimensionOptimizer::optimize(child.get(), stash)); + child.set(RemoveTrivialDimensionOptimizer::optimize(child.get(), stash)); + child.set(VectorFromDoublesFunction::optimize(child.get(), stash)); + child.set(DenseTensorCreateFunction::optimize(child.get(), stash)); + child.set(DenseTensorPeekFunction::optimize(child.get(), stash)); + child.set(DenseLambdaPeekOptimizer::optimize(child.get(), stash)); + child.set(UnpackBitsFunction::optimize(child.get(), stash)); + child.set(FastRenameOptimizer::optimize(child.get(), stash)); + child.set(PowAsMapOptimizer::optimize(child.get(), stash)); + child.set(InplaceMapFunction::optimize(child.get(), stash)); + child.set(MixedSimpleJoinFunction::optimize(child.get(), stash)); + child.set(JoinWithNumberFunction::optimize(child.get(), stash)); + child.set(DenseSingleReduceFunction::optimize(child.get(), stash)); + child.set(SparseMergeFunction::optimize(child.get(), stash)); + child.set(SparseNoOverlapJoinFunction::optimize(child.get(), stash)); + child.set(SparseFullOverlapJoinFunction::optimize(child.get(), stash)); + }); return root.get(); } diff --git a/eval/src/vespa/eval/eval/tensor_function.cpp b/eval/src/vespa/eval/eval/tensor_function.cpp index c0cd7280212..70797250c5a 100644 --- a/eval/src/vespa/eval/eval/tensor_function.cpp +++ b/eval/src/vespa/eval/eval/tensor_function.cpp @@ -443,7 +443,11 @@ const TensorFunction &inject(const ValueType &type, size_t param_idx, Stash &sta const TensorFunction &reduce(const TensorFunction &child, Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) { ValueType result_type = child.result_type().reduce(dimensions); - return stash.create<Reduce>(result_type, child, aggr, dimensions); + if (dimensions.empty()) { // expand dimension list for full reduce + return stash.create<Reduce>(result_type, child, aggr, child.result_type().dimension_names()); + } else { + return stash.create<Reduce>(result_type, child, aggr, dimensions); + } } const TensorFunction &map(const TensorFunction &child, map_fun_t function, Stash &stash) { diff --git a/eval/src/vespa/eval/eval/value.cpp b/eval/src/vespa/eval/eval/value.cpp index b799658cfae..e32e3152449 100644 --- a/eval/src/vespa/eval/eval/value.cpp +++ b/eval/src/vespa/eval/eval/value.cpp @@ -10,6 +10,11 @@ namespace eval { namespace { +struct EmptyView : Value::Index::View { + void lookup(ConstArrayRef<const string_id*> ) override {} + bool next_result(ConstArrayRef<string_id*> , size_t &) override { return false; } +}; + struct TrivialView : Value::Index::View { bool first = false; void lookup(ConstArrayRef<const string_id*> ) override { first = true; } @@ -36,6 +41,20 @@ struct MySum { } // <unnamed> +EmptyIndex::EmptyIndex() = default; +EmptyIndex EmptyIndex::_index; + +size_t +EmptyIndex::size() const +{ + return 0; +} + +std::unique_ptr<Value::Index::View> +EmptyIndex::create_view(ConstArrayRef<size_t>) const +{ + return std::make_unique<EmptyView>(); +} TrivialIndex::TrivialIndex() = default; TrivialIndex TrivialIndex::_index; diff --git a/eval/src/vespa/eval/eval/value.h b/eval/src/vespa/eval/eval/value.h index fcdaa7131c7..433ee36cb6a 100644 --- a/eval/src/vespa/eval/eval/value.h +++ b/eval/src/vespa/eval/eval/value.h @@ -64,6 +64,19 @@ struct Value { }; /** + * Common empty index + **/ +class EmptyIndex : public Value::Index { +private: + EmptyIndex(); + static EmptyIndex _index; +public: + static const EmptyIndex &get() { return _index; } + size_t size() const override; + std::unique_ptr<View> create_view(ConstArrayRef<size_t> dims) const override; +}; + +/** * Common index for values without any mapped dimensions. **/ class TrivialIndex : public Value::Index { diff --git a/eval/src/vespa/eval/instruction/CMakeLists.txt b/eval/src/vespa/eval/instruction/CMakeLists.txt index 3ed969c0a18..90ab58827d1 100644 --- a/eval/src/vespa/eval/instruction/CMakeLists.txt +++ b/eval/src/vespa/eval/instruction/CMakeLists.txt @@ -3,6 +3,7 @@ vespa_add_library(eval_instruction OBJECT SOURCES add_trivial_dimension_optimizer.cpp + best_similarity_function.cpp dense_cell_range_function.cpp dense_dot_product_function.cpp dense_hamming_distance.cpp diff --git a/eval/src/vespa/eval/instruction/best_similarity_function.cpp b/eval/src/vespa/eval/instruction/best_similarity_function.cpp new file mode 100644 index 00000000000..a8531d84cf0 --- /dev/null +++ b/eval/src/vespa/eval/instruction/best_similarity_function.cpp @@ -0,0 +1,224 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "best_similarity_function.h" +#include <vespa/eval/eval/operation.h> +#include <vespa/eval/eval/value.h> +#include <vespa/vespalib/util/binary_hamming_distance.h> +#include <cblas.h> + +namespace vespalib::eval { + +using namespace tensor_function; +using namespace operation; + +namespace { + +struct BestSimParam { + ValueType res_type; + size_t inner_size; + BestSimParam(const ValueType &res_type_in, size_t inner_size_in) + : res_type(res_type_in), inner_size(inner_size_in) {} +}; + +struct UseDotProduct { + static float calc(const float *pri, const float *sec, size_t size) { + return cblas_sdot(size, pri, 1, sec, 1); + } +}; + +struct UseHammingDist { + static float calc(const Int8Float *pri, const Int8Float *sec, size_t size) { + return binary_hamming_distance(pri, sec, size); + } +}; + +template <typename CT, typename AGGR, typename DIST> +float best_similarity(const CT *pri, ConstArrayRef<CT> sec_cells, size_t inner_size) { + AGGR aggr; + for (const CT *sec = sec_cells.begin(); sec < sec_cells.end(); sec += inner_size) { + aggr.sample(DIST::calc(pri, sec, inner_size)); + } + return aggr.result(); +} + +template <bool is_double> +const Value &create_empty_result(const ValueType &type, Stash &stash) { + if (is_double) { + return stash.create<DoubleValue>(0.0); + } else if (type.count_mapped_dimensions() == 0) { + auto zero_cells = stash.create_array<float>(type.dense_subspace_size()); + return stash.create<ValueView>(type, TrivialIndex::get(), TypedCells(zero_cells)); + } else { + return stash.create<ValueView>(type, EmptyIndex::get(), TypedCells(nullptr, CellType::FLOAT, 0)); + } +} + +template <bool is_double, typename CT, typename AGGR, typename DIST> +void my_best_similarity_op(InterpretedFunction::State &state, uint64_t param) { + size_t inner_size = is_double ? param : unwrap_param<BestSimParam>(param).inner_size; + const ValueType &res_type = is_double ? DoubleValue::shared_type() : unwrap_param<BestSimParam>(param).res_type; + const Value &pri_value = state.peek(1); + auto pri_cells = pri_value.cells().typify<CT>(); + auto sec_cells = state.peek(0).cells().typify<CT>(); + if ((pri_cells.size() == 0) || (sec_cells.size() == 0)) { + return state.pop_pop_push(create_empty_result<is_double>(res_type, state.stash)); + } + if (is_double) { + auto best_sim = best_similarity<CT, AGGR, DIST>(pri_cells.begin(), sec_cells, inner_size); + return state.pop_pop_push(state.stash.create<DoubleValue>(best_sim)); + } + auto out_cells = state.stash.create_uninitialized_array<float>(pri_cells.size() / inner_size); + const CT *pri = pri_cells.begin(); + for (auto &out: out_cells) { + out = best_similarity<CT, AGGR, DIST>(pri, sec_cells, inner_size); + pri += inner_size; + } + Value &result_ref = state.stash.create<ValueView>(res_type, pri_value.index(), TypedCells(out_cells)); + state.pop_pop_push(result_ref); +} + +//----------------------------------------------------------------------------- + +size_t stride(const ValueType &type, const vespalib::string &name) { + size_t stride = 0; + for (const auto &dim: type.dimensions()) { + if (dim.is_indexed()) { + if (dim.name == name) { + stride = 1; + } else { + stride *= dim.size; + } + } + } + return stride; +} + +bool check_dims(const ValueType &pri, const ValueType &sec, + const vespalib::string &best, const vespalib::string &inner) +{ + if ((stride(pri, inner) != 1) || (stride(sec, inner) != 1)) { + return false; + } + if (pri.dimension_index(best) != ValueType::Dimension::npos) { + return false; + } + if (sec.dimension_index(best) == ValueType::Dimension::npos) { + return false; + } + for (auto &&type = sec.reduce({inner,best}); auto &&dim: type.dimensions()) { + if (!dim.is_trivial()) { + return false; + } + } + return true; +} + +size_t get_dim_size(const ValueType &type, const vespalib::string &dim) { + size_t npos = ValueType::Dimension::npos; + size_t idx = type.dimension_index(dim); + assert(idx != npos); + assert(type.dimensions()[idx].is_indexed()); + return type.dimensions()[idx].size; +} + +const Reduce *check_reduce(const TensorFunction &expr, std::initializer_list<Aggr> allow) { + if (auto reduce = as<Reduce>(expr)) { + if (reduce->dimensions().size() == 1) { + if (std::find(allow.begin(), allow.end(), reduce->aggr()) != allow.end()) { + return reduce; + } + } + } + return nullptr; +} + +const Join *check_join(const TensorFunction &expr, std::initializer_list<op2_t> allow) { + if (auto join = as<Join>(expr)) { + if (std::find(allow.begin(), allow.end(), join->function()) != allow.end()) { + return join; + } + } + return nullptr; +} + +struct SelectFun { + const ValueType &res_type; + const ValueType &lhs_type; + const ValueType &rhs_type; + SelectFun(const auto &res, const auto &lhs, const auto &rhs) + : res_type(res.result_type()), lhs_type(lhs.result_type()), rhs_type(rhs.result_type()) {} + template <typename R1> static InterpretedFunction::op_function invoke(Aggr best_aggr, op2_t join_fun, CellType cell_types) { + if ((best_aggr == Aggr::MAX) && (join_fun == Mul::f) && (cell_types == CellType::FLOAT)) { + return my_best_similarity_op<R1::value, float, aggr::Max<float>, UseDotProduct>; + } + if ((best_aggr == Aggr::MIN) && (join_fun == Hamming::f) && (cell_types == CellType::INT8)) { + return my_best_similarity_op<R1::value, Int8Float, aggr::Min<float>, UseHammingDist>; + } + return nullptr; + } + InterpretedFunction::op_function operator()(Aggr best_aggr, op2_t join_fun) { + static_assert(std::is_same_v<float, CellValueType<CellType::FLOAT>>); + static_assert(std::is_same_v<Int8Float, CellValueType<CellType::INT8>>); + if (lhs_type.cell_type() != rhs_type.cell_type()) { + return nullptr; + } + return typify_invoke<1,TypifyBool,SelectFun>(res_type.is_double(), best_aggr, join_fun, lhs_type.cell_type()); + } +}; + +} // namespace <unnamed> + +uint64_t +BestSimilarityFunction::make_param(Stash &stash) const +{ + if (result_type().is_double()) { + return _inner_size; + } + return wrap_param<BestSimParam>(stash.create<BestSimParam>(result_type(), _inner_size)); +} + +BestSimilarityFunction::BestSimilarityFunction(const ValueType &res_type_in, + const TensorFunction &pri, + const TensorFunction &sec, + InterpretedFunction::op_function my_fun, + size_t inner_size) + : tensor_function::Op2(res_type_in, pri, sec), + _my_fun(my_fun), + _inner_size(inner_size) +{ +} + +InterpretedFunction::Instruction +BestSimilarityFunction::compile_self(const ValueBuilderFactory &, Stash &stash) const +{ + return InterpretedFunction::Instruction(_my_fun, make_param(stash)); +} + +const TensorFunction & +BestSimilarityFunction::optimize(const TensorFunction &expr, Stash &stash) +{ + if (auto best_reduce = check_reduce(expr, {Aggr::MAX, Aggr::MIN})) { + if (auto sum_reduce = check_reduce(best_reduce->child(), {Aggr::SUM})) { + if (auto join = check_join(sum_reduce->child(), {Mul::f, Hamming::f})) { + SelectFun select_fun(expr, join->lhs(), join->rhs()); + if (auto my_fun = select_fun(best_reduce->aggr(), join->function())) { + const auto &best_dim = best_reduce->dimensions()[0]; + const auto &inner_dim = sum_reduce->dimensions()[0]; + const TensorFunction &lhs = join->lhs(); + const TensorFunction &rhs = join->rhs(); + if (check_dims(lhs.result_type(), rhs.result_type(), best_dim, inner_dim)) { + size_t inner_size = get_dim_size(lhs.result_type(), inner_dim); + return stash.create<BestSimilarityFunction>(expr.result_type(), lhs, rhs, my_fun, inner_size); + } + if (check_dims(rhs.result_type(), lhs.result_type(), best_dim, inner_dim)) { + size_t inner_size = get_dim_size(rhs.result_type(), inner_dim); + return stash.create<BestSimilarityFunction>(expr.result_type(), rhs, lhs, my_fun, inner_size); + } + } + } + } + } + return expr; +} + +} // namespace diff --git a/eval/src/vespa/eval/instruction/best_similarity_function.h b/eval/src/vespa/eval/instruction/best_similarity_function.h new file mode 100644 index 00000000000..5b61eaec085 --- /dev/null +++ b/eval/src/vespa/eval/instruction/best_similarity_function.h @@ -0,0 +1,38 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/eval/eval/tensor_function.h> + +namespace vespalib::eval { + +/** + * Tensor function combining multiple vector-based similarity measures + * to find the best one. This function supports the following cases: + * + * - maximum dot product of vectors with float cell type (MaxSim) + * - minimum hamming distance of bitvectors with int8 cell type + * + * The vectors used to calculate the individual distance metrics must + * be the inner dense dimension of both inputs. The dimension reduced + * to find the best similarity measure must be the remaining dimension + * of one of the inputs. + **/ +class BestSimilarityFunction : public tensor_function::Op2 +{ +private: + InterpretedFunction::op_function _my_fun; + size_t _inner_size; + uint64_t make_param(Stash &stash) const; +public: + BestSimilarityFunction(const ValueType &res_type_in, + const TensorFunction &pri, + const TensorFunction &sec, + InterpretedFunction::op_function my_fun, + size_t inner_size); + InterpretedFunction::Instruction compile_self(const ValueBuilderFactory &factory, Stash &stash) const override; + bool result_is_mutable() const override { return true; } + static const TensorFunction &optimize(const TensorFunction &expr, Stash &stash); +}; + +} // namespace diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index de54b17da09..7e9681209f5 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -123,7 +123,7 @@ public class Flags { ZONE_ID, APPLICATION_ID); public static final UnboundIntFlag NUM_DEPLOY_HELPER_THREADS = defineIntFlag( - "num-model-builder-threads", 0, + "num-model-builder-threads", -1, List.of("balder"), "2021-09-09", "2021-10-01", "Number of threads used for speeding up building of models.", "Takes effect on first (re)start of config server"); @@ -283,6 +283,20 @@ public class Flags { TENANT_ID ); + public static final UnboundIntFlag MAX_CONNECTION_LIFE_IN_HOSTED = defineIntFlag( + "max-connection-life-in-hosted", 45, + List.of("bjorncs"), "2021-09-30", "2021-12-31", + "Max connection life for connections to jdisc endpoints in hosted", + "Takes effect at redeployment", + APPLICATION_ID); + + public static final UnboundBooleanFlag ENABLE_ROUTING_REUSE_PORT = defineFeatureFlag( + "enable-routing-reuse-port", false, + List.of("mortent"), "2021-09-29", "2021-12-31", + "Enable reuse port in routing configuration", + "Takes effect on container restart", + HOSTNAME + ); /** WARNING: public for testing: All flags should be defined in {@link Flags}. */ public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List<String> owners, diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java index 614f2fe6732..907d57fffe7 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java @@ -4,6 +4,7 @@ package ai.vespa.hosted.api; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.security.KeyUtils; @@ -36,9 +37,11 @@ import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.OptionalLong; +import java.util.Set; import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.function.Supplier; @@ -175,6 +178,28 @@ public abstract class ControllerHttpClient { GET)); } + /** Returns the tenants in this system. */ + public Set<TenantName> tenants() { + return toTenants(send(request(HttpRequest.newBuilder(tenantsPath()) + .timeout(Duration.ofSeconds(20)), + GET))); + } + + /** Returns the applications for the given tenant. */ + public Set<ApplicationName> applications(TenantName tenantName) { + return toApplications(send(request(HttpRequest.newBuilder(applicationsPath(tenantName)) + .timeout(Duration.ofSeconds(20)), + GET))); + } + + /** Returns the application instances for the given tenant. */ + public Set<ApplicationId> applicationInstances(TenantName tenantName) { + return toApplicationInstances(send(request(HttpRequest.newBuilder(applicationsPath(tenantName)) + .timeout(Duration.ofSeconds(20)), + GET)), + tenantName); + } + /** Follows the given deployment job until it is done, or this thread is interrupted, at which point the current status is returned. */ public DeploymentLog followDeploymentUntilDone(ApplicationId id, ZoneId zone, long run, Consumer<DeploymentLog.Entry> out) { @@ -227,10 +252,14 @@ public abstract class ControllerHttpClient { return concatenated(endpoint, "application", "v4"); } + private URI tenantsPath() { return concatenated(applicationApiPath(), "tenant"); } + private URI tenantPath(TenantName tenant) { return concatenated(applicationApiPath(), "tenant", tenant.value()); } + private URI applicationsPath(TenantName tenant) { return concatenated(tenantPath(tenant), "application"); } + private URI applicationPath(TenantName tenant, ApplicationName application) { return concatenated(tenantPath(tenant), "application", application.value()); } @@ -415,6 +444,32 @@ public abstract class ControllerHttpClient { : OptionalLong.empty()); } + private static Set<TenantName> toTenants(HttpResponse<byte[]> response) { + Set<TenantName> tenants = new HashSet<>(); + toInspector(response).traverse((ArrayTraverser) (___, entryObject) -> + tenants.add(TenantName.from(entryObject.field("tenant").asString()))); + return tenants; + } + + private static Set<ApplicationName> toApplications(HttpResponse<byte[]> response) { + Set<ApplicationName> applications = new HashSet<>(); + toInspector(response).traverse((ArrayTraverser) (___, entryObject) -> + applications.add(ApplicationName.from(entryObject.field("application").asString()))); + return applications; + } + + private static Set<ApplicationId> toApplicationInstances(HttpResponse<byte[]> response, TenantName tenantName) { + Set<ApplicationId> applicationIds = new HashSet<>(); + toInspector(response).traverse((ArrayTraverser) (___, entryObject) -> { + ApplicationName applicationName = ApplicationName.from(entryObject.field("application").asString()); + entryObject.field("instances").traverse((ArrayTraverser) (____, instanceObject) -> + applicationIds.add(ApplicationId.from(tenantName, + applicationName, + InstanceName.from(instanceObject.field("instance").asString())))); + }); + return applicationIds; + } + private static Slime toSlime(byte[] data) { return SlimeUtils.jsonToSlime(data); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java index cdf117c11ce..70e5cf14b54 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java @@ -44,7 +44,10 @@ public class NodesAndHosts<NL extends NodeList> { public NL nodes() { return nodes; } public NodeList childrenOf(Node host) { - HostAndNodes hostAndNodes = host2Nodes.get(host.hostname()); + return childrenOf(host.hostname()); + } + public NodeList childrenOf(String hostname) { + HostAndNodes hostAndNodes = host2Nodes.get(hostname); return hostAndNodes != null ? NodeList.copyOf(hostAndNodes.children) : NodeList.of(); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java index 78686d8aeed..9c5b556b309 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java @@ -22,6 +22,7 @@ import com.yahoo.vespa.hosted.provision.LockedNodeList; 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.hosted.provision.NodesAndHosts; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.node.History; import com.yahoo.vespa.hosted.provision.node.IP; @@ -269,8 +270,8 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer { ArrayList<Node> mutableNodes) { for (int clusterIndex = 0; clusterIndex < preprovisionCapacity.size(); ++clusterIndex) { ClusterCapacity clusterCapacity = preprovisionCapacity.get(clusterIndex); - LockedNodeList nodeList = new LockedNodeList(mutableNodes, () -> {}); - List<Node> candidates = findCandidates(clusterCapacity, clusterIndex, nodeList); + NodesAndHosts<LockedNodeList> nodesAndHosts = NodesAndHosts.create(new LockedNodeList(mutableNodes, () -> {})); + List<Node> candidates = findCandidates(clusterCapacity, clusterIndex, nodesAndHosts); int deficit = Math.max(0, clusterCapacity.count() - candidates.size()); if (deficit > 0) { return Optional.of(clusterCapacity.withCount(deficit)); @@ -283,7 +284,7 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer { return Optional.empty(); } - private List<Node> findCandidates(ClusterCapacity clusterCapacity, int clusterIndex, LockedNodeList nodeList) { + private List<Node> findCandidates(ClusterCapacity clusterCapacity, int clusterIndex, NodesAndHosts<LockedNodeList> nodesAndHosts) { NodeResources nodeResources = toNodeResources(clusterCapacity); // We'll allocate each ClusterCapacity as a unique cluster in a dummy application @@ -296,7 +297,7 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer { NodeSpec nodeSpec = NodeSpec.from(clusterCapacity.count(), nodeResources, false, true); int wantedGroups = 1; - NodePrioritizer prioritizer = new NodePrioritizer(nodeList, applicationId, clusterSpec, nodeSpec, wantedGroups, + NodePrioritizer prioritizer = new NodePrioritizer(nodesAndHosts, applicationId, clusterSpec, nodeSpec, wantedGroups, true, nodeRepository().nameResolver(), nodeRepository().resourcesCalculator(), nodeRepository().spareCount()); List<NodeCandidate> nodeCandidates = prioritizer.collect(List.of()); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java index aad32f64fa8..76ccac9c618 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java @@ -10,6 +10,7 @@ import com.yahoo.transaction.Mutex; import com.yahoo.vespa.hosted.provision.LockedNodeList; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeRepository; +import com.yahoo.vespa.hosted.provision.NodesAndHosts; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner.HostSharing; @@ -30,6 +31,18 @@ public class GroupPreparer { private final NodeRepository nodeRepository; private final Optional<HostProvisioner> hostProvisioner; + /** + * Contains list of prepared nodes and the NodesAndHost object to use for next prepare call. + */ + public static class PrepareResult { + public final List<Node> prepared; + public final NodesAndHosts<LockedNodeList> allNodesAndHosts; + PrepareResult(List<Node> prepared, NodesAndHosts<LockedNodeList> allNodesAndHosts) { + this.prepared = prepared; + this.allNodesAndHosts = allNodesAndHosts; + } + } + public GroupPreparer(NodeRepository nodeRepository, Optional<HostProvisioner> hostProvisioner) { this.nodeRepository = nodeRepository; this.hostProvisioner = hostProvisioner; @@ -45,33 +58,44 @@ public class GroupPreparer { * This method will remove from this list if it finds it needs additional nodes * @param indices the next available node indices for this cluster. * This method will consume these when it allocates new nodes to the cluster. - * @return the list of nodes this cluster group will have allocated if activated + * @param allNodesAndHosts list of all nodes and hosts. Use createNodesAndHostUnlocked to create param for + * first invocation. Then use previous PrepareResult.allNodesAndHosts for the following. + * @return the list of nodes this cluster group will have allocated if activated, and */ // Note: This operation may make persisted changes to the set of reserved and inactive nodes, // but it may not change the set of active nodes, as the active nodes must stay in sync with the // active config model which is changed on activate - public List<Node> prepare(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, - List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups) { + public PrepareResult prepare(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, + List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups, + NodesAndHosts<LockedNodeList> allNodesAndHosts) { // Try preparing in memory without global unallocated lock. Most of the time there should be no changes and we // can return nodes previously allocated. - { - NodeAllocation probeAllocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes, - indices::probeNext, wantedGroups, PROBE_LOCK); - if (probeAllocation.fulfilledAndNoChanges()) { - List<Node> acceptedNodes = probeAllocation.finalNodes(); - surplusActiveNodes.removeAll(acceptedNodes); - indices.commitProbe(); - return acceptedNodes; - } + NodeAllocation probeAllocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes, + indices::probeNext, wantedGroups, allNodesAndHosts); + if (probeAllocation.fulfilledAndNoChanges()) { + List<Node> acceptedNodes = probeAllocation.finalNodes(); + surplusActiveNodes.removeAll(acceptedNodes); + indices.commitProbe(); + return new PrepareResult(acceptedNodes, allNodesAndHosts); + } else { + // There were some changes, so re-do the allocation with locks indices.resetProbe(); + List<Node> prepared = prepareWithLocks(application, cluster, requestedNodes, surplusActiveNodes, indices, wantedGroups); + return new PrepareResult(prepared, createNodesAndHostUnlocked()); } + } + + // Use this to create allNodesAndHosts param to prepare method for first invocation of prepare + public NodesAndHosts<LockedNodeList> createNodesAndHostUnlocked() { return NodesAndHosts.create(nodeRepository.nodes().list(PROBE_LOCK)); } - // There were some changes, so re-do the allocation with locks + /// Note that this will write to the node repo. + private List<Node> prepareWithLocks(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, + List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups) { try (Mutex lock = nodeRepository.nodes().lock(application); Mutex allocationLock = nodeRepository.nodes().lockUnallocated()) { - + NodesAndHosts<LockedNodeList> allNodesAndHosts = NodesAndHosts.create(nodeRepository.nodes().list(allocationLock)); NodeAllocation allocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes, - indices::next, wantedGroups, allocationLock); + indices::next, wantedGroups, allNodesAndHosts); NodeType hostType = allocation.nodeType().hostType(); if (canProvisionDynamically(hostType)) { HostSharing sharing = hostSharing(requestedNodes, hostType); @@ -115,10 +139,10 @@ public class GroupPreparer { private NodeAllocation prepareAllocation(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, List<Node> surplusActiveNodes, Supplier<Integer> nextIndex, int wantedGroups, - Mutex allocationLock) { - LockedNodeList allNodes = nodeRepository.nodes().list(allocationLock); - NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requestedNodes, nextIndex, nodeRepository); - NodePrioritizer prioritizer = new NodePrioritizer(allNodes, + NodesAndHosts<LockedNodeList> allNodesAndHosts) { + + NodeAllocation allocation = new NodeAllocation(allNodesAndHosts, application, cluster, requestedNodes, nextIndex, nodeRepository); + NodePrioritizer prioritizer = new NodePrioritizer(allNodesAndHosts, application, cluster, requestedNodes, diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java index bdb7cb4b64d..d96613201c3 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java @@ -11,6 +11,7 @@ import com.yahoo.config.provision.SystemName; 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.hosted.provision.NodesAndHosts; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.node.Allocation; @@ -40,7 +41,7 @@ class NodeAllocation { private static final Logger LOG = Logger.getLogger(NodeAllocation.class.getName()); /** List of all nodes in node-repository */ - private final NodeList allNodes; + private final NodesAndHosts<? extends NodeList> allNodesAndHosts; /** The application this list is for */ private final ApplicationId application; @@ -80,9 +81,9 @@ class NodeAllocation { private final NodeRepository nodeRepository; private final NodeResourceLimits nodeResourceLimits; - NodeAllocation(NodeList allNodes, ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, + NodeAllocation(NodesAndHosts<? extends NodeList> allNodesAndHosts, ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, Supplier<Integer> nextIndex, NodeRepository nodeRepository) { - this.allNodes = allNodes; + this.allNodesAndHosts = allNodesAndHosts; this.application = application; this.cluster = cluster; this.requestedNodes = requestedNodes; @@ -199,7 +200,7 @@ class NodeAllocation { // In non-dynamic provisioned zones we require that if either of the nodes on the host requires exclusivity, // then all the nodes on the host must have the same owner - for (Node nodeOnHost : allNodes.childrenOf(candidate.parentHostname().get())) { + for (Node nodeOnHost : allNodesAndHosts.childrenOf(candidate.parentHostname().get())) { if (nodeOnHost.allocation().isEmpty()) continue; if (requestedNodes.isExclusive() || nodeOnHost.allocation().get().membership().cluster().isExclusive()) { if ( ! nodeOnHost.allocation().get().owner().equals(application)) return true; @@ -273,7 +274,7 @@ class NodeAllocation { } private Node resize(Node node) { - NodeResources hostResources = allNodes.parentOf(node).get().flavor().resources(); + NodeResources hostResources = allNodesAndHosts.parentOf(node).get().flavor().resources(); return node.with(new Flavor(requestedNodes.resources().get() .with(hostResources.diskSpeed()) .with(hostResources.storageType())), @@ -324,7 +325,7 @@ class NodeAllocation { if (hostType == NodeType.host) return nodeRepository.database().readProvisionIndices(count); // Infrastructure hosts have fixed indices, starting at 1 - Set<Integer> currentIndices = allNodes.nodeType(hostType) + Set<Integer> currentIndices = allNodesAndHosts.nodes().nodeType(hostType) .hostnames() .stream() // TODO(mpolden): Use cluster index instead of parsing hostname, once all @@ -424,7 +425,7 @@ class NodeAllocation { if (nodeType() == NodeType.tenant) return accepted; // Infrastructure nodes are always allocated by type. Count all nodes as accepted so that we never exceed // the wanted number of nodes for the type. - return allNodes.nodeType(nodeType()).size(); + return allNodesAndHosts.nodes().nodeType(nodeType()).size(); } /** Prefer to retire nodes we want the least */ diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java index 1f618a18d24..c7a2d27ca00 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java @@ -31,7 +31,7 @@ import java.util.stream.Collectors; public class NodePrioritizer { private final List<NodeCandidate> nodes = new ArrayList<>(); - private final NodesAndHosts<LockedNodeList> allNodes; + private final NodesAndHosts<LockedNodeList> allNodesAndHosts; private final HostCapacity capacity; private final NodeSpec requestedNodes; private final ApplicationId application; @@ -45,21 +45,21 @@ public class NodePrioritizer { private final int currentClusterSize; private final Set<Node> spareHosts; - public NodePrioritizer(LockedNodeList allLockedNodes, ApplicationId application, ClusterSpec clusterSpec, NodeSpec nodeSpec, + public NodePrioritizer(NodesAndHosts<LockedNodeList> allNodesAndHosts, ApplicationId application, ClusterSpec clusterSpec, NodeSpec nodeSpec, int wantedGroups, boolean dynamicProvisioning, NameResolver nameResolver, HostResourcesCalculator hostResourcesCalculator, int spareCount) { - this.allNodes = NodesAndHosts.create(allLockedNodes); - this.capacity = new HostCapacity(allNodes, hostResourcesCalculator); + this.allNodesAndHosts = allNodesAndHosts; + this.capacity = new HostCapacity(this.allNodesAndHosts, hostResourcesCalculator); this.requestedNodes = nodeSpec; this.clusterSpec = clusterSpec; this.application = application; this.dynamicProvisioning = dynamicProvisioning; this.spareHosts = dynamicProvisioning ? - capacity.findSpareHostsInDynamicallyProvisionedZones(allNodes.nodes().asList()) : - capacity.findSpareHosts(allNodes.nodes().asList(), spareCount); + capacity.findSpareHostsInDynamicallyProvisionedZones(this.allNodesAndHosts.nodes().asList()) : + capacity.findSpareHosts(this.allNodesAndHosts.nodes().asList(), spareCount); this.nameResolver = nameResolver; - NodeList nodesInCluster = allNodes.nodes().owner(application).type(clusterSpec.type()).cluster(clusterSpec.id()); + NodeList nodesInCluster = this.allNodesAndHosts.nodes().owner(application).type(clusterSpec.type()).cluster(clusterSpec.id()); NodeList nonRetiredNodesInCluster = nodesInCluster.not().retired(); long currentGroups = nonRetiredNodesInCluster.state(Node.State.active).stream() .flatMap(node -> node.allocation() @@ -135,7 +135,7 @@ public class NodePrioritizer { private void addCandidatesOnExistingHosts() { if ( !canAllocateNew) return; - for (Node host : allNodes.nodes()) { + for (Node host : allNodesAndHosts.nodes()) { if ( ! Nodes.canAllocateTenantNodeTo(host, dynamicProvisioning)) continue; if (host.reservedTo().isPresent() && !host.reservedTo().get().equals(application.tenant())) continue; if (host.reservedTo().isPresent() && application.instance().isTester()) continue; @@ -143,12 +143,12 @@ public class NodePrioritizer { if ( ! host.exclusiveToClusterType().map(clusterSpec.type()::equals).orElse(true)) continue; if (spareHosts.contains(host) && !canAllocateToSpareHosts) continue; if ( ! capacity.hasCapacity(host, requestedNodes.resources().get())) continue; - if ( ! allNodes.childrenOf(host).owner(application).cluster(clusterSpec.id()).isEmpty()) continue; + if ( ! allNodesAndHosts.childrenOf(host).owner(application).cluster(clusterSpec.id()).isEmpty()) continue; nodes.add(NodeCandidate.createNewChild(requestedNodes.resources().get(), capacity.availableCapacityOf(host), host, spareHosts.contains(host), - allNodes.nodes(), + allNodesAndHosts.nodes(), nameResolver)); } } @@ -156,7 +156,7 @@ public class NodePrioritizer { /** Add existing nodes allocated to the application */ private void addApplicationNodes() { EnumSet<Node.State> legalStates = EnumSet.of(Node.State.active, Node.State.inactive, Node.State.reserved); - allNodes.nodes().stream() + allNodesAndHosts.nodes().stream() .filter(node -> node.type() == requestedNodes.type()) .filter(node -> legalStates.contains(node.state())) .filter(node -> node.allocation().isPresent()) @@ -169,7 +169,7 @@ public class NodePrioritizer { /** Add nodes already provisioned, but not allocated to any application */ private void addReadyNodes() { - allNodes.nodes().stream() + allNodesAndHosts.nodes().stream() .filter(node -> node.type() == requestedNodes.type()) .filter(node -> node.state() == Node.State.ready) .map(node -> candidateFrom(node, false)) @@ -179,7 +179,7 @@ public class NodePrioritizer { /** Create a candidate from given pre-existing node */ private NodeCandidate candidateFrom(Node node, boolean isSurplus) { - Optional<Node> optionalParent = allNodes.parentOf(node); + Optional<Node> optionalParent = allNodesAndHosts.parentOf(node); if (optionalParent.isPresent()) { Node parent = optionalParent.get(); return NodeCandidate.createChild(node, @@ -217,7 +217,7 @@ public class NodePrioritizer { */ private boolean canStillAllocate(Node node) { if (node.type() != NodeType.tenant || node.parentHostname().isEmpty()) return true; - Optional<Node> parent = allNodes.parentOf(node); + Optional<Node> parent = allNodesAndHosts.parentOf(node); return parent.isPresent() ? Nodes.canAllocateTenantNodeTo(parent.get(), dynamicProvisioning) : null; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java index d5b754117cb..a27cf252a3a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java @@ -5,9 +5,11 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.OutOfCapacityException; +import com.yahoo.vespa.hosted.provision.LockedNodeList; 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.hosted.provision.NodesAndHosts; import com.yahoo.vespa.hosted.provision.node.Nodes; import java.util.ArrayList; @@ -57,22 +59,23 @@ class Preparer { // but it may not change the set of active nodes, as the active nodes must stay in sync with the // active config model which is changed on activate private List<Node> prepareNodes(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, int wantedGroups) { - NodeList allNodes = nodeRepository.nodes().list(); - NodeList appNodes = allNodes.owner(application); + NodesAndHosts<LockedNodeList> allNodesAndHosts = groupPreparer.createNodesAndHostUnlocked(); + NodeList appNodes = allNodesAndHosts.nodes().owner(application); List<Node> surplusNodes = findNodesInRemovableGroups(appNodes, cluster, wantedGroups); List<Integer> usedIndices = appNodes.cluster(cluster.id()).mapToList(node -> node.allocation().get().membership().index()); NodeIndices indices = new NodeIndices(usedIndices, ! cluster.type().isContent()); List<Node> acceptedNodes = new ArrayList<>(); + for (int groupIndex = 0; groupIndex < wantedGroups; groupIndex++) { ClusterSpec clusterGroup = cluster.with(Optional.of(ClusterSpec.Group.from(groupIndex))); - List<Node> accepted = groupPreparer.prepare(application, clusterGroup, - requestedNodes.fraction(wantedGroups), surplusNodes, - indices, wantedGroups); - + GroupPreparer.PrepareResult result = groupPreparer.prepare( + application, clusterGroup, requestedNodes.fraction(wantedGroups), + surplusNodes, indices, wantedGroups, allNodesAndHosts); + allNodesAndHosts = result.allNodesAndHosts; // Might have changed + List<Node> accepted = result.prepared; if (requestedNodes.rejectNonActiveParent()) { - Nodes nodes = nodeRepository.nodes(); - NodeList activeHosts = nodes.list(Node.State.active).parents().nodeType(requestedNodes.type().hostType()); + NodeList activeHosts = allNodesAndHosts.nodes().state(Node.State.active).parents().nodeType(requestedNodes.type().hostType()); accepted = accepted.stream() .filter(node -> node.parentHostname().isEmpty() || activeHosts.parentOf(node).isPresent()) .collect(Collectors.toList()); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java index 80100128379..d9637c412fd 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java @@ -153,8 +153,6 @@ class NodesResponse extends SlimeJsonResponse { allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray("networkPorts"))); orchestrator.apply(new HostName(node.hostname())) .ifPresent(info -> { - object.setBool("allowedToBeDown", info.status().isSuspended()); - // TODO: Remove allowedToBeDown as a special-case of orchestratorStatus if (info.status() != HostStatus.NO_REMARKS) { object.setString("orchestratorStatus", info.status().asString()); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-container1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-container1.json index 6be127315e2..33b7a91fc61 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-container1.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-container1.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":1.0, "memoryGb":4.0, "diskGb":100.0, "bandwidthGbps":1.0,"diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json index cc9f8c7c58f..ec46755b5ac 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":4.0, "memoryGb":32.0, "diskGb":1600.0, "bandwidthGbps":20.0, "diskSpeed": "fast", "storageType":"remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "currentOsVersion": "7.5.2", diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-2.json index f9dd810902c..c44b172633d 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-2.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-2.json @@ -34,7 +34,6 @@ "diskSpeed": "fast", "storageType": "remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json index 74d402de796..df745729288 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-3.json @@ -34,7 +34,6 @@ "diskSpeed": "fast", "storageType": "remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json index b1d8c5fba9a..f6ff292c830 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports-4.json @@ -34,7 +34,6 @@ "diskSpeed": "fast", "storageType": "remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports.json index 660e67796f9..335a95f47f5 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-reports.json @@ -34,7 +34,6 @@ "diskSpeed": "fast", "storageType": "remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1.json index b8397cfb6af..4b7eecdfc26 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":4.0, "memoryGb":32.0, "diskGb":1600.0, "bandwidthGbps":20.0, "diskSpeed":"fast", "storageType":"remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json index 60d194163b7..e0a7448c111 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":4.0, "memoryGb":32.0, "diskGb":1600.0, "bandwidthGbps":20.0, "diskSpeed":"fast", "storageType":"remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node3.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node3.json index 58db8d47bcc..21f3a54311f 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node3.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node3.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":4.0, "memoryGb":32.0, "diskGb":1600.0, "bandwidthGbps":20.0, "diskSpeed":"fast", "storageType":"remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node4.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node4.json index b5ddc4d99a8..789b0580357 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node4.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node4.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":4.0, "memoryGb":32.0, "diskGb":1600.0, "bandwidthGbps":20.0, "diskSpeed":"fast", "storageType":"remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node5.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node5.json index 8f4c91282ca..35ec6fc0273 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node5.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node5.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":4.0, "memoryGb":32.0, "diskGb":1600.0, "bandwidthGbps":20.0, "diskSpeed":"fast", "storageType":"remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/dockerhost1-with-firmware-data.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/dockerhost1-with-firmware-data.json index 866efefc1d3..498780a2eba 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/dockerhost1-with-firmware-data.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/dockerhost1-with-firmware-data.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":4.0, "memoryGb":32.0, "diskGb":1600.0, "bandwidthGbps":20.0, "diskSpeed":"fast", "storageType":"remote" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "currentFirmwareCheck": 100, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node1.json index 32ed54452de..a35e8aa638b 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node1.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node1.json @@ -26,7 +26,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":2.0, "memoryGb":8.0, "diskGb":50.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node10.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node10.json index 624976414da..5d98c75f346 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node10.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node10.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":2.0, "memoryGb":8.0, "diskGb":50.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "vespaVersion": "5.104.142", diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node13.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node13.json index f3feaea4d3a..477a87124e7 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node13.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node13.json @@ -26,7 +26,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":10.0, "memoryGb":48.0, "diskGb":500.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node14.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node14.json index cd71055c3e2..fd533b6372c 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node14.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node14.json @@ -26,7 +26,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":10.0, "memoryGb":48.0, "diskGb":500.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node2.json index f544436159c..e288aaccc28 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node2.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node2.json @@ -26,7 +26,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":2.0, "memoryGb":8.0, "diskGb":50.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-after-changes.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-after-changes.json index 7298f4737fe..6f25254b420 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-after-changes.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-after-changes.json @@ -28,7 +28,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/my/image:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":1.0, "memoryGb":4.0, "diskGb":100.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": true, "orchestratorStatus": "ALLOWED_TO_BE_DOWN", "suspendedSinceMillis": 0, "rebootGeneration": 2, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-with-hostnames.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-with-hostnames.json index 603b7f22bb3..165e2480340 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-with-hostnames.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-with-hostnames.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":1.0, "memoryGb":4.0, "diskGb":100.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "vespaVersion": "6.41.0", diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4.json index ba9e58bdcba..ceb9d88763f 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4.json @@ -27,7 +27,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":1.0, "memoryGb":4.0, "diskGb":100.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "vespaVersion": "6.41.0", diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6.json index f4dbf657926..1e64c8a8edd 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node6.json @@ -26,7 +26,6 @@ "wantedDockerImage": "docker-registry.domain.tld:8080/dist/vespa:6.42.0", "wantedVespaVersion": "6.42.0", "requestedResources": { "vcpu":2.0, "memoryGb":8.0, "diskGb":50.0, "bandwidthGbps":1.0, "diskSpeed":"fast", "storageType":"any" }, - "allowedToBeDown": false, "rebootGeneration": 0, "currentRebootGeneration": 0, "failCount": 0, @@ -150,4 +150,9 @@ <module>zookeeper-server</module> </modules> + <properties> + <maven.deploy.skip>true</maven.deploy.skip> + <maven.javadoc.skip>true</maven.javadoc.skip> + </properties> + </project> |