aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java19
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java19
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java9
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MemoryMetricsDb.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java13
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java16
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ProvisionedExpirer.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Reports.java5
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ParentHostFilter.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/EmptyProvisionServiceProvider.java5
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostResourcesCalculator.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/HostCapacityResponse.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ServiceMonitorStub.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java18
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java28
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningCompleteHostCalculatorTest.java11
33 files changed, 175 insertions, 79 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
index 8646121bd4b..f00414aa654 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.applications;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterInfo;
import com.yahoo.config.provision.IntRange;
import com.yahoo.config.provision.Capacity;
@@ -33,6 +34,7 @@ public class Cluster {
private final ClusterResources min, max;
private final IntRange groupSize;
private final boolean required;
+ private final Optional<CloudAccount> cloudAccount;
private final List<Autoscaling> suggestions;
private final Autoscaling target;
private final ClusterInfo clusterInfo;
@@ -47,6 +49,7 @@ public class Cluster {
ClusterResources maxResources,
IntRange groupSize,
boolean required,
+ Optional<CloudAccount> cloudAccount,
List<Autoscaling> suggestions,
Autoscaling target,
ClusterInfo clusterInfo,
@@ -58,6 +61,7 @@ public class Cluster {
this.max = Objects.requireNonNull(maxResources);
this.groupSize = Objects.requireNonNull(groupSize);
this.required = required;
+ this.cloudAccount = Objects.requireNonNull(cloudAccount);
this.suggestions = Objects.requireNonNull(suggestions);
Objects.requireNonNull(target);
if (target.resources().isPresent() && ! target.resources().get().isWithin(minResources, maxResources))
@@ -89,6 +93,9 @@ public class Cluster {
*/
public boolean required() { return required; }
+ /** Returns the enclave cloud account of this cluster, or empty if not enclave. */
+ public Optional<CloudAccount> cloudAccount() { return cloudAccount; }
+
/**
* Returns the computed resources (between min and max, inclusive) this cluster should
* have allocated at the moment (whether or not it actually has it),
@@ -134,19 +141,19 @@ public class Cluster {
public Cluster withConfiguration(boolean exclusive, Capacity capacity) {
return new Cluster(id, exclusive,
capacity.minResources(), capacity.maxResources(), capacity.groupSize(), capacity.isRequired(),
- suggestions, target, capacity.clusterInfo(), bcpGroupInfo, scalingEvents);
+ capacity.cloudAccount(), suggestions, target, capacity.clusterInfo(), bcpGroupInfo, scalingEvents);
}
public Cluster withSuggestions(List<Autoscaling> suggestions) {
- return new Cluster(id, exclusive, min, max, groupSize, required, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
+ return new Cluster(id, exclusive, min, max, groupSize, required, cloudAccount, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
}
public Cluster withTarget(Autoscaling target) {
- return new Cluster(id, exclusive, min, max, groupSize, required, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
+ return new Cluster(id, exclusive, min, max, groupSize, required, cloudAccount, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
}
public Cluster with(BcpGroupInfo bcpGroupInfo) {
- return new Cluster(id, exclusive, min, max, groupSize, required, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
+ return new Cluster(id, exclusive, min, max, groupSize, required, cloudAccount, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
}
/** Add or update (based on "at" time) a scaling event */
@@ -160,7 +167,7 @@ public class Cluster {
scalingEvents.add(scalingEvent);
prune(scalingEvents);
- return new Cluster(id, exclusive, min, max, groupSize, required, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
+ return new Cluster(id, exclusive, min, max, groupSize, required, cloudAccount, suggestions, target, clusterInfo, bcpGroupInfo, scalingEvents);
}
@Override
@@ -192,7 +199,7 @@ public class Cluster {
public static Cluster create(ClusterSpec.Id id, boolean exclusive, Capacity requested) {
return new Cluster(id, exclusive,
requested.minResources(), requested.maxResources(), requested.groupSize(), requested.isRequired(),
- List.of(), Autoscaling.empty(), requested.clusterInfo(), BcpGroupInfo.empty(), List.of());
+ requested.cloudAccount(), List.of(), Autoscaling.empty(), requested.clusterInfo(), BcpGroupInfo.empty(), List.of());
}
/** The predicted time it will take to rescale this cluster. */
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java
index cb70eb977c4..75a00fa951e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Flavor;
@@ -35,10 +36,12 @@ public class AllocatableResources {
/** Fake allocatable resources from requested capacity */
public AllocatableResources(ClusterResources requested,
ClusterSpec clusterSpec,
- NodeRepository nodeRepository) {
+ NodeRepository nodeRepository,
+ CloudAccount cloudAccount) {
this.nodes = requested.nodes();
this.groups = requested.groups();
- this.realResources = nodeRepository.resourcesCalculator().requestToReal(requested.nodeResources(), nodeRepository.exclusiveAllocation(clusterSpec), false);
+ this.realResources = nodeRepository.resourcesCalculator().requestToReal(requested.nodeResources(), cloudAccount,
+ nodeRepository.exclusiveAllocation(clusterSpec), false);
this.advertisedResources = requested.nodeResources();
this.clusterSpec = clusterSpec;
this.fulfilment = 1;
@@ -180,17 +183,20 @@ public class AllocatableResources {
// We decide resources: Add overhead to what we'll request (advertised) to make sure real becomes (at least) cappedNodeResources
var allocatableResources = calculateAllocatableResources(wantedResources,
nodeRepository,
+ model.cloudAccount(),
clusterSpec,
applicationLimits,
exclusive,
true);
var worstCaseRealResources = nodeRepository.resourcesCalculator().requestToReal(allocatableResources.advertisedResources,
+ model.cloudAccount(),
exclusive,
false);
if ( ! systemLimits.isWithinRealLimits(worstCaseRealResources, clusterSpec)) {
allocatableResources = calculateAllocatableResources(wantedResources,
nodeRepository,
+ model.cloudAccount(),
clusterSpec,
applicationLimits,
exclusive,
@@ -210,7 +216,7 @@ public class AllocatableResources {
for (Flavor flavor : nodeRepository.flavors().getFlavors()) {
// Flavor decide resources: Real resources are the worst case real resources we'll get if we ask for these advertised resources
NodeResources advertisedResources = nodeRepository.resourcesCalculator().advertisedResourcesOf(flavor);
- NodeResources realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive, false);
+ NodeResources realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, model.cloudAccount(), exclusive, false);
// Adjust where we don't need exact match to the flavor
if (flavor.resources().storageType() == NodeResources.StorageType.remote) {
@@ -251,20 +257,21 @@ public class AllocatableResources {
private static AllocatableResources calculateAllocatableResources(ClusterResources wantedResources,
NodeRepository nodeRepository,
+ CloudAccount cloudAccount,
ClusterSpec clusterSpec,
Limits applicationLimits,
boolean exclusive,
boolean bestCase) {
var systemLimits = nodeRepository.nodeResourceLimits();
- var advertisedResources = nodeRepository.resourcesCalculator().realToRequest(wantedResources.nodeResources(), exclusive, bestCase);
+ var advertisedResources = nodeRepository.resourcesCalculator().realToRequest(wantedResources.nodeResources(), cloudAccount, exclusive, bestCase);
advertisedResources = systemLimits.enlargeToLegal(advertisedResources, clusterSpec, exclusive, true); // Ask for something legal
advertisedResources = applicationLimits.cap(advertisedResources); // Overrides other conditions, even if it will then fail
- var realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive, bestCase); // What we'll really get
+ var realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, cloudAccount, exclusive, bestCase); // What we'll really get
if ( ! systemLimits.isWithinRealLimits(realResources, clusterSpec)
&& advertisedResources.storageType() == NodeResources.StorageType.any) {
// Since local disk reserves some of the storage, try to constrain to remote disk
advertisedResources = advertisedResources.with(NodeResources.StorageType.remote);
- realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive, bestCase);
+ realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, cloudAccount, exclusive, bestCase);
}
return new AllocatableResources(wantedResources.with(realResources),
advertisedResources,
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
index 45ef2d1d7b5..61d4ced1367 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
@@ -10,7 +10,6 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
import static com.yahoo.vespa.hosted.provision.autoscale.Autoscaler.headroomRequiredToScaleDown;
@@ -38,9 +37,7 @@ public class AllocationOptimizer {
* @return the best allocation, if there are any possible legal allocations, fulfilling the target
* fully or partially, within the limits
*/
- public Optional<AllocatableResources> findBestAllocation(Load loadAdjustment,
- ClusterModel model,
- Limits limits) {
+ public Optional<AllocatableResources> findBestAllocation(Load loadAdjustment, ClusterModel model, Limits limits) {
return findBestAllocations(loadAdjustment, model, limits).stream().findFirst();
}
@@ -51,9 +48,7 @@ public class AllocationOptimizer {
* @return the best allocations, if there are any possible legal allocations, fulfilling the target
* fully or partially, within the limits. The list contains the three best allocations, sorted from most to least preferred.
*/
- public List<AllocatableResources> findBestAllocations(Load loadAdjustment,
- ClusterModel model,
- Limits limits) {
+ public List<AllocatableResources> findBestAllocations(Load loadAdjustment, ClusterModel model, Limits limits) {
if (limits.isEmpty())
limits = Limits.of(new ClusterResources(minimumNodes, 1, NodeResources.unspecified()),
new ClusterResources(maximumNodes, maximumNodes, NodeResources.unspecified()),
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java
index 986ab830283..a0f9d6e260a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.autoscale;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
@@ -123,6 +124,7 @@ public class ClusterModel {
public Application application() { return application; }
public ClusterSpec clusterSpec() { return clusterSpec; }
+ public CloudAccount cloudAccount() { return cluster.cloudAccount().orElse(CloudAccount.empty); }
public AllocatableResources current() { return current; }
private ClusterNodesTimeseries nodeTimeseries() { return nodeTimeseries; }
private ClusterTimeseries clusterTimeseries() { return clusterTimeseries; }
@@ -438,6 +440,7 @@ public class ClusterModel {
clusterSpec,
application.id());
return nodeRepository.resourcesCalculator().requestToReal(initialResources,
+ cloudAccount(),
nodeRepository.exclusiveAllocation(clusterSpec),
false).memoryGb();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MemoryMetricsDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MemoryMetricsDb.java
index 940109bab8a..8b2dc44669f 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MemoryMetricsDb.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MemoryMetricsDb.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.collections.Pair;
+import com.yahoo.component.annotation.Inject;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
@@ -33,6 +34,11 @@ public class MemoryMetricsDb implements MetricsDb {
/** Lock all access for now since we modify lists inside a map */
private final Object lock = new Object();
+ @Inject
+ public MemoryMetricsDb() {
+ this(Clock.systemUTC());
+ }
+
public MemoryMetricsDb(Clock clock) {
this.clock = clock;
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java
index e9230d2c91a..057430b381f 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java
@@ -296,6 +296,11 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
}
}
+ private static String getStr(Record record, int col) {
+ CharSequence charSequence = record.getStrA(col);
+ return charSequence != null ? charSequence.toString() : "";
+ }
+
private ListMap<String, NodeMetricSnapshot> getNodeSnapshots(Instant startTime,
Set<String> hostnames,
SqlExecutionContext context) throws SqlException {
@@ -312,7 +317,7 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
try (RecordCursor cursor = factory.getCursor(context)) {
Record record = cursor.getRecord();
while (cursor.hasNext()) {
- String hostname = record.getStr(0).toString();
+ String hostname = getStr(record, 0);
if (hostnames.isEmpty() || hostnames.contains(hostname)) {
snapshots.put(hostname,
new NodeMetricSnapshot(Instant.ofEpochMilli(record.getTimestamp(1) / 1000),
@@ -345,9 +350,9 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
try (RecordCursor cursor = factory.getCursor(context)) {
Record record = cursor.getRecord();
while (cursor.hasNext()) {
- String applicationIdString = record.getStr(0).toString();
+ String applicationIdString = getStr(record, 0);
if ( ! application.serializedForm().equals(applicationIdString)) continue;
- String clusterId = record.getStr(1).toString();
+ String clusterId = getStr(record, 1);
if (cluster.value().equals(clusterId)) {
snapshots.add(new ClusterMetricSnapshot(Instant.ofEpochMilli(record.getTimestamp(2) / 1000),
record.getFloat(3),
@@ -475,7 +480,7 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
try (RecordCursor cursor = factory.getCursor(context)) {
Record record = cursor.getRecord();
while (cursor.hasNext()) {
- columns.add(record.getStr(0).toString());
+ columns.add(getStr(record, 0));
}
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java
index ab103b0bfcf..939c958efdd 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java
@@ -27,8 +27,8 @@ public class DirtyExpirer extends Expirer {
private final boolean wantToDeprovisionOnExpiry;
- DirtyExpirer(NodeRepository nodeRepository, Duration dirtyTimeout, Metric metric) {
- super(Node.State.dirty, History.Event.Type.deallocated, nodeRepository, dirtyTimeout, metric);
+ DirtyExpirer(NodeRepository nodeRepository, Duration expiryTime, Metric metric) {
+ super(Node.State.dirty, History.Event.Type.deallocated, nodeRepository, expiryTime, metric);
// Deprovision hosts on expiry if dynamically provisioned
this.wantToDeprovisionOnExpiry = nodeRepository.zone().cloud().dynamicProvisioning();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java
index d97f4566e57..76abf5c2aef 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveExpirer.java
@@ -16,7 +16,7 @@ import java.util.List;
/**
* Maintenance job which moves inactive nodes to dirty or parked after timeout.
*
- * The timeout is in place to provide a grace period in which nodes can be brought back to active
+ * The expiry time is in place to provide a grace period in which nodes can be brought back to active
* if they were deactivated in error. As inactive nodes retain their state
* they can be brought back to active and correct state faster than a new node.
*
@@ -32,12 +32,12 @@ import java.util.List;
public class InactiveExpirer extends Expirer {
private final NodeRepository nodeRepository;
- private final Duration timeout;
+ private final Duration expiryTime;
- InactiveExpirer(NodeRepository nodeRepository, Duration timeout, Metric metric) {
- super(Node.State.inactive, History.Event.Type.deactivated, nodeRepository, timeout, metric);
+ InactiveExpirer(NodeRepository nodeRepository, Duration expiryTime, Metric metric) {
+ super(Node.State.inactive, History.Event.Type.deactivated, nodeRepository, expiryTime, metric);
this.nodeRepository = nodeRepository;
- this.timeout = timeout;
+ this.expiryTime = expiryTime;
}
@Override
@@ -49,12 +49,12 @@ public class InactiveExpirer extends Expirer {
@Override
protected boolean isExpired(Node node) {
- return super.isExpired(node, timeout(node)) ||
+ return super.isExpired(node, expiryTime(node)) ||
node.allocation().get().owner().instance().isTester();
}
- private Duration timeout(Node node) {
- return timeout;
+ private Duration expiryTime(Node node) {
+ return expiryTime;
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ProvisionedExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ProvisionedExpirer.java
index 24901cb10a9..adcc486d5b4 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ProvisionedExpirer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ProvisionedExpirer.java
@@ -27,8 +27,8 @@ public class ProvisionedExpirer extends Expirer {
private final NodeRepository nodeRepository;
- ProvisionedExpirer(NodeRepository nodeRepository, Duration timeout, Metric metric) {
- super(Node.State.provisioned, History.Event.Type.provisioned, nodeRepository, timeout, metric);
+ ProvisionedExpirer(NodeRepository nodeRepository, Duration expiryTime, Metric metric) {
+ super(Node.State.provisioned, History.Event.Type.provisioned, nodeRepository, expiryTime, metric);
this.nodeRepository = nodeRepository;
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Reports.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Reports.java
index 8e82307deb1..e10a4e82a0c 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Reports.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Reports.java
@@ -6,7 +6,6 @@ import com.yahoo.slime.Inspector;
import com.yahoo.slime.ObjectTraverser;
import com.yahoo.slime.Type;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -20,8 +19,8 @@ public class Reports {
private final Map<String, Report> reports;
- public Reports() { this(Collections.emptyMap()); }
- private Reports(Map<String, Report> reports) { this.reports = Collections.unmodifiableMap(reports); }
+ public Reports() { this(Map.of()); }
+ private Reports(Map<String, Report> reports) { this.reports = Map.copyOf(reports); }
public boolean isEmpty() { return reports.isEmpty(); }
public Optional<Report> getReport(String id) { return Optional.ofNullable(reports.get(id)); }
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ParentHostFilter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ParentHostFilter.java
index d5aa82a8dc2..821fcd51e18 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ParentHostFilter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ParentHostFilter.java
@@ -12,7 +12,7 @@ import java.util.stream.Collectors;
/**
* Filter based on the parent host value (for virtualized nodes).
*
- * @author dybis
+ * @author Haakon Dybdahl
*/
public class ParentHostFilter {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
index 1315207efb8..8a5780496b1 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.persistence;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterInfo;
import com.yahoo.config.provision.IntRange;
import com.yahoo.config.provision.ApplicationId;
@@ -56,7 +57,7 @@ public class ApplicationSerializer {
private static final String maxResourcesKey = "max";
private static final String groupSizeKey = "groupSize";
private static final String requiredKey = "required";
- private static final String suggestedKey = "suggested";
+ private static final String cloudAccountKey = "cloudAccount";
private static final String suggestionsKey = "suggestionsKey";
private static final String clusterInfoKey = "clusterInfo";
private static final String bcpDeadlineKey = "bcpDeadline";
@@ -141,8 +142,7 @@ public class ApplicationSerializer {
toSlime(cluster.maxResources(), clusterObject.setObject(maxResourcesKey));
toSlime(cluster.groupSize(), clusterObject.setObject(groupSizeKey));
clusterObject.setBool(requiredKey, cluster.required());
- // TODO(olaa): Remove 'suggested' once all configservers have stopped reading entry
- toSlime(Autoscaling.empty(), clusterObject.setObject(suggestedKey));
+ cluster.cloudAccount().ifPresent(cloudAccount -> clusterObject.setString(cloudAccountKey, cloudAccount.value()));
toSlime(cluster.suggestions(), clusterObject.setArray(suggestionsKey));
toSlime(cluster.target(), clusterObject.setObject(targetKey));
if (! cluster.clusterInfo().isEmpty())
@@ -159,6 +159,7 @@ public class ApplicationSerializer {
clusterResourcesFromSlime(clusterObject.field(maxResourcesKey)),
intRangeFromSlime(clusterObject.field(groupSizeKey)),
clusterObject.field(requiredKey).asBool(),
+ optionalCloudAccount(clusterObject.field(cloudAccountKey)),
suggestionsFromSlime(clusterObject.field(suggestionsKey)),
autoscalingFromSlime(clusterObject.field(targetKey)),
clusterInfoFromSlime(clusterObject.field(clusterInfoKey)),
@@ -329,6 +330,10 @@ public class ApplicationSerializer {
};
}
+ private static Optional<CloudAccount> optionalCloudAccount(Inspector inspector) {
+ return inspector.valid() ? Optional.of(CloudAccount.from(inspector.asString())) : Optional.empty();
+ }
+
private static Optional<Instant> optionalInstant(Inspector inspector) {
return inspector.valid() ? Optional.of(Instant.ofEpochMilli(inspector.asLong())) : Optional.empty();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
index d511570881b..d2edaaf3737 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
@@ -157,7 +157,7 @@ public class CuratorDb {
* @return the nodes in their persisted state
*/
public List<Node> writeTo(List<Node> nodes, Agent agent, Optional<String> reason) {
- if (nodes.isEmpty()) return Collections.emptyList();
+ if (nodes.isEmpty()) return List.of();
List<Node> writtenNodes = new ArrayList<>(nodes.size());
@@ -191,7 +191,7 @@ public class CuratorDb {
}
public Node writeTo(Node.State toState, Node node, Agent agent, Optional<String> reason) {
- return writeTo(toState, Collections.singletonList(node), agent, reason).get(0);
+ return writeTo(toState, List.of(node), agent, reason).get(0);
}
/**
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
index a1306d7831a..ec48e1d5f34 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
@@ -51,6 +51,8 @@ public class CapacityPolicies {
private ClusterResources applyOn(ClusterResources resources, Capacity capacity, ApplicationId application, boolean exclusive) {
int nodes = decideSize(resources.nodes(), capacity.isRequired(), application.instance().isTester());
int groups = Math.min(resources.groups(), nodes); // cannot have more groups than nodes
+ while (groups > 1 && nodes % groups != 0)
+ groups--; // Must be divisible by the number of groups
var nodeResources = decideNodeResources(resources.nodeResources(), capacity.isRequired(), exclusive);
return new ClusterResources(nodes, groups, nodeResources);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/EmptyProvisionServiceProvider.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/EmptyProvisionServiceProvider.java
index 0d6a98f50a3..dae4b11a609 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/EmptyProvisionServiceProvider.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/EmptyProvisionServiceProvider.java
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.provisioning;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
@@ -41,10 +42,10 @@ public class EmptyProvisionServiceProvider implements ProvisionServiceProvider {
public NodeResources advertisedResourcesOf(Flavor flavor) { return flavor.resources(); }
@Override
- public NodeResources requestToReal(NodeResources resources, boolean exclusive, boolean bestCase) { return resources; }
+ public NodeResources requestToReal(NodeResources resources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) { return resources; }
@Override
- public NodeResources realToRequest(NodeResources resources, boolean exclusive, boolean bestCase) { return resources; }
+ public NodeResources realToRequest(NodeResources resources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) { return resources; }
@Override
public long reservedDiskSpaceInBase2Gb(NodeType nodeType, boolean sharedHost) { return 0; }
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostResourcesCalculator.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostResourcesCalculator.java
index e474ae6eea3..204660f9869 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostResourcesCalculator.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostResourcesCalculator.java
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.provisioning;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
@@ -28,13 +29,14 @@ public interface HostResourcesCalculator {
* Used with exclusive hosts:
* Returns the lowest possible real resources we'll get if requesting the given advertised resources
*/
- NodeResources requestToReal(NodeResources advertisedResources, boolean exclusiveAllocation, boolean bestCase);
+ NodeResources requestToReal(NodeResources advertisedResources, CloudAccount cloudAccount,
+ boolean exclusiveAllocation, boolean bestCase);
/**
* Used with shared hosts:
* Returns the advertised resources we need to request to be sure to get at least the given real resources.
*/
- NodeResources realToRequest(NodeResources realResources, boolean exclusiveAllocation, boolean bestCase);
+ NodeResources realToRequest(NodeResources realResources, CloudAccount cloudAccount, boolean exclusiveAllocation, boolean bestCase);
/**
* Returns the disk space to reserve in base2 GB. This space is reserved for use by the host, e.g. for storing
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 8c52f389daf..b149a9af2c2 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
@@ -179,6 +179,13 @@ class NodeAllocation {
if (violatesExclusivity(candidate) != NodeCandidate.ExclusivityViolation.NONE) return Retirement.violatesExclusivity;
if (requiredHostFlavor.isPresent() && ! candidate.parent.map(node -> node.flavor().name()).equals(requiredHostFlavor)) return Retirement.violatesHostFlavor;
if (candidate.violatesSpares) return Retirement.violatesSpares;
+
+ var group = candidate.allocation().get().membership().cluster().group();
+ if (cluster.isStateful() && group.isPresent() && requested.count().isPresent()) {
+ long nodesInGroup = nodes.values().stream().filter(n -> groupOf(n).equals(group) && ! isRetired(n)).count();
+ if (nodesInGroup >= requested.groupSize())
+ return Retirement.groupSurplus;
+ }
return Retirement.none;
}
@@ -290,6 +297,10 @@ class NodeAllocation {
return candidate.allocation().flatMap(a -> a.membership().cluster().group());
}
+ private boolean isRetired(NodeCandidate candidate) {
+ return candidate.allocation().map(a -> a.membership().retired()).orElse(false);
+ }
+
private Node resize(Node node) {
NodeResources hostResources = allNodes.parentOf(node).get().flavor().resources();
return node.with(new Flavor(requested.resources().get()
@@ -463,6 +474,7 @@ class NodeAllocation {
violatesHostFlavor("node violates host flavor"),
violatesHostFlavorGeneration("node violates host flavor generation"),
violatesSpares("node is assigned to a host we want to use as a spare"),
+ groupSurplus("group has enough nodes"),
none("");
private final String description;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
index d4c4e86f0a3..d8565b81e41 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
@@ -55,6 +55,7 @@ public abstract class NodeCandidate implements Nodelike, Comparable<NodeCandidat
final boolean exclusiveSwitch;
/** True if this node belongs to a group which will not be needed after this deployment */
+ // TODO: Always false
final boolean isSurplus;
/** This node does not exist in the node repository yet */
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 8e910a4d61c..7ac80dfbdb3 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
@@ -6,6 +6,7 @@ import com.yahoo.config.provision.ActivationContext;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationTransaction;
import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostFilter;
@@ -187,7 +188,8 @@ public class NodeRepositoryProvisioner implements Provisioner {
boolean firstDeployment = nodes.isEmpty();
var current =
firstDeployment // start at min, preserve current resources otherwise
- ? new AllocatableResources(initialResourcesFrom(requested, clusterSpec, application.id()), clusterSpec, nodeRepository)
+ ? new AllocatableResources(initialResourcesFrom(requested, clusterSpec, application.id()), clusterSpec,
+ nodeRepository, requested.cloudAccount().orElse(CloudAccount.empty))
: new AllocatableResources(nodes, nodeRepository);
var model = new ClusterModel(nodeRepository, application, clusterSpec, cluster, nodes, current, nodeRepository.metricsDb(), nodeRepository.clock());
return within(Limits.of(requested), model, firstDeployment);
@@ -199,9 +201,7 @@ public class NodeRepositoryProvisioner implements Provisioner {
/** Make the minimal adjustments needed to the current resources to stay within the limits */
- private ClusterResources within(Limits limits,
- ClusterModel model,
- boolean firstDeployment) {
+ private ClusterResources within(Limits limits, ClusterModel model, boolean firstDeployment) {
if (limits.min().equals(limits.max())) return limits.min();
// Don't change current deployments that are still legal
@@ -209,9 +209,7 @@ public class NodeRepositoryProvisioner implements Provisioner {
return model.current().advertisedResources();
// Otherwise, find an allocation that preserves the current resources as well as possible
- return allocationOptimizer.findBestAllocation(Load.one(),
- model,
- limits)
+ return allocationOptimizer.findBestAllocation(Load.one(), model, limits)
.orElseThrow(() -> newNoAllocationPossible(model.current().clusterSpec(), limits))
.advertisedResources();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/HostCapacityResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/HostCapacityResponse.java
index 9f7c795cf48..9fbc096667e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/HostCapacityResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/HostCapacityResponse.java
@@ -12,7 +12,6 @@ import com.yahoo.vespa.hosted.provision.maintenance.CapacityChecker;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -46,7 +45,7 @@ public class HostCapacityResponse extends HttpResponse {
}
private List<Node> parseHostList(String hosts) {
- List<String> hostNames = Arrays.asList(hosts.split(","));
+ List<String> hostNames = List.of(hosts.split(","));
try {
return capacityChecker.nodesFromHostnames(hostNames);
} catch (IllegalArgumentException e) {
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 c518087f325..cfe4886c903 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
@@ -27,7 +27,6 @@ import com.yahoo.vespa.orchestrator.status.HostInfo;
import com.yahoo.vespa.orchestrator.status.HostStatus;
import java.net.URI;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
@@ -66,7 +65,7 @@ class NodesResponse extends SlimeJsonResponse {
this.wantedDockerTagFlag = PermanentFlags.WANTED_DOCKER_TAG.bindTo(nodeRepository.flagSource());
// Cannot use Set.of() because the nodeRepository account can also be the empty account (at least in tests).
- var nonEnclaveAccounts = new HashSet<>(Arrays.asList(CloudAccount.empty, nodeRepository.zone().cloud().account()));
+ var nonEnclaveAccounts = new HashSet<>(List.of(CloudAccount.empty, nodeRepository.zone().cloud().account()));
this.filter = NodesV2ApiHandler.toNodeFilter(request, nonEnclaveAccounts);
Cursor root = slime.setObject();
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
index f653416d973..5795e25d247 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
@@ -8,7 +8,7 @@ import com.yahoo.config.provision.SystemName;
* For running NodeRepository API with some mocked data.
* This is used by both NodeAdmin and NodeRepository tests.
*
- * @author dybis
+ * @author Haakon Dybdahl
*/
public class ContainerConfig {
@@ -27,6 +27,7 @@ public class ContainerConfig {
<accesslog type='disabled'/>
<component id='com.yahoo.test.ManualClock'/>
<component id='com.yahoo.vespa.curator.mock.MockCurator'/>
+ <component id='com.yahoo.vespa.hosted.provision.autoscale.MemoryMetricsDb'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.OrchestratorMock'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.MockDeployer'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.MockInfraDeployer'/>
@@ -34,7 +35,6 @@ public class ContainerConfig {
<component id='com.yahoo.vespa.hosted.provision.testutils.ServiceMonitorStub'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.MockDuperModel'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors'/>
- <component id='com.yahoo.vespa.hosted.provision.autoscale.QuestMetricsDb'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.MockMetricsFetcher'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.MockNodeRepository'/>
<component id='com.yahoo.vespa.hosted.provision.testutils.MockProvisionServiceProvider'/>
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java
index 5f0ac4fbcdb..166fcb8edc7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java
@@ -9,6 +9,7 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -37,7 +38,7 @@ public class MockNameResolver implements NameResolver {
Objects.requireNonNull(hostname, "hostname must be non-null");
Arrays.stream(ipAddress).forEach(ip -> Objects.requireNonNull(ip, "ipAddress must be non-null"));
records.computeIfAbsent(hostname, (k) -> new HashSet<>())
- .addAll(Arrays.asList(ipAddress));
+ .addAll(List.of(ipAddress));
return this;
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ServiceMonitorStub.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ServiceMonitorStub.java
index 5409061c441..700a16577af 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ServiceMonitorStub.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ServiceMonitorStub.java
@@ -19,7 +19,6 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.service.monitor.ServiceModel;
import com.yahoo.vespa.service.monitor.ServiceMonitor;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -38,7 +37,7 @@ public class ServiceMonitorStub implements ServiceMonitor {
@Inject
@SuppressWarnings("unused")
public ServiceMonitorStub(NodeRepository nodeRepository) {
- this(Collections.emptyMap(), nodeRepository);
+ this(Map.of(), nodeRepository);
}
/** Create a service monitor where all nodes are initially up */
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java
index 5e4dfdc974d..8318ec65f05 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
@@ -92,7 +93,7 @@ public class ClusterModelTest {
return new ClusterModel(nodeRepository,
application.with(status),
clusterSpec, cluster,
- new AllocatableResources(clusterResources(), clusterSpec, nodeRepository),
+ new AllocatableResources(clusterResources(), clusterSpec, nodeRepository, cluster.cloudAccount().orElse(CloudAccount.empty)),
clock, Duration.ofMinutes(10), Duration.ofMinutes(5),
timeseries(cluster,100, queryRate, writeRate, clock),
ClusterNodesTimeseries.empty());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java
index 0fa73aa50a5..f2507786a8b 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.autoscale.awsnodes;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
@@ -43,7 +44,7 @@ public class AwsHostResourcesCalculatorImpl implements HostResourcesCalculator {
}
@Override
- public NodeResources requestToReal(NodeResources advertisedResources, boolean exclusive, boolean bestCase) {
+ public NodeResources requestToReal(NodeResources advertisedResources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) {
var consideredFlavors = consideredFlavorsGivenAdvertised(advertisedResources);
double memoryOverhead = consideredFlavors.stream()
.mapToDouble(flavor -> resourcesCalculator.memoryOverhead(flavor, advertisedResources, false))
@@ -56,7 +57,7 @@ public class AwsHostResourcesCalculatorImpl implements HostResourcesCalculator {
}
@Override
- public NodeResources realToRequest(NodeResources realResources, boolean exclusive, boolean bestCase) {
+ public NodeResources realToRequest(NodeResources realResources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) {
double chosenMemoryOverhead = bestCase ? Integer.MAX_VALUE : 0;
double chosenDiskOverhead = bestCase ? Integer.MAX_VALUE : 0;
for (VespaFlavor flavor : consideredFlavorsGivenReal(realResources)) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
index f25d4cc3c30..72f402ca997 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.persistence;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterInfo;
import com.yahoo.config.provision.IntRange;
import com.yahoo.config.provision.ApplicationId;
@@ -40,6 +41,7 @@ public class ApplicationSerializerTest {
new ClusterResources(12, 6, new NodeResources(3, 6, 21, 24)),
IntRange.empty(),
true,
+ Optional.empty(),
List.of(),
Autoscaling.empty(),
ClusterInfo.empty(),
@@ -52,6 +54,7 @@ public class ApplicationSerializerTest {
new ClusterResources(14, 7, new NodeResources(3, 6, 21, 24)),
IntRange.of(3, 5),
false,
+ Optional.of(CloudAccount.from("aws:123456789012")),
List.of(new Autoscaling(Autoscaling.Status.unavailable,
"",
Optional.of(new ClusterResources(20, 10,
@@ -97,6 +100,7 @@ public class ApplicationSerializerTest {
assertEquals(originalCluster.maxResources(), serializedCluster.maxResources());
assertEquals(originalCluster.groupSize(), serializedCluster.groupSize());
assertEquals(originalCluster.required(), serializedCluster.required());
+ assertEquals(originalCluster.cloudAccount(), serializedCluster.cloudAccount());
assertEquals(originalCluster.suggestions(), serializedCluster.suggestions());
assertEquals(originalCluster.target(), serializedCluster.target());
assertEquals(originalCluster.clusterInfo(), serializedCluster.clusterInfo());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
index ff5ffd82bf1..a91902c8eba 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
@@ -399,7 +399,7 @@ public class DynamicAllocationTest {
}
@Test
- public void node_resources_are_relaxed_in_dev() {
+ public void node_resources_are_reduced_in_dev() {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build();
tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.fast)), NodeType.host, 10, true);
tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.slow)), NodeType.host, 10, true);
@@ -419,6 +419,22 @@ public class DynamicAllocationTest {
}
@Test
+ public void node_resources_are_reduced_in_staging() {
+ var resources = new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.fast);
+ ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.staging, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build();
+ tester.makeReadyNodes(3, new Flavor(resources), NodeType.host, 10, true);
+ tester.activateTenantHosts();
+
+ ApplicationId application = ProvisioningTester.applicationId();
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test")).vespaVersion("1").build();
+
+ List<HostSpec> hosts = tester.prepare(application, cluster, 36, 2, resources);
+ tester.activate(application, hosts);
+ assertEquals(3, hosts.size());
+ assertEquals(1, hosts.stream().map(host -> host.membership().get().cluster().group().get()).distinct().count());
+ }
+
+ @Test
public void switching_from_legacy_flavor_syntax_to_resources_does_not_cause_reallocation() {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build();
tester.makeReadyNodes(2, new Flavor(new NodeResources(5, 20, 1400, 3)), NodeType.host, 10, true);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
index abcef421b4c..78a34326949 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
@@ -553,6 +553,34 @@ public class DynamicProvisioningTest {
2, 1, resources);
}
+ @Test
+ public void split_into_two_groups() {
+ List<Flavor> flavors = List.of(new Flavor("2x", new NodeResources(2, 20, 200, 0.1, fast, local)));
+
+ ProvisioningTester tester = new ProvisioningTester.Builder().dynamicProvisioning(true, false)
+ .flavors(flavors)
+ .hostProvisioner(new MockHostProvisioner(flavors))
+ .nameResolver(nameResolver)
+ .build();
+
+ tester.activateTenantHosts();
+
+ ApplicationId app1 = applicationId("app1");
+ ClusterSpec cluster1 = ClusterSpec.request(content, new ClusterSpec.Id("cluster1")).vespaVersion("8").build();
+
+ System.out.println("Initial deployment ----------------------");
+ tester.activate(app1, cluster1, Capacity.from(resources(6, 1, 2, 20, 200, fast, StorageType.any)));
+ tester.assertNodes("Initial deployment: 1 group",
+ 6, 1, 2, 20, 200, fast, remote, app1, cluster1);
+
+ System.out.println("Split into 2 groups ---------------------");
+ tester.activate(app1, cluster1, Capacity.from(resources(6, 2, 2, 20, 200, fast, StorageType.any)));
+ tester.assertNodes("Change to 2 groups: Gets 6 active non-retired nodes",
+ 6, 2, 2, 20, 200, fast, remote, app1, cluster1);
+ List<Node> retired = tester.nodeRepository().nodes().list().owner(app1).cluster(cluster1.id()).state(Node.State.active).retired().asList();
+ assertEquals("... and in addition 3 retired nodes", 3, retired.size());
+ }
+
private ProvisioningTester tester(boolean sharing) {
var hostProvisioner = new MockHostProvisioner(new NodeFlavors(ProvisioningTester.createConfig()).getFlavors(), nameResolver, 0);
return new ProvisioningTester.Builder().dynamicProvisioning(true, sharing).hostProvisioner(hostProvisioner).nameResolver(nameResolver).build();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java
index 1f8178dff6a..183ff85da47 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.provisioning;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Flavor;
@@ -143,6 +144,7 @@ public class DynamicProvisioningTester {
cluster.maxResources(),
cluster.groupSize(),
cluster.required(),
+ cluster.cloudAccount(),
cluster.suggestions(),
cluster.target(),
cluster.clusterInfo(),
@@ -265,12 +267,12 @@ public class DynamicProvisioningTester {
}
@Override
- public NodeResources requestToReal(NodeResources resources, boolean exclusive, boolean bestCase) {
+ public NodeResources requestToReal(NodeResources resources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) {
return resources.withMemoryGb(resources.memoryGb());
}
@Override
- public NodeResources realToRequest(NodeResources resources, boolean exclusive, boolean bestCase) {
+ public NodeResources realToRequest(NodeResources resources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) {
return resources.withMemoryGb(resources.memoryGb());
}
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 5f2790e886a..7b690b880c2 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
@@ -288,13 +288,13 @@ public class ProvisioningTest {
assertEquals("Superfluous container nodes are also dirtyed",
4-2 + 5-2 + 1 + 4-2, tester.nodeRepository().nodes().list(Node.State.dirty).size());
assertEquals("Superfluous content nodes are retired",
- 5-3 + 6-3 - 1, tester.getNodes(application1, Node.State.active).retired().size());
+ 5-3 + 6-3 -1, tester.getNodes(application1, Node.State.active).retired().size());
// increase content slightly
SystemState state6 = prepare(application1, 2, 2, 4, 3, defaultResources, tester);
tester.activate(application1, state6.allHosts);
assertEquals("One content node is unretired",
- 5-4 + 6-3 - 1, tester.getNodes(application1, Node.State.active).retired().size());
+ 5-4 + 6-3 -1, tester.getNodes(application1, Node.State.active).retired().size());
// Then reserve more
SystemState state7 = prepare(application1, 8, 2, 2, 2, defaultResources, tester);
@@ -505,7 +505,7 @@ public class ProvisioningTest {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east")))
.flavors(List.of(hostFlavor))
.build();
- tester.makeReadyHosts(31, hostFlavor.resources()).activateTenantHosts();
+ tester.makeReadyHosts(32, hostFlavor.resources()).activateTenantHosts();
ApplicationId app1 = ProvisioningTester.applicationId("app1");
ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
index 48bed11d83f..4ec290dd7ba 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
@@ -811,13 +811,13 @@ public class ProvisioningTester {
}
@Override
- public NodeResources requestToReal(NodeResources resources, boolean exclusive, boolean bestCase) {
+ public NodeResources requestToReal(NodeResources resources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) {
return resources.withMemoryGb(resources.memoryGb() - memoryTaxGb)
.withDiskGb(resources.diskGb() - ( resources.storageType() == local ? localDiskTax : 0) );
}
@Override
- public NodeResources realToRequest(NodeResources resources, boolean exclusive, boolean bestCase) {
+ public NodeResources realToRequest(NodeResources resources, CloudAccount cloudAccount, boolean exclusive, boolean bestCase) {
return resources.withMemoryGb(resources.memoryGb() + memoryTaxGb)
.withDiskGb(resources.diskGb() + ( resources.storageType() == local ? localDiskTax : 0) );
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningCompleteHostCalculatorTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningCompleteHostCalculatorTest.java
index c18815fc439..1bf42d72180 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningCompleteHostCalculatorTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningCompleteHostCalculatorTest.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.provisioning;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
@@ -62,8 +63,8 @@ public class VirtualNodeProvisioningCompleteHostCalculatorTest {
Flavor hostFlavor = new Flavor(new NodeResources(20, 40, 1000, 4));
var calculator = new CompleteResourcesCalculator(hostFlavor);
var originalReal = new NodeResources(0.7, 6.0, 12.9, 1.0);
- var realToRequest = calculator.realToRequest(originalReal, false, false);
- var requestToReal = calculator.requestToReal(realToRequest, false, false);
+ var realToRequest = calculator.realToRequest(originalReal, CloudAccount.empty, false, false);
+ var requestToReal = calculator.requestToReal(realToRequest, CloudAccount.empty, false, false);
var realResourcesOf = calculator.realResourcesOf(realToRequest);
assertEquals(originalReal, requestToReal);
assertEquals(originalReal, realResourcesOf);
@@ -93,7 +94,8 @@ public class VirtualNodeProvisioningCompleteHostCalculatorTest {
}
@Override
- public NodeResources requestToReal(NodeResources advertisedResources, boolean exclusive, boolean bestCase) {
+ public NodeResources requestToReal(NodeResources advertisedResources, CloudAccount cloudAccount,
+ boolean exclusive, boolean bestCase) {
double memoryOverhead = memoryOverhead(advertisedResourcesOf(hostFlavor).memoryGb(), advertisedResources, false);
double diskOverhead = diskOverhead(advertisedResourcesOf(hostFlavor).diskGb(), advertisedResources, false);
return advertisedResources.withMemoryGb(advertisedResources.memoryGb() - memoryOverhead)
@@ -108,7 +110,8 @@ public class VirtualNodeProvisioningCompleteHostCalculatorTest {
}
@Override
- public NodeResources realToRequest(NodeResources realResources, boolean exclusive, boolean bestCase) {
+ public NodeResources realToRequest(NodeResources realResources, CloudAccount cloudAccount,
+ boolean exclusive, boolean bestCase) {
double memoryOverhead = memoryOverhead(advertisedResourcesOf(hostFlavor).memoryGb(), realResources, true);
double diskOverhead = diskOverhead(advertisedResourcesOf(hostFlavor).diskGb(), realResources, true);
return realResources.withMemoryGb(realResources.memoryGb() + memoryOverhead)