diff options
58 files changed, 442 insertions, 246 deletions
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json index ada3119f5bb..0c061dd8222 100644 --- a/config-model-api/abi-spec.json +++ b/config-model-api/abi-spec.json @@ -884,7 +884,8 @@ "public boolean useNewAthenzFilter()", "public boolean usePhraseSegmenting()", "public java.lang.String proxyProtocol()", - "public java.util.Optional athenzDomain()" + "public java.util.Optional athenzDomain()", + "public boolean useDedicatedNodesWhenUnspecified()" ], "fields": [] }, 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 7fcde1b5e6b..b9ada59cb0b 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 @@ -67,6 +67,7 @@ public interface ModelContext { default boolean usePhraseSegmenting() { return false; } default String proxyProtocol() { return "https-only"; } default Optional<AthenzDomain> athenzDomain() { return Optional.empty(); } + default boolean useDedicatedNodesWhenUnspecified() { return false; } } } diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java index e49ceffabc1..802fdcc1dda 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java @@ -44,6 +44,7 @@ public class TestProperties implements ModelContext.Properties { private Optional<EndpointCertificateSecrets> endpointCertificateSecrets = Optional.empty(); private boolean useNewAthenzFilter = false; private boolean usePhraseSegmenting = false; + private boolean useDedicatedNodesWhenUnspecified = false; private AthenzDomain athenzDomain; @Override public boolean multitenant() { return multitenant; } @@ -66,6 +67,7 @@ public class TestProperties implements ModelContext.Properties { @Override public boolean useBucketSpaceMetric() { return true; } @Override public boolean useNewAthenzFilter() { return useNewAthenzFilter; } @Override public boolean usePhraseSegmenting() { return usePhraseSegmenting; } + @Override public boolean useDedicatedNodesWhenUnspecified() { return useDedicatedNodesWhenUnspecified; } @Override public Optional<AthenzDomain> athenzDomain() { return Optional.ofNullable(athenzDomain); } public TestProperties setDefaultTermwiseLimit(double limit) { @@ -123,6 +125,11 @@ public class TestProperties implements ModelContext.Properties { return this; } + public TestProperties setUseDedicatedNodesWhenUnspecified(boolean useDedicatedNodesWhenUnspecified) { + this.useDedicatedNodesWhenUnspecified = useDedicatedNodesWhenUnspecified; + return this; + } + public TestProperties setAthenzDomain(AthenzDomain domain) { this.athenzDomain = domain; return this; 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 165404ce9d4..1b2df3d47e3 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 @@ -570,12 +570,8 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private void addNodesFromXml(ApplicationContainerCluster cluster, Element containerElement, ConfigModelContext context) { Element nodesElement = XML.getChild(containerElement, "nodes"); - if (nodesElement == null) { // default single node on localhost - ApplicationContainer node = new ApplicationContainer(cluster, "container.0", 0, cluster.isHostedVespa()); - HostResource host = allocateSingleNodeHost(cluster, log, containerElement, context); - node.setHostResource(host); - node.initService(context.getDeployLogger()); - cluster.addContainers(Collections.singleton(node)); + if (nodesElement == null) { + cluster.addContainers(allocateWithoutNodesTag(cluster, containerElement, context)); } else { List<ApplicationContainer> nodes = createNodes(cluster, nodesElement, context); @@ -652,29 +648,53 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } } - /** Creates a single host when there is no nodes tag */ - private HostResource allocateSingleNodeHost(ApplicationContainerCluster cluster, DeployLogger logger, Element containerElement, ConfigModelContext context) { + /** Allocate a container cluster without a nodes tag */ + private List<ApplicationContainer> allocateWithoutNodesTag(ApplicationContainerCluster cluster, Element containerElement, ConfigModelContext context) { DeployState deployState = context.getDeployState(); HostSystem hostSystem = cluster.hostSystem(); if (deployState.isHosted()) { - Optional<HostResource> singleContentHost = getHostResourceFromContentClusters(cluster, containerElement, context); - if (singleContentHost.isPresent()) { // there is a content cluster; put the container on its first node - return singleContentHost.get(); + // TODO(mpolden): The old way of allocating. Remove when 7.198 is the oldest model in production and the + // feature flag is set to true in all zones. + if (!context.properties().useDedicatedNodesWhenUnspecified()) { + Optional<HostResource> singleContentHost = getHostResourceFromContentClusters(cluster, containerElement, context); + if (singleContentHost.isPresent()) { // there is a content cluster; put the container on its first node + return singleHostContainerCluster(cluster, singleContentHost.get(), context); + } + else { // request 1 node + ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from(cluster.getName())) + .vespaVersion(deployState.getWantedNodeVespaVersion()) + .dockerImageRepo(deployState.getWantedDockerImageRepo()) + .build(); + Capacity capacity = Capacity.fromCount(1, + Optional.empty(), + false, + ! deployState.getProperties().isBootstrap()); + HostResource host = hostSystem.allocateHosts(clusterSpec, capacity, 1, log).keySet().iterator().next(); + return singleHostContainerCluster(cluster, host, context); + } } - else { // request 1 node - ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from(cluster.getName())) - .vespaVersion(deployState.getWantedNodeVespaVersion()) - .dockerImageRepo(deployState.getWantedDockerImageRepo()) - .build(); - Capacity capacity = Capacity.fromCount(1, - Optional.empty(), - false, - ! deployState.getProperties().isBootstrap()); - return hostSystem.allocateHosts(clusterSpec, capacity, 1, logger).keySet().iterator().next(); - } - } else { - return hostSystem.getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC); - } + // request just enough nodes to satisfy environment capacity requirement + ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container, + ClusterSpec.Id.from(cluster.getName())) + .vespaVersion(deployState.getWantedNodeVespaVersion()) + .dockerImageRepo(deployState.getWantedDockerImageRepo()) + .build(); + int nodeCount = deployState.zone().environment().isProduction() ? 2 : 1; + Capacity capacity = Capacity.fromCount(nodeCount, + Optional.empty(), + false, + !deployState.getProperties().isBootstrap()); + var hosts = hostSystem.allocateHosts(clusterSpec, capacity, 1, log); + return createNodesFromHosts(log, hosts, cluster); + } + return singleHostContainerCluster(cluster, hostSystem.getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC), context); + } + + private List<ApplicationContainer> singleHostContainerCluster(ApplicationContainerCluster cluster, HostResource host, ConfigModelContext context) { + ApplicationContainer node = new ApplicationContainer(cluster, "container.0", 0, cluster.isHostedVespa()); + node.setHostResource(host); + node.initService(context.getDeployLogger()); + return List.of(node); } private List<ApplicationContainer> createNodesFromNodeCount(ApplicationContainerCluster cluster, Element nodesElement, ConfigModelContext context) { diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java index d215fdbb7a0..1670ac23ba4 100644 --- a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java +++ b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java @@ -1398,6 +1398,31 @@ public class ModelProvisioningTest { } @Test + public void testNoNodeTagMeansTwoNodesInContainerClusterWithFeatureFlag() { + String services = + "<?xml version='1.0' encoding='utf-8' ?>\n" + + "<services>" + + " <container id='foo' version='1.0'>" + + " <search/>" + + " <document-api/>" + + " </container>" + + " <content version='1.0' id='bar'>" + + " <documents>" + + " <document type='type1' mode='index'/>" + + " </documents>" + + " </content>" + + "</services>"; + VespaModelTester tester = new VespaModelTester(); + tester.setUseDedicatedNodesWhenUnspecified(true); + tester.addHosts(3); + VespaModel model = tester.createModel(services, true); + assertEquals(3, model.getRoot().hostSystem().getHosts().size()); + assertEquals(2, model.getAdmin().getSlobroks().size()); + assertEquals(2, model.getContainerClusters().get("foo").getContainers().size()); + assertEquals(1, model.getContentClusters().get("bar").getRootGroup().countNodes()); + } + + @Test public void testNoNodeTagMeans1NodeNoContent() { String services = "<?xml version='1.0' encoding='utf-8' ?>\n" + diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java index b6180ab78b9..fd837c6dea3 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/test/VespaModelTester.java @@ -48,6 +48,7 @@ public class VespaModelTester { private Map<NodeResources, Collection<Host>> hostsByResources = new HashMap<>(); private ApplicationId applicationId = ApplicationId.defaultId(); private boolean useDedicatedNodeForLogserver = false; + private boolean useDedicatedNodesWhenUnspecified = false; public VespaModelTester() { this(new NullConfigModelRegistry()); @@ -97,6 +98,10 @@ public class VespaModelTester { this.useDedicatedNodeForLogserver = useDedicatedNodeForLogserver; } + public void setUseDedicatedNodesWhenUnspecified(boolean useDedicatedNodesWhenUnspecified) { + this.useDedicatedNodesWhenUnspecified = useDedicatedNodesWhenUnspecified; + } + /** Creates a model which uses 0 as start index and fails on out of capacity */ public VespaModel createModel(String services, String ... retiredHostNames) { return createModel(Zone.defaultZone(), services, true, retiredHostNames); @@ -137,7 +142,8 @@ public class VespaModelTester { .setMultitenant(true) .setHostedVespa(hosted) .setApplicationId(applicationId) - .setUseDedicatedNodeForLogserver(useDedicatedNodeForLogserver); + .setUseDedicatedNodeForLogserver(useDedicatedNodeForLogserver) + .setUseDedicatedNodesWhenUnspecified(useDedicatedNodesWhenUnspecified); DeployState deployState = new DeployState.Builder() .applicationPackage(appPkg) diff --git a/config-provisioning/abi-spec.json b/config-provisioning/abi-spec.json index 46ece153986..853fe4b11ed 100644 --- a/config-provisioning/abi-spec.json +++ b/config-provisioning/abi-spec.json @@ -288,6 +288,7 @@ "public com.yahoo.config.provision.ClusterSpec$Type type()", "public com.yahoo.config.provision.ClusterSpec$Id id()", "public java.util.Optional dockerImageRepo()", + "public java.util.Optional dockerImage()", "public com.yahoo.component.Version vespaVersion()", "public java.util.Optional group()", "public java.util.Optional combinedId()", diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java index 21d6a226b18..66a2ff411fe 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java @@ -48,6 +48,9 @@ public final class ClusterSpec { /** Returns the docker image repository part of a docker image we want this cluster to run */ public Optional<String> dockerImageRepo() { return dockerImageRepo; } + /** Returns the docker image (repository + vespa version) we want this cluster to run */ + public Optional<String> dockerImage() { return dockerImageRepo.map(repo -> repo + ":" + vespaVersion.toFullString()); } + /** Returns the version of Vespa that we want this cluster to run */ public Version vespaVersion() { return vespaVersion; } diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java index cd3d08b072a..6399352a6ec 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java @@ -191,10 +191,11 @@ public class NodeResources { @Override public String toString() { - return "[vcpu: " + vcpu + ", memory: " + memoryGb + " Gb, disk " + diskGb + " Gb" + - (bandwidthGbps > 0 ? ", bandwidth: " + bandwidthGbps + " Gbps" : "") + - ( ! diskSpeed.isDefault() ? ", disk speed: " + diskSpeed : "") + - ( ! storageType.isDefault() ? ", storage type: " + storageType : "") + "]"; + return String.format("[vcpu: %1$.1f, memory: %2$.1f Gb, disk %3$.1f Gb" + + (bandwidthGbps > 0 ? ", bandwidth: %4$.1f Gbps" : "") + + ( ! diskSpeed.isDefault() ? ", disk speed: " + diskSpeed : "") + + ( ! storageType.isDefault() ? ", storage type: " + storageType : "") + "]", + vcpu, memoryGb, diskGb, bandwidthGbps); } /** Returns true if all the resources of this are the same or larger than the given resources */ diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java index 779dd1d24f7..cee15cd6571 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java @@ -82,10 +82,12 @@ public class AllocatedHostsSerializer { private static void toSlime(HostSpec host, Cursor object) { object.setString(hostSpecHostNameKey, host.hostname()); aliasesToSlime(host, object); - // TODO serialize dockerImageRepo host.membership().ifPresent(membership -> { object.setString(hostSpecMembershipKey, membership.stringValue()); object.setString(hostSpecVespaVersionKey, membership.cluster().vespaVersion().toFullString()); + membership.cluster().dockerImageRepo().ifPresent(repo -> { + object.setString(hostSpecDockerImageRepoKey, repo); + }); }); host.flavor().ifPresent(flavor -> toSlime(flavor, object)); host.requestedResources().ifPresent(resources -> toSlime(resources, object.setObject(requestedResourcesKey))); @@ -123,7 +125,9 @@ public class AllocatedHostsSerializer { public static AllocatedHosts fromSlime(Inspector inspector, Optional<NodeFlavors> nodeFlavors) { Inspector array = inspector.field(mappingKey); Set<HostSpec> hosts = new LinkedHashSet<>(); - array.traverse((ArrayTraverser)(i, host) -> hosts.add(hostFromSlime(host.field(hostSpecKey), nodeFlavors))); + array.traverse((ArrayTraverser)(i, host) -> { + hosts.add(hostFromSlime(host.field(hostSpecKey), nodeFlavors)); + }); return AllocatedHosts.withHosts(hosts); } @@ -134,7 +138,8 @@ public class AllocatedHostsSerializer { object.field(hostSpecMembershipKey).valid() ? Optional.of(membershipFromSlime(object)) : Optional.empty(), optionalString(object.field(hostSpecCurrentVespaVersionKey)).map(com.yahoo.component.Version::new), NetworkPortsSerializer.fromSlime(object.field(hostSpecNetworkPortsKey)), - nodeResourcesFromSlime(object.field(requestedResourcesKey))); + nodeResourcesFromSlime(object.field(requestedResourcesKey)), + optionalString(object.field(hostSpecDockerImageRepoKey))); } private static List<String> aliasesFromSlime(Inspector object) { diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/NodeResourcesTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/NodeResourcesTest.java new file mode 100644 index 00000000000..21e1afeed17 --- /dev/null +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/NodeResourcesTest.java @@ -0,0 +1,21 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.provision; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author bratseth + */ +public class NodeResourcesTest { + + @Test + public void testToString() { + assertEquals("[vcpu: 1.0, memory: 10.0 Gb, disk 100.0 Gb]", + new NodeResources(1., 10., 100., 0).toString()); + assertEquals("[vcpu: 0.3, memory: 3.3 Gb, disk 33.3 Gb, bandwidth: 0.3 Gbps]", + new NodeResources(1/3., 10/3., 100/3., 0.3).toString()); + } + +} diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java index 137e1478073..f41b36b34ca 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java @@ -10,6 +10,7 @@ import com.yahoo.config.provision.NetworkPorts; import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provisioning.FlavorsConfig; +import com.yahoo.text.Utf8; import org.junit.Test; import java.io.IOException; @@ -37,8 +38,12 @@ public class AllocatedHostsSerializerTest { hosts.add(new HostSpec("with-aliases", List.of("alias1", "alias2"))); hosts.add(new HostSpec("allocated", + List.of(), + Optional.empty(), Optional.of(ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), - Optional.of("docker.foo.com:4443/vespa/bar"))))); + Optional.of("docker.foo.com:4443/vespa/bar"))), + Optional.empty(), Optional.empty(), Optional.empty(), + Optional.of("docker.foo.com:4443/vespa/bar"))); hosts.add(new HostSpec("flavor-from-resources-1", Collections.emptyList(), new Flavor(new NodeResources(0.5, 3.1, 4, 1)))); hosts.add(new HostSpec("flavor-from-resources-2", 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 930bdaadcea..a292ea65d9d 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 @@ -146,6 +146,7 @@ public class ModelContextImpl implements ModelContext { private final boolean usePhraseSegmenting; private final String proxyProtocol; private final Optional<AthenzDomain> athenzDomain; + private final boolean useDedicatedNodesWhenUnspecified; public Properties(ApplicationId applicationId, boolean multitenantFromConfig, @@ -186,6 +187,8 @@ public class ModelContextImpl implements ModelContext { this.proxyProtocol = Flags.PROXY_PROTOCOL.bindTo(flagSource) .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); this.athenzDomain = athenzDomain; + this.useDedicatedNodesWhenUnspecified = Flags.DEDICATED_NODES_WHEN_UNSPECIFIED.bindTo(flagSource) + .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); } @Override @@ -251,6 +254,12 @@ public class ModelContextImpl implements ModelContext { @Override public Optional<AthenzDomain> athenzDomain() { return athenzDomain; } + + @Override + public boolean useDedicatedNodesWhenUnspecified() { + return useDedicatedNodesWhenUnspecified; + } + } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java index d7dbdecf41b..596e19bfe65 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java @@ -13,6 +13,7 @@ import com.yahoo.config.provision.AthenzService; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.log.LogLevel; @@ -91,6 +92,7 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.stream.Collectors.joining; @@ -117,22 +119,16 @@ public class InternalStepRunner implements StepRunner { static final NodeResources DEFAULT_TESTER_RESOURCES_AWS = new NodeResources(2, 8, 50, 0.3, NodeResources.DiskSpeed.any); - static final Duration capacityTimeout = Duration.ofMinutes(5); - static final Duration endpointTimeout = Duration.ofMinutes(15); - static final Duration endpointCertificateTimeout = Duration.ofMinutes(15); - static final Duration testerTimeout = Duration.ofMinutes(30); - static final Duration nodesDownTimeout = Duration.ofMinutes(60); - static final Duration noNodesDownTimeout = Duration.ofMinutes(120); - static final Duration certificateTimeout = Duration.ofMinutes(300); - private final Controller controller; private final TestConfigSerializer testConfigSerializer; private final DeploymentFailureMails mails; + private final Timeouts timeouts; public InternalStepRunner(Controller controller) { this.controller = controller; this.testConfigSerializer = new TestConfigSerializer(controller.system()); this.mails = new DeploymentFailureMails(controller.zoneRegistry()); + this.timeouts = Timeouts.of(controller.system()); } @Override @@ -263,8 +259,8 @@ public class InternalStepRunner implements StepRunner { ? Optional.of(deploymentFailed) : Optional.empty(); switch (e.getErrorCode()) { case CERTIFICATE_NOT_READY: - if (startTime.plus(endpointCertificateTimeout).isBefore(controller.clock().instant())) { - logger.log("Deployment failed to find provisioned endpoint certificate after " + endpointCertificateTimeout); + if (startTime.plus(timeouts.endpointCertificate()).isBefore(controller.clock().instant())) { + logger.log("Deployment failed to find provisioned endpoint certificate after " + timeouts.endpointCertificate()); return Optional.of(RunStatus.endpointCertificateTimeout); } return result; @@ -279,7 +275,7 @@ public class InternalStepRunner implements StepRunner { return result; case OUT_OF_CAPACITY: logger.log(e.getServerMessage()); - return controller.system().isCd() && startTime.plus(capacityTimeout).isAfter(controller.clock().instant()) + return controller.system().isCd() && startTime.plus(timeouts.capacity()).isAfter(controller.clock().instant()) ? Optional.empty() : Optional.of(outOfCapacity); case INVALID_APPLICATION_PACKAGE: @@ -294,8 +290,8 @@ public class InternalStepRunner implements StepRunner { switch (e.type()) { case CERT_NOT_AVAILABLE: // Same as CERTIFICATE_NOT_READY above, only from the controller - if (startTime.plus(endpointCertificateTimeout).isBefore(controller.clock().instant())) { - logger.log("Deployment failed to find provisioned endpoint certificate after " + endpointCertificateTimeout); + if (startTime.plus(timeouts.endpointCertificate()).isBefore(controller.clock().instant())) { + logger.log("Deployment failed to find provisioned endpoint certificate after " + timeouts.endpointCertificate()); return Optional.of(RunStatus.endpointCertificateTimeout); } return Optional.empty(); @@ -352,25 +348,25 @@ public class InternalStepRunner implements StepRunner { return Optional.of(running); } } - else if (timedOut(id, deployment.get(), endpointTimeout)) { - logger.log(WARNING, "Endpoints failed to show up within " + endpointTimeout.toMinutes() + " minutes!"); + else if (timedOut(id, deployment.get(), timeouts.endpoint())) { + logger.log(WARNING, "Endpoints failed to show up within " + timeouts.endpoint().toMinutes() + " minutes!"); return Optional.of(error); } } String failureReason = null; - NodeList suspendedTooLong = nodeList.suspendedSince(controller.clock().instant().minus(nodesDownTimeout)); + NodeList suspendedTooLong = nodeList.suspendedSince(controller.clock().instant().minus(timeouts.nodesDown())); if ( ! suspendedTooLong.isEmpty()) { - failureReason = "Some nodes have been suspended for more than " + nodesDownTimeout.toMinutes() + " minutes:\n" + + failureReason = "Some nodes have been suspended for more than " + timeouts.nodesDown().toMinutes() + " minutes:\n" + suspendedTooLong.asList().stream().map(node -> node.node().hostname().value()).collect(joining("\n")); } if (run.noNodesDownSince() - .map(since -> since.isBefore(controller.clock().instant().minus(noNodesDownTimeout))) + .map(since -> since.isBefore(controller.clock().instant().minus(timeouts.noNodesDown()))) .orElse(false)) { if (summary.needPlatformUpgrade() > 0 || summary.needReboot() > 0 || summary.needRestart() > 0) - failureReason = "No nodes allowed to suspend to progress installation for " + noNodesDownTimeout.toMinutes() + " minutes."; + failureReason = "No nodes allowed to suspend to progress installation for " + timeouts.noNodesDown().toMinutes() + " minutes."; else failureReason = "Nodes not able to start with new application package."; } @@ -442,8 +438,8 @@ public class InternalStepRunner implements StepRunner { return Optional.of(running); } - if (run.stepInfo(installTester).get().startTime().get().plus(testerTimeout).isBefore(controller.clock().instant())) { - logger.log(WARNING, "Installation of tester failed to complete within " + testerTimeout.toMinutes() + " minutes!"); + if (run.stepInfo(installTester).get().startTime().get().plus(timeouts.tester()).isBefore(controller.clock().instant())) { + logger.log(WARNING, "Installation of tester failed to complete within " + timeouts.tester().toMinutes() + " minutes!"); return Optional.of(error); } @@ -807,7 +803,7 @@ public class InternalStepRunner implements StepRunner { X509Certificate certificate = X509CertificateBuilder.fromKeypair(keyPair, subject, controller.clock().instant(), - controller.clock().instant().plus(certificateTimeout), + controller.clock().instant().plus(timeouts.testerCertificate()), SignatureAlgorithm.SHA512_WITH_RSA, BigInteger.valueOf(1)) .build(); @@ -928,4 +924,27 @@ public class InternalStepRunner implements StepRunner { } + + static class Timeouts { + + private final SystemName system; + + private Timeouts(SystemName system) { + this.system = requireNonNull(system); + } + + public static Timeouts of(SystemName system) { + return new Timeouts(system); + } + + Duration capacity() { return Duration.ofMinutes(system.isCd() ? 5 : 0); } + Duration endpoint() { return Duration.ofMinutes(15); } + Duration endpointCertificate() { return Duration.ofMinutes(15); } + Duration tester() { return Duration.ofMinutes(30); } + Duration nodesDown() { return Duration.ofMinutes(system.isCd() ? 30 : 60); } + Duration noNodesDown() { return Duration.ofMinutes(system.isCd() ? 30 : 120); } + Duration testerCertificate() { return Duration.ofMinutes(300); } + + } + } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java index 1b2928c57b1..7298d84b1b3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java @@ -120,7 +120,8 @@ public class VersionStatus { var deploymentStatistics = computeDeploymentStatistics(infrastructureVersions.keySet(), - controller.jobController().deploymentStatuses(ApplicationList.from(controller.applications().asList()))); + controller.jobController().deploymentStatuses(ApplicationList.from(controller.applications().asList()) + .withProjectId())); List<VespaVersion> versions = new ArrayList<>(); List<Version> releasedVersions = controller.mavenRepository().metadata().versions(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java index 706a7cec2a9..a04dc6fb579 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java @@ -367,7 +367,7 @@ public class DeploymentContext { triggerJobs(); RunId id = currentRun(job).id(); doDeploy(job); - tester.clock().advance(InternalStepRunner.noNodesDownTimeout.plusSeconds(1)); + tester.clock().advance(InternalStepRunner.Timeouts.of(tester.controller().system()).noNodesDown().plusSeconds(1)); runner.advance(currentRun(job)); assertTrue(jobs.run(id).get().hasFailed()); assertTrue(jobs.run(id).get().hasEnded()); @@ -381,7 +381,7 @@ public class DeploymentContext { RunId id = currentRun(job).id(); doDeploy(job); doUpgrade(job); - tester.clock().advance(InternalStepRunner.noNodesDownTimeout.plusSeconds(1)); + tester.clock().advance(InternalStepRunner.Timeouts.of(tester.controller().system()).noNodesDown().plusSeconds(1)); runner.advance(currentRun(job)); assertTrue(jobs.run(id).get().hasFailed()); assertTrue(jobs.run(id).get().hasEnded()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java index 21b6e729e41..0e949419792 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java @@ -162,7 +162,7 @@ public class InternalStepRunnerTest { tester.runner().run(); assertEquals(unfinished, tester.jobs().run(id).get().stepStatuses().get(Step.installReal)); - tester.clock().advance(InternalStepRunner.noNodesDownTimeout.plus(Duration.ofSeconds(1))); + tester.clock().advance(InternalStepRunner.Timeouts.of(system()).noNodesDown().plus(Duration.ofSeconds(1))); tester.runner().run(); assertEquals(installationFailed, tester.jobs().run(id).get().status()); } @@ -190,7 +190,7 @@ public class InternalStepRunnerTest { tester.configServer().convergeServices(app.testerId().id(), JobType.stagingTest.zone(system())); tester.runner().run(); - tester.clock().advance(InternalStepRunner.endpointTimeout.plus(Duration.ofSeconds(1))); + tester.clock().advance(InternalStepRunner.Timeouts.of(system()).endpoint().plus(Duration.ofSeconds(1))); tester.runner().run(); assertEquals(failed, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal)); } @@ -214,7 +214,7 @@ public class InternalStepRunnerTest { Node systemTestNode = tester.configServer().nodeRepository().list(JobType.systemTest.zone(system()), app.instanceId()).iterator().next(); - tester.clock().advance(InternalStepRunner.noNodesDownTimeout.minus(Duration.ofSeconds(1))); + tester.clock().advance(InternalStepRunner.Timeouts.of(system()).noNodesDown().minus(Duration.ofSeconds(1))); tester.configServer().nodeRepository().putByHostname(JobType.systemTest.zone(system()), new Node.Builder(systemTestNode) .serviceState(Node.ServiceState.allowedDown) @@ -229,7 +229,7 @@ public class InternalStepRunnerTest { assertEquals(unfinished, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal)); assertEquals(failed, tester.jobs().last(app.instanceId(), JobType.stagingTest).get().stepStatuses().get(Step.installInitialReal)); - tester.clock().advance(InternalStepRunner.nodesDownTimeout.minus(Duration.ofSeconds(3))); + tester.clock().advance(InternalStepRunner.Timeouts.of(system()).nodesDown().minus(Duration.ofSeconds(3))); tester.runner().run(); assertEquals(unfinished, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal)); @@ -439,7 +439,7 @@ public class InternalStepRunnerTest { trusted.add(tester.jobs().run(id).get().testerCertificate().get()); assertEquals(trusted, tester.configServer().application(app.instanceId(), id.type().zone(system())).get().applicationPackage().trustedCertificates()); - tester.clock().advance(InternalStepRunner.certificateTimeout.plus(Duration.ofSeconds(1))); + tester.clock().advance(InternalStepRunner.Timeouts.of(system()).testerCertificate().plus(Duration.ofSeconds(1))); tester.runner().run(); assertEquals(RunStatus.aborted, tester.jobs().run(id).get().status()); } diff --git a/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp b/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp index a2e8219e916..01b8515400b 100644 --- a/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp +++ b/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp @@ -23,9 +23,9 @@ const mbus::string DocumentProtocol::NAME = "document"; DocumentProtocol::DocumentProtocol(const LoadTypeSet& loadTypes, std::shared_ptr<const DocumentTypeRepo> repo, const string &configId) : - _routingPolicyRepository(new RoutingPolicyRepository()), - _routableRepository(new RoutableRepository(loadTypes)), - _repo(repo) + _routingPolicyRepository(std::make_unique<RoutingPolicyRepository>()), + _routableRepository(std::make_unique<RoutableRepository>(loadTypes)), + _repo(std::move(repo)) { // Prepare config string for routing policy factories. string cfg = (configId.empty() ? "client" : configId); @@ -148,10 +148,8 @@ DocumentProtocol & DocumentProtocol::putRoutableFactory(uint32_t type, IRoutableFactory::SP factory, const std::vector<vespalib::VersionSpecification> &versions) { - for (std::vector<vespalib::VersionSpecification>::const_iterator it = versions.begin(); - it != versions.end(); ++it) - { - putRoutableFactory(type, factory, *it); + for (const auto & version : versions) { + putRoutableFactory(type, factory, version); } return *this; } diff --git a/documentapi/src/vespa/documentapi/messagebus/documentprotocol.h b/documentapi/src/vespa/documentapi/messagebus/documentprotocol.h index 5582c0ea153..eeae4553b3b 100644 --- a/documentapi/src/vespa/documentapi/messagebus/documentprotocol.h +++ b/documentapi/src/vespa/documentapi/messagebus/documentprotocol.h @@ -197,7 +197,7 @@ public: DocumentProtocol(const LoadTypeSet& loadTypes, std::shared_ptr<const document::DocumentTypeRepo> repo, const string &configId = ""); - ~DocumentProtocol(); + ~DocumentProtocol() override; /** * Adds a new routable factory to this protocol. This method is thread-safe, and may be invoked on a diff --git a/eval/src/tests/ann/nns-l2.h b/eval/src/tests/ann/nns-l2.h index 857866ff73b..82a95741200 100644 --- a/eval/src/tests/ann/nns-l2.h +++ b/eval/src/tests/ann/nns-l2.h @@ -1,7 +1,7 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once -#include <string.h> +#include <cstring> #include <vespa/vespalib/util/arrayref.h> #include <vespa/vespalib/hwaccelrated/iaccelrated.h> @@ -34,7 +34,7 @@ static double hw_l2_sq_dist(const T * af, const T * bf, size_t sz) template <typename FltType = float> struct L2DistCalc { - vespalib::hwaccelrated::IAccelrated::UP _hw; + const vespalib::hwaccelrated::IAccelrated & _hw; L2DistCalc() : _hw(vespalib::hwaccelrated::IAccelrated::getAccelrator()) {} @@ -42,16 +42,16 @@ struct L2DistCalc { using ConstArr = vespalib::ConstArrayRef<FltType>; double product(const FltType *v1, const FltType *v2, size_t sz) { - return _hw->dotProduct(v1, v2, sz); + return _hw.dotProduct(v1, v2, sz); } double product(ConstArr v1, ConstArr v2) { const FltType *p1 = v1.begin(); const FltType *p2 = v2.begin(); - return _hw->dotProduct(p1, p2, v1.size()); + return _hw.dotProduct(p1, p2, v1.size()); } double l2sq(ConstArr vector) { const FltType *v = vector.begin(); - return _hw->dotProduct(v, v, vector.size()); + return _hw.dotProduct(v, v, vector.size()); } double l2sq_dist(ConstArr v1, ConstArr v2, Arr tmp) { for (size_t i = 0; i < v1.size(); ++i) { diff --git a/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.cpp b/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.cpp index 4a28b54d201..c47521e702d 100644 --- a/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.cpp +++ b/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.cpp @@ -43,18 +43,6 @@ DirectSparseTensorBuilder::build() { return std::make_unique<SparseTensor>(std::move(_type), std::move(_cells), std::move(_stash)); } -void -DirectSparseTensorBuilder::insertCell(SparseTensorAddressRef address, double value) { - // This address should not already exist and a new cell should be inserted. - insertCell(address, value, [](double, double) -> double { HDR_ABORT("should not be reached"); }); -} - -void -DirectSparseTensorBuilder::insertCell(SparseTensorAddressBuilder &address, double value) { - // This address should not already exist and a new cell should be inserted. - insertCell(address.getAddressRef(), value, [](double, double) -> double { HDR_ABORT("should not be reached"); }); -} - void DirectSparseTensorBuilder::reserve(uint32_t estimatedCells) { _cells.resize(estimatedCells*2); } diff --git a/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h b/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h index f9182a199be..bcb22c0761d 100644 --- a/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h +++ b/eval/src/vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h @@ -45,15 +45,20 @@ public: } } - void insertCell(SparseTensorAddressRef address, double value); + void insertCell(SparseTensorAddressRef address, double value) { + // This address should not already exist and a new cell should be inserted. + insertCell(address, value, [](double, double) -> double { HDR_ABORT("should not be reached"); }); + } template <class Function> - void insertCell(SparseTensorAddressBuilder &address, double value, Function &&func) - { + void insertCell(SparseTensorAddressBuilder &address, double value, Function &&func) { insertCell(address.getAddressRef(), value, func); } - void insertCell(SparseTensorAddressBuilder &address, double value); + void insertCell(SparseTensorAddressBuilder &address, double value) { + // This address should not already exist and a new cell should be inserted. + insertCell(address.getAddressRef(), value, [](double, double) -> double { HDR_ABORT("should not be reached"); }); + } eval::ValueType &fast_type() { return _type; } Cells &cells() { return _cells; } 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 14ac9ecd678..b549fb74db4 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -56,7 +56,7 @@ public class Flags { HOSTNAME); public static final UnboundBooleanFlag ENABLE_FLEET_SSHD_CONFIG = defineFeatureFlag( - "enable-fleet-sshd-config", false, + "enable-fleet-sshd-config", true, "Whether fleet should manage the /etc/ssh/sshd_config file.", "Takes effect on next host admin tick.", HOSTNAME); @@ -250,7 +250,7 @@ public class Flags { "Takes effect on next deployment of the application", APPLICATION_ID); public static final UnboundBooleanFlag PHRASE_SEGMENTING = defineFeatureFlag( - "phrase-segmenting", true, + "phrase-segmenting", false, "Should 'implicit phrases' in queries we parsed to a phrase or and?", "Takes effect on redeploy", ZONE_ID, APPLICATION_ID); @@ -273,6 +273,12 @@ public class Flags { "Takes effect immediately", APPLICATION_ID); + public static final UnboundBooleanFlag DEDICATED_NODES_WHEN_UNSPECIFIED = defineFeatureFlag( + "dedicated-nodes-when-unspecified", false, + "Whether config-server should allocate dedicated container nodes when <nodes/> is not specified in services.xml", + "Takes effect on redeploy", + APPLICATION_ID); + /** WARNING: public for testing: All flags should be defined in {@link Flags}. */ public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, String description, String modificationEffect, FetchVector.Dimension... dimensions) { diff --git a/messagebus/src/vespa/messagebus/messagebus.cpp b/messagebus/src/vespa/messagebus/messagebus.cpp index d277063a273..fee9504007b 100644 --- a/messagebus/src/vespa/messagebus/messagebus.cpp +++ b/messagebus/src/vespa/messagebus/messagebus.cpp @@ -99,7 +99,7 @@ MessageBus::MessageBus(INetwork &net, ProtocolSet protocols) : MessageBusParams params; while (!protocols.empty()) { IProtocol::SP protocol = protocols.extract(); - if (protocol.get() != nullptr) { + if (protocol) { params.addProtocol(protocol); } } @@ -132,8 +132,7 @@ MessageBus::~MessageBus() bool done = false; while (!done) { vespalib::Gate gate; - Messenger::ITask::UP task(new ShutdownTask(_network, *_msn, done, gate)); - _msn->enqueue(std::move(task)); + _msn->enqueue(std::make_unique<ShutdownTask>(_network, *_msn, done, gate)); gate.await(); } } @@ -157,11 +156,10 @@ MessageBus::setup(const MessageBusParams ¶ms) // Start messenger. IRetryPolicy::SP retryPolicy = params.getRetryPolicy(); - if (retryPolicy.get() != nullptr) { - _resender.reset(new Resender(retryPolicy)); + if (retryPolicy) { + _resender = std::make_unique<Resender>(retryPolicy); - Messenger::ITask::UP task(new ResenderTask(*_resender)); - _msn->addRecurrentTask(std::move(task)); + _msn->addRecurrentTask(std::make_unique<ResenderTask>(*_resender)); } if (!_msn->start()) { throw vespalib::NetworkSetupFailureException("Failed to start messenger."); @@ -273,7 +271,7 @@ MessageBus::sync() void MessageBus::handleMessage(Message::UP msg) { - if (_resender.get() != nullptr && msg->hasBucketSequence()) { + if (_resender && msg->hasBucketSequence()) { deliverError(std::move(msg), ErrorCode::SEQUENCE_ERROR, "Bucket sequences not supported when resender is enabled."); return; @@ -292,8 +290,7 @@ MessageBus::setupRouting(const RoutingSpec &spec) LOG(info, "Protocol '%s' is not supported, ignoring routing table.", cfg.getProtocol().c_str()); continue; } - RoutingTable::SP rt(new RoutingTable(cfg)); - rtm[cfg.getProtocol()] = rt; + rtm[cfg.getProtocol()] = std::make_shared<RoutingTable>(cfg); } { LockGuard guard(_lock); @@ -383,7 +380,7 @@ MessageBus::deliverMessage(Message::UP msg, const string &session) void MessageBus::deliverError(Message::UP msg, uint32_t errCode, const string &errMsg) { - Reply::UP reply(new EmptyReply()); + auto reply = std::make_unique<EmptyReply>(); reply->swapState(*msg); reply->addError(Error(errCode, errMsg)); diff --git a/messagebus/src/vespa/messagebus/messenger.cpp b/messagebus/src/vespa/messagebus/messenger.cpp index 63df7f2f482..5313c4adcbb 100644 --- a/messagebus/src/vespa/messagebus/messenger.cpp +++ b/messagebus/src/vespa/messagebus/messenger.cpp @@ -33,7 +33,7 @@ public: } ~MessageTask() { - if (_msg.get() != nullptr) { + if (_msg) { _msg->discard(); } } @@ -43,7 +43,7 @@ public: } uint8_t priority() const override { - if (_msg.get() != nullptr) { + if (_msg) { return _msg->priority(); } @@ -65,7 +65,7 @@ public: } ~ReplyTask() { - if (_reply.get() != nullptr) { + if (_reply) { _reply->discard(); } } @@ -75,7 +75,7 @@ public: } uint8_t priority() const override { - if (_reply.get() != nullptr) { + if (_reply) { return _reply->priority(); } @@ -206,7 +206,7 @@ Messenger::Run(FastOS_ThreadInterface *thread, void *arg) _queue.pop(); } } - if (task.get() != nullptr) { + if (task) { try { task->run(); } catch (const std::exception &e) { @@ -223,16 +223,14 @@ Messenger::Run(FastOS_ThreadInterface *thread, void *arg) void Messenger::addRecurrentTask(ITask::UP task) { - ITask::UP add(new AddRecurrentTask(_children, std::move(task))); - enqueue(std::move(add)); + enqueue(std::make_unique<AddRecurrentTask>(_children, std::move(task))); } void Messenger::discardRecurrentTasks() { vespalib::Gate gate; - ITask::UP task(new DiscardRecurrentTasks(gate, _children)); - enqueue(std::move(task)); + enqueue(std::make_unique<DiscardRecurrentTasks>(gate, _children)); gate.await(); } @@ -248,13 +246,13 @@ Messenger::start() void Messenger::deliverMessage(Message::UP msg, IMessageHandler &handler) { - enqueue(ITask::UP(new MessageTask(std::move(msg), handler))); + enqueue(std::make_unique<MessageTask>(std::move(msg), handler)); } void Messenger::deliverReply(Reply::UP reply, IReplyHandler &handler) { - enqueue(ITask::UP(new ReplyTask(std::move(reply), handler))); + enqueue(std::make_unique<ReplyTask>(std::move(reply), handler)); } void @@ -273,7 +271,7 @@ void Messenger::sync() { vespalib::Gate gate; - enqueue(ITask::UP(new SyncTask(gate))); + enqueue(std::make_unique<SyncTask>(gate)); gate.await(); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java index 5a797038f94..05b171f36b0 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java @@ -609,10 +609,14 @@ public class NodeRepository extends AbstractComponent { children.forEach(child -> requireRemovable(child, true, force)); db.removeNodes(children); List<Node> removed = new ArrayList<>(children); - if (zone.cloud().value().equals("aws")) + if (zone.cloud().value().equals("aws")) { db.removeNodes(List.of(node)); - else + } + else { + node = node.withWantToRetire(false, Agent.system, clock.instant()); + node = node.with(node.status().withWantToDeprovision(false)); move(node, State.deprovisioned, Agent.system, Optional.empty()); + } removed.add(node); return removed; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java index 2bbd429fe4e..d2fa773a5a2 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java @@ -44,7 +44,7 @@ public class AllocatableClusterResources { } /** - * Returns the resources which will actually be available in this cluster with this allocation. + * Returns the resources which will actually be available per node in this cluster with this allocation. * These should be used for reasoning about allocation to meet measured demand. */ public NodeResources realResources() { return realResources; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Resource.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Resource.java index 0eac14d61ca..e84544e7e7b 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Resource.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Resource.java @@ -20,7 +20,7 @@ public enum Resource { /** Memory utilization ratio */ memory { - String metricName() { return "mem.util"; } + String metricName() { return "mem_total.util"; } double idealAverageLoad() { return 0.7; } double valueFrom(NodeResources resources) { return resources.memoryGb(); } double valueFromMetric(double metricValue) { return metricValue / 100; } // % to ratio @@ -29,7 +29,7 @@ public enum Resource { /** Disk utilization ratio */ disk { String metricName() { return "disk.util"; } - double idealAverageLoad() { return 0.7; } + double idealAverageLoad() { return 0.6; } double valueFrom(NodeResources resources) { return resources.diskGb(); } double valueFromMetric(double metricValue) { return metricValue / 100; } // % to ratio }; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java index 1a75e5c4c74..d4a237f8e23 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java @@ -67,16 +67,17 @@ public class AutoscalingMaintainer extends Maintainer { int currentGroups = (int) clusterNodes.stream().map(node -> node.allocation().get().membership().cluster().group()).distinct().count(); ClusterSpec.Type clusterType = clusterNodes.get(0).allocation().get().membership().cluster().type(); log.info("Autoscale: " + application + " " + clusterType + " " + clusterId + - " from " + toString(clusterNodes.size(), currentGroups, clusterNodes.get(0).flavor().resources()) + - " to " + toString(target.get().nodes(), target.get().groups(), target.get().advertisedResources())); + "\nfrom " + toString(clusterNodes.size(), currentGroups, clusterNodes.get(0).flavor().resources()) + + "\nto " + toString(target.get().nodes(), target.get().groups(), target.get().advertisedResources())); lastLogged.put(new Pair<>(application, clusterId), nodeRepository().clock().instant()); } private String toString(int nodes, int groups, NodeResources resources) { - return nodes + - (groups > 1 ? " in " + groups + " groups " : " ") + - " * " + resources + - " (total: " + "[vcpu: " + nodes * resources.vcpu() + ", memory: " + nodes * resources.memoryGb() + " Gb, disk " + nodes * resources.diskGb() + " Gb])"; + return String.format(nodes + (groups > 1 ? " (in " + groups + " groups)" : "") + + " * [vcpu: %1$.1f, memory: %2$.1f Gb, disk %3$.1f Gb]" + + " (total: [vcpu: %4$.1f, memory: %5$.1f Gb, disk: %6$.1f Gb])," + + resources.vcpu(), resources.memoryGb(), resources.diskGb(), + nodes * resources.vcpu(), nodes * resources.memoryGb(), nodes * resources.diskGb()); } private Map<ClusterSpec.Id, List<Node>> nodesByCluster(List<Node> applicationNodes) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java index be43c0139e0..94f4dab1245 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java @@ -187,8 +187,7 @@ public class NodeSerializer { object.setLong(currentRestartGenerationKey, allocation.restartGeneration().current()); object.setBool(removableKey, allocation.isRemovable()); object.setString(wantedVespaVersionKey, allocation.membership().cluster().vespaVersion().toString()); - // TODO serialize dockerImageRepo - //object.setString(wantedDockerImageRepoKey, allocation.membership().cluster().dockerImageRepo().orElse("")); + allocation.membership().cluster().dockerImageRepo().ifPresent(repo -> object.setString(wantedDockerImageRepoKey, repo)); allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray(networkPortsKey))); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java index 7a49ad9c44d..73061acd9c1 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java @@ -5,6 +5,7 @@ import com.google.inject.Inject; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; @@ -148,7 +149,8 @@ public class NodeRepositoryProvisioner implements Provisioner { Optional.of(nodeAllocation.membership()), node.status().vespaVersion(), nodeAllocation.networkPorts(), - requestedResources)); + requestedResources, + node.status().dockerImage().map(DockerImage::repository))); if (nodeAllocation.networkPorts().isPresent()) { log.log(LogLevel.DEBUG, () -> "Prepared node " + node.hostname() + " has port allocations"); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java index 7f283452538..248cfbec662 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java @@ -157,7 +157,8 @@ class NodesResponse extends HttpResponse { toSlime(allocation.membership(), object.setObject("membership")); object.setLong("restartGeneration", allocation.restartGeneration().wanted()); object.setLong("currentRestartGeneration", allocation.restartGeneration().current()); - object.setString("wantedDockerImage", nodeRepository.dockerImage(node).withTag(allocation.membership().cluster().vespaVersion()).asString()); + object.setString("wantedDockerImage", allocation.membership().cluster().dockerImage() + .orElseGet(() -> nodeRepository.dockerImage(node).withTag(allocation.membership().cluster().vespaVersion()).asString())); object.setString("wantedVespaVersion", allocation.membership().cluster().vespaVersion().toFullString()); toSlime(allocation.requestedResources(), object.setObject("requestedResources")); allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray("networkPorts"))); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java index 8830204547e..56b848a5cf2 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTest.java @@ -157,12 +157,23 @@ public class NodeRepositoryTest { tester.addNode("id2", "host2", "default", NodeType.host); assertFalse(tester.nodeRepository().getNode("host1").get().history().hasEventAfter(History.Event.Type.deprovisioned, testStart)); + // Set host 1 properties and remove it + Node host1 = tester.nodeRepository().getNode("host1").get(); + host1 = host1.withWantToRetire(true, Agent.system, tester.nodeRepository().clock().instant()); + host1 = host1.with(host1.status().withWantToDeprovision(true)); + tester.nodeRepository().write(host1, tester.nodeRepository().lock(host1)); tester.nodeRepository().removeRecursively("host1"); - assertEquals(Node.State.deprovisioned, tester.nodeRepository().getNode("host1").get().state()); - assertTrue(tester.nodeRepository().getNode("host1").get().history().hasEventAfter(History.Event.Type.deprovisioned, testStart)); - Node existing = tester.addNode("id1", "host1", "default", NodeType.host); - assertTrue(existing.history().hasEventAfter(History.Event.Type.deprovisioned, testStart)); + // Host 1 is deprovisioned and unwanted properties are cleared + host1 = tester.nodeRepository().getNode("host1").get(); + assertEquals(Node.State.deprovisioned, host1.state()); + assertTrue(host1.history().hasEventAfter(History.Event.Type.deprovisioned, testStart)); + assertFalse(host1.status().wantToRetire()); + assertFalse(host1.status().wantToDeprovision()); + + // Adding it again moves it from deprovisioned + host1 = tester.addNode("id1", "host1", "default", NodeType.host); + assertTrue(host1.history().hasEventAfter(History.Event.Type.deprovisioned, testStart)); } @Test diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java index 0cb97bb65d7..d154af4f025 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java @@ -71,7 +71,7 @@ public class AutoscalingIntegrationTest { " {\n" + " \"values\": {\n" + " \"cpu.util\": 16.2,\n" + - " \"mem.util\": 23.1,\n" + + " \"mem_total.util\": 23.1,\n" + " \"disk.util\": 82\n" + " },\n" + " \"dimensions\": {\n" + @@ -90,7 +90,7 @@ public class AutoscalingIntegrationTest { " {\n" + " \"values\": {\n" + " \"cpu.util\": 20,\n" + - " \"mem.util\": 23.1,\n" + + " \"mem_total.util\": 23.1,\n" + " \"disk.util\": 40\n" + " },\n" + " \"dimensions\": {\n" + diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java index da4f74bf05b..3dfebedb0e6 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java @@ -41,7 +41,7 @@ public class NodeMetricsFetcherTest { httpClient.requestsReceived.get(0)); assertEquals(5, values.size()); assertEquals("metric value cpu.util: 16.2 at 1970-01-01T00:20:34Z for host-1.yahoo.com", values.get(0).toString()); - assertEquals("metric value mem.util: 23.1 at 1970-01-01T00:20:34Z for host-1.yahoo.com", values.get(1).toString()); + assertEquals("metric value mem_total.util: 23.1 at 1970-01-01T00:20:34Z for host-1.yahoo.com", values.get(1).toString()); assertEquals("metric value disk.util: 82.0 at 1970-01-01T00:20:34Z for host-1.yahoo.com", values.get(2).toString()); assertEquals("metric value cpu.util: 20.0 at 1970-01-01T00:20:00Z for host-2.yahoo.com", values.get(3).toString()); assertEquals("metric value disk.util: 40.0 at 1970-01-01T00:20:00Z for host-2.yahoo.com", values.get(4).toString()); @@ -54,7 +54,7 @@ public class NodeMetricsFetcherTest { httpClient.requestsReceived.get(1)); assertEquals(3, values.size()); assertEquals("metric value cpu.util: 10.0 at 1970-01-01T00:21:40Z for host-3.yahoo.com", values.get(0).toString()); - assertEquals("metric value mem.util: 15.0 at 1970-01-01T00:21:40Z for host-3.yahoo.com", values.get(1).toString()); + assertEquals("metric value mem_total.util: 15.0 at 1970-01-01T00:21:40Z for host-3.yahoo.com", values.get(1).toString()); assertEquals("metric value disk.util: 20.0 at 1970-01-01T00:21:40Z for host-3.yahoo.com", values.get(2).toString()); } } @@ -87,7 +87,7 @@ public class NodeMetricsFetcherTest { " {\n" + " \"values\": {\n" + " \"cpu.util\": 16.2,\n" + - " \"mem.util\": 23.1,\n" + + " \"mem_total.util\": 23.1,\n" + " \"disk.util\": 82\n" + " },\n" + " \"dimensions\": {\n" + @@ -130,7 +130,7 @@ public class NodeMetricsFetcherTest { " {\n" + " \"values\": {\n" + " \"cpu.util\": 10,\n" + - " \"mem.util\": 15,\n" + + " \"mem_total.util\": 15,\n" + " \"disk.util\": 20\n" + " },\n" + " \"dimensions\": {\n" + diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java index 1f9d7ce5126..931d87a3265 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; @@ -157,6 +158,34 @@ public class ProvisioningTest { } @Test + public void dockerImageRepoIsReturnedIfSet() { + ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); + + tester.makeReadyNodes(4, defaultResources, NodeType.host, 1); + tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); + + // deploy + ApplicationId application1 = tester.makeApplicationId(); + SystemState state1 = prepare(application1, tester, 1, 1, 1, 1, defaultResources, "1.2.3"); + String dockerImageRepo = "docker.domain.tld/my/image"; + prepare(application1, tester, 1, 1, 1 , 1 , false, defaultResources, "1.2.3", Optional.of(dockerImageRepo)); + tester.activate(application1, state1.allHosts); + + HostSpec host1 = state1.container0.iterator().next(); + Node node1 = tester.nodeRepository().getNode(host1.hostname()).get(); + DockerImage dockerImage = DockerImage.fromString(dockerImageRepo).withTag(Version.fromString("1.2.3")); + tester.nodeRepository().write(node1.with(node1.status().withDockerImage(dockerImage)), () -> {}); + + // redeploy + SystemState state2 = prepare(application1, tester, 1, 1, 1 ,1 , false, defaultResources, "1.2.3", Optional.of(dockerImageRepo)); + tester.activate(application1, state2.allHosts); + + host1 = state2.container0.iterator().next(); + node1 = tester.nodeRepository().getNode(host1.hostname()).get(); + assertEquals(dockerImage, node1.status().dockerImage().get()); + } + + @Test public void application_deployment_variable_application_size() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); @@ -334,7 +363,21 @@ public class ProvisioningTest { tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); ApplicationId application = tester.makeApplicationId(); - SystemState state = prepare(application, 2, 2, 3, 3, defaultResources, Version.fromString("6.91"), tester); + SystemState state = prepare(application, tester, 2, 2, 3, 3, defaultResources, "6.91"); + assertEquals(4, state.allHosts.size()); + tester.activate(application, state.allHosts); + } + + @Test + public void deploy_specific_vespa_version_and_docker_image() { + ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); + + tester.makeReadyNodes(4, defaultResources, NodeType.host, 1); + tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); + + ApplicationId application = tester.makeApplicationId(); + String dockerImageRepo = "docker.domain.tld/my/image"; + SystemState state = prepare(application, tester, 2, 2, 3, 3, false, defaultResources, "6.91", Optional.of(dockerImageRepo)); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -630,7 +673,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); try { - prepare(application, 1, 0, 1, 0, true, defaultResources, Version.fromString("6.42"), tester); + prepare(application, tester, 1, 0, 1, 0, true, defaultResources, "6.42", Optional.empty()); fail("Expected exception"); } catch (IllegalArgumentException ignored) {} } @@ -652,17 +695,17 @@ public class ProvisioningTest { public void cluster_spec_update_for_already_reserved_nodes() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - Version version1 = Version.fromString("6.42"); - Version version2 = Version.fromString("6.43"); + String version1 = "6.42"; + String version2 = "6.43"; tester.makeReadyNodes(2, defaultResources); - prepare(application, 1, 0, 1, 0, true, defaultResources, version1, tester); + prepare(application, tester, 1, 0, 1, 0, true, defaultResources, version1, Optional.empty()); tester.getNodes(application, Node.State.reserved).forEach(node -> - assertEquals(version1, node.allocation().get().membership().cluster().vespaVersion())); + assertEquals(Version.fromString(version1), node.allocation().get().membership().cluster().vespaVersion())); - prepare(application, 1, 0, 1, 0, true, defaultResources, version2, tester); + prepare(application, tester, 1, 0, 1, 0, true, defaultResources, version2, Optional.empty()); tester.getNodes(application, Node.State.reserved).forEach(node -> - assertEquals(version2, node.allocation().get().membership().cluster().vespaVersion())); + assertEquals(Version.fromString(version2), node.allocation().get().membership().cluster().vespaVersion())); } @Test @@ -702,29 +745,27 @@ public class ProvisioningTest { private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, int content1Size, NodeResources flavor, ProvisioningTester tester) { - return prepare(application, container0Size, container1Size, content0Size, content1Size, flavor, - Version.fromString("6.42"), tester); + return prepare(application, tester, container0Size, container1Size, content0Size, content1Size, flavor, "6.42"); } - private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, - int content1Size, NodeResources nodeResources, Version wantedVersion, ProvisioningTester tester) { - return prepare(application, container0Size, container1Size, content0Size, content1Size, false, nodeResources, - wantedVersion, tester); + private SystemState prepare(ApplicationId application, ProvisioningTester tester, int container0Size, int container1Size, int content0Size, + int content1Size, NodeResources nodeResources, String wantedVersion) { + return prepare(application, tester, container0Size, container1Size, content0Size, content1Size, false, nodeResources, + wantedVersion, Optional.empty()); } - private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, - int content1Size, boolean required, NodeResources nodeResources, Version wantedVersion, - ProvisioningTester tester) { + private SystemState prepare(ApplicationId application, ProvisioningTester tester, int container0Size, int container1Size, int content0Size, + int content1Size, boolean required, NodeResources nodeResources, String wantedVersion, Optional<String> dockerImageRepo) { // "deploy prepare" with a two container clusters and a storage cluster having of two groups ClusterSpec containerCluster0 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container0")).vespaVersion(wantedVersion).build(); ClusterSpec containerCluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1")).vespaVersion(wantedVersion).build(); ClusterSpec contentCluster0 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content0")).vespaVersion(wantedVersion).build(); ClusterSpec contentCluster1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content1")).vespaVersion(wantedVersion).build(); - Set<HostSpec> container0 = prepare(application, containerCluster0, container0Size, 1, required, nodeResources, tester); - Set<HostSpec> container1 = prepare(application, containerCluster1, container1Size, 1, required, nodeResources, tester); - Set<HostSpec> content0 = prepare(application, contentCluster0, content0Size, 1, required, nodeResources, tester); - Set<HostSpec> content1 = prepare(application, contentCluster1, content1Size, 1, required, nodeResources, tester); + Set<HostSpec> container0 = prepare(application, tester, containerCluster0, container0Size, 1, required, nodeResources); + Set<HostSpec> container1 = prepare(application, tester, containerCluster1, container1Size, 1, required, nodeResources); + Set<HostSpec> content0 = prepare(application, tester, contentCluster0, content0Size, 1, required, nodeResources); + Set<HostSpec> content1 = prepare(application, tester, contentCluster1, content1Size, 1, required, nodeResources); Set<HostSpec> allHosts = new HashSet<>(); allHosts.addAll(container0); @@ -755,8 +796,8 @@ public class ProvisioningTest { return new SystemState(allHosts, container0, container1, content0, content1); } - private Set<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, - boolean required, NodeResources nodeResources, ProvisioningTester tester) { + private Set<HostSpec> prepare(ApplicationId application, ProvisioningTester tester, ClusterSpec cluster, int nodeCount, int groups, + boolean required, NodeResources nodeResources) { if (nodeCount == 0) return Collections.emptySet(); // this is a shady practice return new HashSet<>(tester.prepare(application, cluster, nodeCount, groups, required, nodeResources)); } diff --git a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp index fef063fb1cf..e6eb01596a8 100644 --- a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp +++ b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp @@ -84,6 +84,7 @@ private: EntryVector _removes; generation_t _transfer_gen; generation_t _trim_gen; + mutable size_t _memory_usage_cnt; public: MockNearestNeighborIndex(const DocVectorAccess& vectors) @@ -91,7 +92,8 @@ public: _adds(), _removes(), _transfer_gen(std::numeric_limits<generation_t>::max()), - _trim_gen(std::numeric_limits<generation_t>::max()) + _trim_gen(std::numeric_limits<generation_t>::max()), + _memory_usage_cnt(0) { } void clear() { @@ -119,6 +121,7 @@ public: } generation_t get_transfer_gen() const { return _transfer_gen; } generation_t get_trim_gen() const { return _trim_gen; } + size_t memory_usage_cnt() const { return _memory_usage_cnt; } void add_document(uint32_t docid) override { auto vector = _vectors.get_vector(docid).typify<double>(); @@ -135,6 +138,7 @@ public: _trim_gen = first_used_gen; } vespalib::MemoryUsage memory_usage() const override { + ++_memory_usage_cnt; return vespalib::MemoryUsage(); } std::vector<Neighbor> find_top_k(uint32_t k, vespalib::tensor::TypedCells vector, uint32_t explore_k) const override { @@ -582,4 +586,12 @@ TEST_F("commit() ensures transfer and trim hold lists on nearest neighbor index" EXPECT_EQUAL(gen_3, index.get_trim_gen()); } +TEST_F("Memory usage is extracted from index when updating stats on attribute", DenseTensorAttributeMockIndex) +{ + size_t before = f.mock_index().memory_usage_cnt(); + f.getStatus(); + size_t after = f.mock_index().memory_usage_cnt(); + EXPECT_EQUAL(before + 1, after); +} + TEST_MAIN() { TEST_RUN_ALL(); vespalib::unlink("test.dat"); } diff --git a/searchlib/src/vespa/searchlib/attribute/attributevector.h b/searchlib/src/vespa/searchlib/attribute/attributevector.h index b5f6beaa718..a396fb70b7c 100644 --- a/searchlib/src/vespa/searchlib/attribute/attributevector.h +++ b/searchlib/src/vespa/searchlib/attribute/attributevector.h @@ -320,6 +320,10 @@ protected: return _genHolder; } + const GenerationHolder& getGenerationHolder() const { + return _genHolder; + } + template<typename T> bool clearDoc(ChangeVectorT< ChangeTemplate<T> > &changes, DocId doc); diff --git a/searchlib/src/vespa/searchlib/common/bitvector.cpp b/searchlib/src/vespa/searchlib/common/bitvector.cpp index e28ebe6682f..2e70e9f2603 100644 --- a/searchlib/src/vespa/searchlib/common/bitvector.cpp +++ b/searchlib/src/vespa/searchlib/common/bitvector.cpp @@ -165,7 +165,7 @@ BitVector::countInterval(Index start, Index end) const ++endw; } if (startw < endw) { - res += IAccelrated::getAccelrator()->populationCount(bitValues + startw, endw - startw); + res += IAccelrated::getAccelrator().populationCount(bitValues + startw, endw - startw); } if (partialEnd) { res += Optimized::popCount(bitValues[endw] & ~endBits(last)); @@ -178,7 +178,7 @@ void BitVector::orWith(const BitVector & right) { verifyContains(*this, right); - IAccelrated::getAccelrator()->orBit(getActiveStart(), right.getWordIndex(getStartIndex()), getActiveBytes()); + IAccelrated::getAccelrator().orBit(getActiveStart(), right.getWordIndex(getStartIndex()), getActiveBytes()); repairEnds(); invalidateCachedCount(); @@ -201,7 +201,7 @@ BitVector::andWith(const BitVector & right) { verifyContains(*this, right); - IAccelrated::getAccelrator()->andBit(getActiveStart(), right.getWordIndex(getStartIndex()), getActiveBytes()); + IAccelrated::getAccelrator().andBit(getActiveStart(), right.getWordIndex(getStartIndex()), getActiveBytes()); setGuardBit(); invalidateCachedCount(); @@ -213,7 +213,7 @@ BitVector::andNotWith(const BitVector& right) { verifyContains(*this, right); - IAccelrated::getAccelrator()->andNotBit(getActiveStart(), right.getWordIndex(getStartIndex()), getActiveBytes()); + IAccelrated::getAccelrator().andNotBit(getActiveStart(), right.getWordIndex(getStartIndex()), getActiveBytes()); setGuardBit(); invalidateCachedCount(); @@ -221,7 +221,7 @@ BitVector::andNotWith(const BitVector& right) void BitVector::notSelf() { - IAccelrated::getAccelrator()->notBit(getActiveStart(), getActiveBytes()); + IAccelrated::getAccelrator().notBit(getActiveStart(), getActiveBytes()); setGuardBit(); invalidateCachedCount(); } diff --git a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp index 811a318682b..ec31bcb5117 100644 --- a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp +++ b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp @@ -268,7 +268,7 @@ void DotProductExecutorBase<BaseType>::execute(uint32_t docId) { size_t count = getAttributeValues(docId, values); size_t commonRange = std::min(count, _queryVector.size()); static_assert(std::is_same<typename AT::ValueType, BaseType>::value); - outputs().set_number(0, _multiplier->dotProduct( + outputs().set_number(0, _multiplier.dotProduct( &_queryVector[0], reinterpret_cast<const typename AT::ValueType *>(values), commonRange)); } diff --git a/searchlib/src/vespa/searchlib/features/dotproductfeature.h b/searchlib/src/vespa/searchlib/features/dotproductfeature.h index a13fc6794fc..bca6983877c 100644 --- a/searchlib/src/vespa/searchlib/features/dotproductfeature.h +++ b/searchlib/src/vespa/searchlib/features/dotproductfeature.h @@ -181,8 +181,8 @@ public: using AT = multivalue::Value<BaseType>; using V = std::vector<BaseType>; private: - vespalib::hwaccelrated::IAccelrated::UP _multiplier; - V _queryVector; + const vespalib::hwaccelrated::IAccelrated & _multiplier; + V _queryVector; virtual size_t getAttributeValues(uint32_t docid, const AT * & count) = 0; public: DotProductExecutorBase(const V & queryVector); diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp index b023df19c5b..9f127018a92 100644 --- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp +++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp @@ -64,6 +64,16 @@ DenseTensorAttribute::consider_remove_from_index(DocId docid) } } +vespalib::MemoryUsage +DenseTensorAttribute::memory_usage() const +{ + vespalib::MemoryUsage result = TensorAttribute::memory_usage(); + if (_index) { + result.merge(_index->memory_usage()); + } + return result; +} + DenseTensorAttribute::DenseTensorAttribute(vespalib::stringref baseFileName, const Config& cfg, const NearestNeighborIndexFactory& index_factory) : TensorAttribute(baseFileName, cfg, _denseTensorStore), diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.h b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.h index 84a60376fe7..8eba8354fbd 100644 --- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.h +++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.h @@ -24,6 +24,7 @@ private: std::unique_ptr<NearestNeighborIndex> _index; void consider_remove_from_index(DocId docid); + vespalib::MemoryUsage memory_usage() const override; public: DenseTensorAttribute(vespalib::stringref baseFileName, const Config& cfg, diff --git a/searchlib/src/vespa/searchlib/tensor/distance_functions.h b/searchlib/src/vespa/searchlib/tensor/distance_functions.h index 09f538269d8..d4636b04ef7 100644 --- a/searchlib/src/vespa/searchlib/tensor/distance_functions.h +++ b/searchlib/src/vespa/searchlib/tensor/distance_functions.h @@ -23,9 +23,9 @@ public: auto rhs_vector = rhs.typify<FloatType>(); size_t sz = lhs_vector.size(); assert(sz == rhs_vector.size()); - return _computer->squaredEuclideanDistance(&lhs_vector[0], &rhs_vector[0], sz); + return _computer.squaredEuclideanDistance(&lhs_vector[0], &rhs_vector[0], sz); } - vespalib::hwaccelrated::IAccelrated::UP _computer; + const vespalib::hwaccelrated::IAccelrated & _computer; }; template class SquaredEuclideanDistance<float>; diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp b/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp index 9795330b061..6d48ea1a967 100644 --- a/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp +++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp @@ -420,6 +420,7 @@ HnswIndex::memory_usage() const result.merge(_node_refs.getMemoryUsage()); result.merge(_nodes.getMemoryUsage()); result.merge(_links.getMemoryUsage()); + result.merge(_visited_set_pool.memory_usage()); return result; } diff --git a/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp b/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp index 0b9628e6872..acca4c3d8f1 100644 --- a/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp +++ b/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp @@ -111,10 +111,7 @@ TensorAttribute::onCommit() void TensorAttribute::onUpdateStat() { - // update statistics - vespalib::MemoryUsage total = _refVector.getMemoryUsage(); - total.merge(_tensorStore.getMemoryUsage()); - total.mergeGenerationHeldBytes(getGenerationHolder().getHeldBytes()); + vespalib::MemoryUsage total = memory_usage(); this->updateStatistics(_refVector.size(), _refVector.size(), total.allocatedBytes(), @@ -178,6 +175,15 @@ TensorAttribute::setTensorRef(DocId docId, EntryRef ref) } } +vespalib::MemoryUsage +TensorAttribute::memory_usage() const +{ + vespalib::MemoryUsage result = _refVector.getMemoryUsage(); + result.merge(_tensorStore.getMemoryUsage()); + result.mergeGenerationHeldBytes(getGenerationHolder().getHeldBytes()); + return result; +} + Tensor::UP TensorAttribute::getEmptyTensor() const { diff --git a/searchlib/src/vespa/searchlib/tensor/tensor_attribute.h b/searchlib/src/vespa/searchlib/tensor/tensor_attribute.h index 64f978a31d7..5e65317baa2 100644 --- a/searchlib/src/vespa/searchlib/tensor/tensor_attribute.h +++ b/searchlib/src/vespa/searchlib/tensor/tensor_attribute.h @@ -27,6 +27,8 @@ protected: void doCompactWorst(); void checkTensorType(const Tensor &tensor); void setTensorRef(DocId docId, EntryRef ref); + virtual vespalib::MemoryUsage memory_usage() const; + public: DECLARE_IDENTIFIABLE_ABSTRACT(TensorAttribute); using RefCopyVector = vespalib::Array<EntryRef>; diff --git a/staging_vespalib/src/tests/memorydatastore/memorydatastore.cpp b/staging_vespalib/src/tests/memorydatastore/memorydatastore.cpp index 7d047e36566..6c71d0a2cf6 100644 --- a/staging_vespalib/src/tests/memorydatastore/memorydatastore.cpp +++ b/staging_vespalib/src/tests/memorydatastore/memorydatastore.cpp @@ -36,7 +36,7 @@ MemoryDataStoreTest::testMemoryDataStore() void MemoryDataStoreTest::testVariableSizeVector() { - VariableSizeVector v(256); + VariableSizeVector v(20000, 5*20000); for (size_t i(0); i < 10000; i++) { asciistream os; os << i; diff --git a/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.cpp b/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.cpp index 33fcc29db11..b5107e68454 100644 --- a/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.cpp +++ b/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.cpp @@ -25,7 +25,7 @@ StorageProtocol::StorageProtocol(const std::shared_ptr<const document::DocumentT { } -StorageProtocol::~StorageProtocol() {} +StorageProtocol::~StorageProtocol() = default; mbus::IRoutingPolicy::UP StorageProtocol::createPolicy(const mbus::string&, const mbus::string&) const diff --git a/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.h b/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.h index 67ea121c340..40f2d26d833 100644 --- a/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.h +++ b/storageapi/src/vespa/storageapi/mbusprot/storageprotocol.h @@ -17,13 +17,13 @@ public: StorageProtocol(const std::shared_ptr<const document::DocumentTypeRepo>, const documentapi::LoadTypeSet& loadTypes); - ~StorageProtocol(); + ~StorageProtocol() override; const mbus::string& getName() const override { return NAME; } mbus::IRoutingPolicy::UP createPolicy(const mbus::string& name, const mbus::string& param) const override; mbus::Blob encode(const vespalib::Version&, const mbus::Routable&) const override; mbus::Routable::UP decode(const vespalib::Version&, mbus::BlobRef) const override; - virtual bool requireSequencing() const override { return true; } + bool requireSequencing() const override { return true; } private: ProtocolSerialization5_0 _serializer5_0; ProtocolSerialization5_1 _serializer5_1; diff --git a/vespalib/src/tests/dotproduct/dotproductbenchmark.cpp b/vespalib/src/tests/dotproduct/dotproductbenchmark.cpp index 3588e0ce239..d6e1aef9394 100644 --- a/vespalib/src/tests/dotproduct/dotproductbenchmark.cpp +++ b/vespalib/src/tests/dotproduct/dotproductbenchmark.cpp @@ -48,12 +48,12 @@ public: FullBenchmark(size_t numDocs, size_t numValue); ~FullBenchmark(); void compute(size_t docId) const override { - _dp->dotProduct(&_query[0], &_values[docId * _query.size()], _query.size()); + _dp.dotProduct(&_query[0], &_values[docId * _query.size()], _query.size()); } private: std::vector<T> _values; std::vector<T> _query; - IAccelrated::UP _dp; + const IAccelrated & _dp; }; template <typename T> diff --git a/vespalib/src/vespa/vespalib/data/memorydatastore.cpp b/vespalib/src/vespa/vespalib/data/memorydatastore.cpp index 866fb7d4929..df1b68cff12 100644 --- a/vespalib/src/vespa/vespalib/data/memorydatastore.cpp +++ b/vespalib/src/vespa/vespalib/data/memorydatastore.cpp @@ -15,9 +15,7 @@ MemoryDataStore::MemoryDataStore(Alloc && initialAlloc, Lock * lock) : _buffers.emplace_back(std::move(initialAlloc)); } -MemoryDataStore::~MemoryDataStore() -{ -} +MemoryDataStore::~MemoryDataStore() = default; MemoryDataStore::Reference MemoryDataStore::push_back(const void * data, const size_t sz) @@ -40,15 +38,14 @@ MemoryDataStore::push_back(const void * data, const size_t sz) return ref; } -VariableSizeVector::VariableSizeVector(size_t initialSize) : +VariableSizeVector::VariableSizeVector(size_t initialCount, size_t initialBufferSize) : _vector(), - _store(Alloc::alloc(initialSize)) + _store(Alloc::alloc(initialBufferSize)) { + _vector.reserve(initialCount); } -VariableSizeVector::~VariableSizeVector() -{ -} +VariableSizeVector::~VariableSizeVector() = default; VariableSizeVector::Reference VariableSizeVector::push_back(const void * data, const size_t sz) diff --git a/vespalib/src/vespa/vespalib/data/memorydatastore.h b/vespalib/src/vespa/vespalib/data/memorydatastore.h index 3ef0e3ee986..2d02bfb107c 100644 --- a/vespalib/src/vespa/vespalib/data/memorydatastore.h +++ b/vespalib/src/vespa/vespalib/data/memorydatastore.h @@ -99,7 +99,7 @@ public: }; VariableSizeVector(const VariableSizeVector &) = delete; VariableSizeVector & operator = (const VariableSizeVector &) = delete; - VariableSizeVector(size_t initialSize=256); + VariableSizeVector(size_t initialCount, size_t initialBufferSize); ~VariableSizeVector(); iterator begin() { return iterator(_vector, 0); } iterator end() { return iterator(_vector, size()); } diff --git a/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp b/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp index 2febe3f4405..1956dde689c 100644 --- a/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp +++ b/vespalib/src/vespa/vespalib/data/slime/binary_format.cpp @@ -7,10 +7,7 @@ #include <vespa/log/log.h> LOG_SETUP(".vespalib.data.slime.binary_format"); -namespace vespalib { -namespace slime { - -namespace binary_format { +namespace vespalib::slime::binary_format { struct BinaryEncoder : public ArrayTraverser, public ObjectSymbolTraverser @@ -242,32 +239,6 @@ size_t decode(const Memory &memory, Slime &slime, const Inserter &inserter) { return input.failed() ? 0 : input.get_offset(); } -} // namespace vespalib::slime::binary_format - -void -BinaryFormat::encode(const Slime &slime, Output &output) -{ - size_t chunk_size = 8000; - OutputWriter out(output, chunk_size); - binary_format::BinaryEncoder encoder(out); - encoder.encodeSymbolTable(slime); - encoder.encodeValue(slime.get()); -} - -size_t -BinaryFormat::decode(const Memory &memory, Slime &slime) -{ - return binary_format::decode<false>(memory, slime, SlimeInserter(slime)); -} - -size_t -BinaryFormat::decode_into(const Memory &memory, Slime &slime, const Inserter &inserter) -{ - return binary_format::decode<true>(memory, slime, inserter); -} - -namespace binary_format { - void write_cmpr_ulong(OutputWriter &out, uint64_t value) { out.commit(encode_cmpr_ulong(out.reserve(10), value)); @@ -288,5 +259,25 @@ write_type_and_size(OutputWriter &out, uint32_t type, uint64_t size) { } -} // namespace vespalib::slime -} // namespace vespalib +namespace vespalib::slime { + +void +BinaryFormat::encode(const Slime &slime, Output &output) { + size_t chunk_size = 8000; + OutputWriter out(output, chunk_size); + binary_format::BinaryEncoder encoder(out); + encoder.encodeSymbolTable(slime); + encoder.encodeValue(slime.get()); +} + +size_t +BinaryFormat::decode(const Memory &memory, Slime &slime) { + return binary_format::decode<false>(memory, slime, SlimeInserter(slime)); +} + +size_t +BinaryFormat::decode_into(const Memory &memory, Slime &slime, const Inserter &inserter) { + return binary_format::decode<true>(memory, slime, inserter); +} + +} diff --git a/vespalib/src/vespa/vespalib/data/slime/symbol_table.cpp b/vespalib/src/vespa/vespalib/data/slime/symbol_table.cpp index 643e23b5887..a7de28932f3 100644 --- a/vespalib/src/vespa/vespalib/data/slime/symbol_table.cpp +++ b/vespalib/src/vespa/vespalib/data/slime/symbol_table.cpp @@ -3,15 +3,14 @@ #include "symbol_table.h" #include <vespa/vespalib/stllike/hash_map.hpp> -namespace vespalib { -namespace slime { +namespace vespalib::slime { SymbolTable::SymbolTable(size_t expectedNumSymbols) : _symbols(3*expectedNumSymbols), - _names() + _names(expectedNumSymbols, expectedNumSymbols*16) { } -SymbolTable::~SymbolTable() { } +SymbolTable::~SymbolTable() = default; void SymbolTable::clear() { @@ -39,5 +38,4 @@ SymbolTable::lookup(const Memory &name) const { return pos->second; } -} // namespace vespalib::slime -} // namespace vespalib +} diff --git a/vespalib/src/vespa/vespalib/data/slime/symbol_table.h b/vespalib/src/vespa/vespalib/data/slime/symbol_table.h index c726da0319b..30599d76597 100644 --- a/vespalib/src/vespa/vespalib/data/slime/symbol_table.h +++ b/vespalib/src/vespa/vespalib/data/slime/symbol_table.h @@ -7,8 +7,7 @@ #include <vespa/vespalib/stllike/hash_map.h> #include <vespa/vespalib/data/memorydatastore.h> -namespace vespalib { -namespace slime { +namespace vespalib::slime { /** * Maps between strings and symbols. @@ -43,6 +42,5 @@ public: void clear(); }; -} // namespace vespalib::slime -} // namespace vespalib +} diff --git a/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp b/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp index ffa994641e9..bb132165e53 100644 --- a/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp +++ b/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.cpp @@ -106,7 +106,7 @@ class RuntimeVerificator public: RuntimeVerificator(); private: - void verify(IAccelrated & accelrated) { + void verify(const IAccelrated & accelrated) { verifyDotproduct<float>(accelrated); verifyDotproduct<double>(accelrated); verifyDotproduct<int32_t>(accelrated); @@ -122,8 +122,8 @@ RuntimeVerificator::RuntimeVerificator() GenericAccelrator generic; verify(generic); - IAccelrated::UP thisCpu(IAccelrated::getAccelrator()); - verify(*thisCpu); + const IAccelrated & thisCpu(IAccelrated::getAccelrator()); + verify(thisCpu); } class Selector @@ -154,11 +154,11 @@ static Selector _G_selector; RuntimeVerificator _G_verifyAccelrator; - -IAccelrated::UP +const IAccelrated & IAccelrated::getAccelrator() { - return _G_selector.create(); + static IAccelrated::UP accelrator = _G_selector.create(); + return *accelrator; } } diff --git a/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.h b/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.h index ea7e268bc6f..0292ad14643 100644 --- a/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.h +++ b/vespalib/src/vespa/vespalib/hwaccelrated/iaccelrated.h @@ -30,7 +30,7 @@ public: virtual double squaredEuclideanDistance(const float * a, const float * b, size_t sz) const = 0; virtual double squaredEuclideanDistance(const double * a, const double * b, size_t sz) const = 0; - static IAccelrated::UP getAccelrator() __attribute__((noinline)); + static const IAccelrated & getAccelrator() __attribute__((noinline)); }; } |