aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/vespa
diff options
context:
space:
mode:
Diffstat (limited to 'config-model/src/main/java/com/yahoo/vespa')
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java23
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java26
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/Host.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryCollector.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryConfigGenerator.java121
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java20
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/JvmHeapSizeValidator.java11
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java33
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForLocalLLMValidator.java55
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForOnnxModelChangesValidator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java35
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/Container.java6
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/SignificanceModelRegistry.java15
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java17
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java23
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java11
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java36
27 files changed, 355 insertions, 140 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java
index 32807db8405..e48aa0eef7c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java
@@ -2,19 +2,15 @@
package com.yahoo.vespa.documentmodel;
import com.yahoo.config.application.api.DeployLogger;
-import com.yahoo.schema.RankProfile;
import com.yahoo.schema.Schema;
-import com.yahoo.searchlib.rankingexpression.Reference;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
-import java.util.logging.Level;
+
+import static java.util.logging.Level.WARNING;
/**
* A document summary definition - a list of summary fields.
@@ -25,7 +21,7 @@ public class DocumentSummary extends FieldView {
private boolean fromDisk = false;
private boolean omitSummaryFeatures = false;
- private List<String> inherited = new ArrayList<>();
+ private final List<String> inherited = new ArrayList<>();
private final Schema owner;
@@ -115,7 +111,7 @@ public class DocumentSummary extends FieldView {
/** Returns the parent of this, if any */
public List<DocumentSummary> inherited() {
- return inherited.stream().map(name -> owner.getSummary(name)).toList();
+ return inherited.stream().map(owner::getSummary).toList();
}
@Override
@@ -126,13 +122,12 @@ public class DocumentSummary extends FieldView {
public void validate(DeployLogger logger) {
for (var inheritedName : inherited) {
var inheritedSummary = owner.getSummary(inheritedName);
- if (inheritedSummary == null) {
- // TODO Vespa 9: Throw IllegalArgumentException instead
- logger.logApplicationPackage(Level.WARNING,
- this + " inherits '" + inheritedName + "' but this" + " is not present in " + owner);
- }
+ // TODO: Throw when no one is doing this anymore
+ if (inheritedName.equals("default"))
+ logger.logApplicationPackage(WARNING, this + " inherits '" + inheritedName + "', which makes no sense. Remove this inheritance");
+ else if (inheritedSummary == null )
+ throw new IllegalArgumentException(this + " inherits '" + inheritedName + "', but this is not present in " + owner);
}
-
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java
index edc25bd8246..7034365c40b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java
@@ -1,7 +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.documentmodel;
-import com.yahoo.document.Field;
+import com.yahoo.vespa.objects.FieldBase;
import java.io.Serializable;
import java.util.Collection;
@@ -14,7 +14,7 @@ import java.util.Map;
public class FieldView implements Serializable {
private final String name;
- private final Map<String, Field> fields = new LinkedHashMap<>();
+ private final Map<String, FieldBase> fields = new LinkedHashMap<>();
/**
* Creates a view with a name
@@ -24,8 +24,8 @@ public class FieldView implements Serializable {
this.name = name;
}
public String getName() { return name; }
- public Collection<Field> getFields() { return fields.values(); }
- public Field get(String name) { return fields.get(name); }
+ public Collection<FieldBase> getFields() { return fields.values(); }
+ public FieldBase get(String name) { return fields.get(name); }
public void remove(String name) { fields.remove(name); }
/**
@@ -34,7 +34,7 @@ public class FieldView implements Serializable {
* @param field the field to add.
* @return itself for chaining purposes.
*/
- public FieldView add(Field field) {
+ public FieldView add(FieldBase field) {
if (fields.containsKey(field.getName())) {
if ( ! fields.get(field.getName()).equals(field)) {
throw new IllegalArgumentException(
@@ -54,7 +54,7 @@ public class FieldView implements Serializable {
* @return Itself for chaining.
*/
public FieldView add(FieldView view) {
- for(Field field : view.getFields()) {
+ for(var field : view.getFields()) {
add(field);
}
return this;
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java
index d50d5e36134..e529779735f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java
@@ -3,10 +3,13 @@ package com.yahoo.vespa.documentmodel;
import com.yahoo.document.DataType;
import com.yahoo.document.Field;
-import com.yahoo.schema.document.TypedKey;
+import com.yahoo.vespa.objects.FieldBase;
import java.io.Serializable;
-import java.util.*;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
import java.util.stream.Collectors;
import static com.yahoo.text.Lowercase.toLowerCase;
@@ -16,7 +19,7 @@ import static com.yahoo.text.Lowercase.toLowerCase;
*
* @author bratseth
*/
-public class SummaryField extends Field implements Cloneable, TypedKey {
+public class SummaryField extends FieldBase implements Cloneable {
/** A source (field name). */
public static class Source implements Serializable {
@@ -53,6 +56,7 @@ public class SummaryField extends Field implements Cloneable, TypedKey {
/** The command used per field in vsmsummary */
private VsmCommand vsmCommand = VsmCommand.NONE;
+ private DataType dataType;
/**
* The data sources for this output summary field, in prioritized order
@@ -62,7 +66,7 @@ public class SummaryField extends Field implements Cloneable, TypedKey {
*/
private Set<Source> sources = new java.util.LinkedHashSet<>();
- private Set<String> destinations =new java.util.LinkedHashSet<>();
+ private Set<String> destinations = new java.util.LinkedHashSet<>();
/** True if this field was defined implicitly */
private boolean implicit = false;
@@ -84,8 +88,9 @@ public class SummaryField extends Field implements Cloneable, TypedKey {
}
public SummaryField(String name, DataType type, SummaryTransform transform) {
- super(name, type);
+ super(name);
this.transform=transform;
+ this.dataType = type;
}
public static SummaryField createWithUnresolvedType(String name) {
@@ -106,6 +111,10 @@ public class SummaryField extends Field implements Cloneable, TypedKey {
public boolean hasUnresolvedType() { return unresolvedType; }
+ public DataType getDataType() {
+ return dataType;
+ }
+
public void setTransform(SummaryTransform transform) {
this.transform = transform;
if (SummaryTransform.DYNAMICTEASER.equals(transform) || SummaryTransform.BOLDED.equals(transform)) {
@@ -194,7 +203,7 @@ public class SummaryField extends Field implements Cloneable, TypedKey {
if (merge.getTransform() != getTransform())
throw new IllegalArgumentException(merge + " conflicts with " + this + ": different transforms");
- if (!merge.getDataType().equals(getDataType()))
+ if (!merge.dataType.equals(dataType))
throw new IllegalArgumentException(merge + " conflicts with " + this + ": different types");
setImplicit(false);
@@ -250,7 +259,7 @@ public class SummaryField extends Field implements Cloneable, TypedKey {
/** Returns a string which aids locating this field in the source search definition */
public String toLocateString() {
- return "summary " + getName() + " type " + toLowerCase(getDataType().getName()) + " in " + getDestinationString();
+ return "summary " + getName() + " type " + toLowerCase(dataType.getName()) + " in " + getDestinationString();
}
@Override
@@ -290,9 +299,6 @@ public class SummaryField extends Field implements Cloneable, TypedKey {
public void setResolvedDataType(DataType type) {
this.dataType = type;
- if (!hasForcedId()) {
- this.fieldId = calculateIdV7(null);
- }
unresolvedType = false;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java b/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
index d8149486b32..806f14da265 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
@@ -342,6 +342,7 @@ public abstract class AbstractService extends TreeConfigProducer<AnyConfigProduc
return getServicePropertyString(key, null);
}
+ @Override
public String getServicePropertyString(String key, String defStr) {
Object result = serviceProperties.get(key);
return (result == null) ? defStr : result.toString();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/Host.java b/config-model/src/main/java/com/yahoo/vespa/model/Host.java
index a8085919a98..f87f1382ffb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/Host.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/Host.java
@@ -16,7 +16,7 @@ import java.util.Objects;
public final class Host extends TreeConfigProducer<AnyConfigProducer> implements SentinelConfig.Producer, Comparable<Host> {
// Memory needed for auxiliary processes always running on the node (config-proxy, metrics-proxy).
- // Keep in sync with node-repository/ClusterModel.
+ // Keep in sync with node-repository/ClusterModel and startup scripts (go and bash).
public static final double memoryOverheadGb = 0.7;
private ConfigSentinel configSentinel = null;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java
index ad728d60c5a..a513cc673dd 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java
@@ -1,11 +1,9 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.admin;
-import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.TreeConfigProducer;
-import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.model.container.Container;
/**
@@ -30,9 +28,4 @@ public class LogserverContainer extends Container {
return "";
}
- @Override
- protected String jvmOmitStackTraceInFastThrowOption(ModelContext.FeatureFlags featureFlags) {
- return featureFlags.jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type.admin);
- }
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
index ca602b447fe..be38298279f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
@@ -97,11 +97,6 @@ public class ClusterControllerContainer extends Container implements
return ContainerServiceType.CLUSTERCONTROLLER_CONTAINER;
}
- @Override
- protected String jvmOmitStackTraceInFastThrowOption(ModelContext.FeatureFlags featureFlags) {
- return featureFlags.jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type.admin);
- }
-
private void configureZooKeeperServer(boolean runStandaloneZooKeeper) {
if (runStandaloneZooKeeper)
ContainerModelBuilder.addReconfigurableZooKeeperServerComponents(this);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
index c4702ba3543..8f262281edc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
@@ -10,7 +10,6 @@ import ai.vespa.metricsproxy.rpc.RpcConnector;
import ai.vespa.metricsproxy.rpc.RpcConnectorConfig;
import ai.vespa.metricsproxy.service.VespaServices;
import ai.vespa.metricsproxy.service.VespaServicesConfig;
-import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.provision.ApplicationId;
@@ -78,11 +77,6 @@ public class MetricsProxyContainer extends Container implements
}
@Override
- protected String jvmOmitStackTraceInFastThrowOption(ModelContext.FeatureFlags featureFlags) {
- return featureFlags.jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type.admin);
- }
-
- @Override
public int getWantedPort() {
return BASEPORT;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryCollector.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryCollector.java
index fcd587622da..03b96b12c03 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryCollector.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryCollector.java
@@ -5,6 +5,7 @@ import com.yahoo.cloud.config.OpenTelemetryConfig;
import com.yahoo.config.model.ApplicationConfigProducerRoot;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.TreeConfigProducer;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.model.AbstractService;
import com.yahoo.vespa.model.PortAllocBridge;
@@ -17,10 +18,12 @@ import java.util.Optional;
public class OpenTelemetryCollector extends AbstractService implements OpenTelemetryConfig.Producer {
private final Zone zone;
+ private final ApplicationId applicationId;
public OpenTelemetryCollector(TreeConfigProducer<?> parent) {
super(parent, "otelcol");
this.zone = null;
+ this.applicationId = null;
setProp("clustertype", "admin");
setProp("clustername", "admin");
}
@@ -28,6 +31,7 @@ public class OpenTelemetryCollector extends AbstractService implements OpenTelem
public OpenTelemetryCollector(TreeConfigProducer<?> parent, DeployState deployState) {
super(parent, "otelcol");
this.zone = deployState.zone();
+ this.applicationId = deployState.getProperties().applicationId();
setProp("clustertype", "admin");
setProp("clustername", "admin");
}
@@ -50,7 +54,7 @@ public class OpenTelemetryCollector extends AbstractService implements OpenTelem
@Override
public void getConfig(OpenTelemetryConfig.Builder builder) {
- var generator = new OpenTelemetryConfigGenerator(zone);
+ var generator = new OpenTelemetryConfigGenerator(zone, applicationId);
AnyConfigProducer pp = this;
AnyConfigProducer p = pp.getParent();
while (p != null && p != pp) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryConfigGenerator.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryConfigGenerator.java
index 36eab6a04b3..3f7ca7b46a7 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryConfigGenerator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/otel/OpenTelemetryConfigGenerator.java
@@ -1,16 +1,23 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.admin.otel;
+import ai.vespa.metricsproxy.metric.dimensions.PublicDimensions;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.yahoo.config.model.ApplicationConfigProducerRoot.StatePortInfo;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterMembership;
+import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.model.Service;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import static com.yahoo.vespa.defaults.Defaults.getDefaults;
@@ -24,8 +31,12 @@ public class OpenTelemetryConfigGenerator {
private final String cert_file;
private final String key_file;
private List<StatePortInfo> statePorts = new ArrayList<>();
+ private final Zone zone;
+ private final ApplicationId applicationId;
- OpenTelemetryConfigGenerator(Zone zone) {
+ OpenTelemetryConfigGenerator(Zone zone, ApplicationId applicationId) {
+ this.zone = zone;
+ this.applicationId = applicationId;
boolean isCd = true;
boolean isPublic = true;
if (zone != null) {
@@ -81,8 +92,15 @@ public class OpenTelemetryConfigGenerator {
if (useTls) addTls(g);
{
g.writeFieldName("labels");
+ var dimVals = serviceAttributes(statePort.service());
+ // these will be tagged as dimension-name/label-value
+ // attributes on all metrics from this /state/v1 port
g.writeStartObject();
- g.writeStringField("service_type", statePort.serviceType());
+ for (var entry : dimVals.entrySet()) {
+ if (entry.getValue() != null) {
+ g.writeStringField(entry.getKey(), entry.getValue());
+ }
+ }
g.writeEndObject();
}
g.writeEndObject();
@@ -123,6 +141,37 @@ public class OpenTelemetryConfigGenerator {
}
g.writeEndObject(); // file
}
+ private void addProcessors(JsonGenerator g) throws java.io.IOException {
+ g.writeFieldName("processors");
+ g.writeStartObject();
+ addResourceProcessor(g);
+ g.writeEndObject();
+ }
+ private void addResourceProcessor(JsonGenerator g) throws java.io.IOException {
+ g.writeFieldName("resource");
+ g.writeStartObject();
+ g.writeFieldName("attributes");
+ g.writeStartArray();
+ // common attributes for all metrics from all services;
+ // which application and which cloud/system/zone/environment
+ addAttributeInsert(g, PublicDimensions.ZONE, zoneAttr());
+ addAttributeInsert(g, PublicDimensions.APPLICATION_ID, appIdAttr());
+ addAttributeInsert(g, "system", systemAttr());
+ addAttributeInsert(g, "tenantName", tenantAttr());
+ addAttributeInsert(g, "applicationName", appNameAttr());
+ addAttributeInsert(g, "instanceName", appInstanceAttr());
+ addAttributeInsert(g, "cloud", cloudAttr());
+ g.writeEndArray();
+ g.writeEndObject();
+ }
+ private void addAttributeInsert(JsonGenerator g, String key, String value) throws java.io.IOException {
+ if (value == null) return;
+ g.writeStartObject();
+ g.writeStringField("key", key);
+ g.writeStringField("value", value);
+ g.writeStringField("action", "insert");
+ g.writeEndObject();
+ }
private void addServiceBlock(JsonGenerator g) throws java.io.IOException {
g.writeFieldName("service");
g.writeStartObject();
@@ -159,6 +208,7 @@ public class OpenTelemetryConfigGenerator {
}
g.writeFieldName("processors");
g.writeStartArray();
+ g.writeString("resource");
g.writeEndArray();
{
g.writeFieldName("exporters");
@@ -186,6 +236,7 @@ public class OpenTelemetryConfigGenerator {
g.writeStartObject();
addReceivers(g);
addExporters(g);
+ addProcessors(g);
addServiceBlock(g);
g.writeEndObject(); // root
g.close();
@@ -203,4 +254,70 @@ public class OpenTelemetryConfigGenerator {
List<String> referencedPaths() {
return List.of(ca_file, cert_file, key_file);
}
+
+ private String zoneAttr() {
+ if (zone == null) return null;
+ return zone.environment().value() + "." + zone.region().value();
+ }
+ private String appIdAttr() {
+ if (applicationId == null) return null;
+ return applicationId.toFullString();
+ }
+ private String systemAttr() {
+ if (zone == null) return null;
+ return zone.system().value();
+ }
+ private String tenantAttr() {
+ if (applicationId == null) return null;
+ return applicationId.tenant().value();
+ }
+ private String appNameAttr() {
+ if (applicationId == null) return null;
+ return applicationId.application().value();
+ }
+ private String appInstanceAttr() {
+ if (applicationId == null) return null;
+ return applicationId.instance().value();
+ }
+ private String cloudAttr() {
+ if (zone == null) return null;
+ return zone.cloud().name().value();
+ }
+
+ private String getDeploymentCluster(ClusterSpec cluster) {
+ if (applicationId == null) return null;
+ if (zone == null) return null;
+ String appString = applicationId.toFullString();
+ return String.join(".", appString,
+ zone.environment().value(),
+ zone.region().value(),
+ cluster.id().value());
+ }
+
+ private Map<String, String> serviceAttributes(Service svc) {
+ Map<String, String> dimvals = new LinkedHashMap<>();
+ dimvals.put("instance", svc.getServiceName()); // should maybe be "local_service_name" ?
+ dimvals.put("instanceType", svc.getServiceType()); // maybe "local_service_type", or remove
+ String cName = svc.getServicePropertyString("clustername", null);
+ if (cName != null) {
+ // what about "clusterid" below, is it always the same?
+ dimvals.put("clustername", cName);
+ }
+ String cType = svc.getServicePropertyString("clustertype", null);
+ if (cType != null) {
+ dimvals.put("clustertype", cType);
+ }
+ var hostResource = svc.getHost();
+ if (hostResource != null) {
+ hostResource.spec().membership().map(ClusterMembership::cluster).ifPresent(cluster -> {
+ dimvals.put(PublicDimensions.DEPLOYMENT_CLUSTER, getDeploymentCluster(cluster));
+ // overrides value above
+ dimvals.put(PublicDimensions.INTERNAL_CLUSTER_TYPE, cluster.type().name());
+ // alternative to above
+ dimvals.put(PublicDimensions.INTERNAL_CLUSTER_ID, cluster.id().value());
+ cluster.group().ifPresent(group -> dimvals.put(PublicDimensions.GROUP_ID, group.toString()));
+ });
+ }
+ return dimvals;
+ }
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java
index 40c9a03b126..02a6b243054 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java
@@ -132,7 +132,7 @@ public class ConstantTensorJsonValidator {
private void consumeTopObject() throws IOException {
for (var cur = parser.nextToken(); cur != JsonToken.END_OBJECT; cur = parser.nextToken()) {
assertCurrentTokenIs(JsonToken.FIELD_NAME);
- String fieldName = parser.getCurrentName();
+ String fieldName = parser.currentName();
switch (fieldName) {
case FIELD_TYPE -> consumeTypeField();
case FIELD_VALUES -> consumeValuesField();
@@ -189,7 +189,7 @@ public class ConstantTensorJsonValidator {
}
for (var cur = parser.nextToken(); cur != JsonToken.END_OBJECT; cur = parser.nextToken()) {
assertCurrentTokenIs(JsonToken.FIELD_NAME);
- validateNumeric(parser.getCurrentName(), parser.nextToken());
+ validateNumeric(parser.currentName(), parser.nextToken());
}
}
@@ -199,7 +199,7 @@ public class ConstantTensorJsonValidator {
boolean seenValue = false;
for (int i = 0; i < 2; i++) {
assertNextTokenIs(JsonToken.FIELD_NAME);
- String fieldName = parser.getCurrentName();
+ String fieldName = parser.currentName();
switch (fieldName) {
case FIELD_ADDRESS -> {
validateTensorAddress(new HashSet<>(tensorDimensions.keySet()));
@@ -228,13 +228,13 @@ public class ConstantTensorJsonValidator {
// Iterate within the address key, value pairs
while ((parser.nextToken() != JsonToken.END_OBJECT)) {
assertCurrentTokenIs(JsonToken.FIELD_NAME);
- String dimensionName = parser.getCurrentName();
+ String dimensionName = parser.currentName();
TensorType.Dimension dimension = tensorDimensions.get(dimensionName);
if (dimension == null) {
- throw new InvalidConstantTensorException(parser, String.format("Tensor dimension '%s' does not exist", parser.getCurrentName()));
+ throw new InvalidConstantTensorException(parser, String.format("Tensor dimension '%s' does not exist", dimensionName));
}
if (!cellDimensions.contains(dimensionName)) {
- throw new InvalidConstantTensorException(parser, String.format("Duplicate tensor dimension '%s'", parser.getCurrentName()));
+ throw new InvalidConstantTensorException(parser, String.format("Duplicate tensor dimension '%s'", dimensionName));
}
cellDimensions.remove(dimensionName);
validateLabel(dimension);
@@ -300,7 +300,7 @@ public class ConstantTensorJsonValidator {
}
private void assertCurrentTokenIs(JsonToken wantedToken) {
- assertTokenIs(parser.getCurrentToken(), wantedToken);
+ assertTokenIs(parser.currentToken(), wantedToken);
}
private void assertNextTokenIs(JsonToken wantedToken) throws IOException {
@@ -316,11 +316,11 @@ public class ConstantTensorJsonValidator {
static class InvalidConstantTensorException extends IllegalArgumentException {
InvalidConstantTensorException(JsonParser parser, String message) {
- super(message + " " + parser.getCurrentLocation().toString());
+ super(message + " " + parser.currentLocation().toString());
}
InvalidConstantTensorException(JsonParser parser, Exception base) {
- super("Failed to parse JSON stream " + parser.getCurrentLocation().toString(), base);
+ super("Failed to parse JSON stream " + parser.currentLocation().toString(), base);
}
InvalidConstantTensorException(IOException base) {
@@ -412,7 +412,7 @@ public class ConstantTensorJsonValidator {
boolean seenValues = false;
for (int i = 0; i < 2; i++) {
assertNextTokenIs(JsonToken.FIELD_NAME);
- String fieldName = parser.getCurrentName();
+ String fieldName = parser.currentName();
switch (fieldName) {
case FIELD_ADDRESS -> {
validateTensorAddress(new HashSet<>(mappedDims));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/JvmHeapSizeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/JvmHeapSizeValidator.java
index 9cf5fe84c21..4900b56801c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/JvmHeapSizeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/JvmHeapSizeValidator.java
@@ -23,21 +23,22 @@ public class JvmHeapSizeValidator implements Validator {
context.model().getContainerClusters().forEach((clusterId, appCluster) -> {
var mp = appCluster.getMemoryPercentage().orElse(null);
if (mp == null) return;
- if (mp.availableMemoryGb().isEmpty()) {
+ if (mp.asAbsoluteGb().isEmpty()) {
context.deployState().getDeployLogger().log(Level.FINE, "Host resources unknown or percentage overridden with 'allocated-memory'");
return;
}
long jvmModelCost = appCluster.onnxModelCostCalculator().aggregatedModelCostInBytes();
if (jvmModelCost > 0) {
- double availableMemoryGb = mp.availableMemoryGb().getAsDouble();
+ double availableMemoryGb = mp.asAbsoluteGb().getAsDouble();
+ int percentageOfTotal = mp.ofContainerTotal().getAsInt();
double modelCostGb = jvmModelCost / (1024D * 1024 * 1024);
context.deployState().getDeployLogger().log(Level.FINE, () -> Text.format("JVM: %d%% (limit: %d%%), %.2fGB (limit: %.2fGB), ONNX: %.2fGB",
- mp.percentage(), percentLimit, availableMemoryGb, gbLimit, modelCostGb));
- if (mp.percentage() < percentLimit) {
+ percentageOfTotal, percentLimit, availableMemoryGb, gbLimit, modelCostGb));
+ if (percentageOfTotal < percentLimit) {
context.illegal(Text.format("Allocated percentage of memory of JVM in cluster '%s' is too low (%d%% < %d%%). " +
"Estimated cost of ONNX models is %.2fGB. Either use a node flavor with more memory or use less expensive models. " +
"You may override this validation by specifying 'allocated-memory' (https://docs.vespa.ai/en/performance/container-tuning.html#jvm-heap-size).",
- clusterId, mp.percentage(), percentLimit, modelCostGb));
+ clusterId, percentageOfTotal, percentLimit, modelCostGb));
}
if (availableMemoryGb < gbLimit) {
context.illegal(
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java
index 4d9386b5f19..ea579aaf5d1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java
@@ -2,12 +2,13 @@
package com.yahoo.vespa.model.application.validation;
import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.CapacityPolicies;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Exclusivity;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.QuotaExceededException;
import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.application.validation.Validation.Context;
@@ -31,25 +32,35 @@ public class QuotaValidator implements Validator {
@Override
public void validate(Context context) {
+ var zone = context.deployState().zone();
+ var exclusivity = new Exclusivity(zone, context.deployState().featureFlags().sharedHosts());
+ var capacityPolicies = new CapacityPolicies(zone, exclusivity, context.model().applicationPackage().getApplicationId(),
+ context.deployState().featureFlags().adminClusterArchitecture());
var quota = context.deployState().getProperties().quota();
quota.maxClusterSize().ifPresent(maxClusterSize -> validateMaxClusterSize(maxClusterSize, context.model()));
- quota.budgetAsDecimal().ifPresent(budget -> validateBudget(budget, context.model(), context.deployState().getProperties().zone()));
+ quota.budgetAsDecimal().ifPresent(budget -> validateBudget(budget, context, capacityPolicies));
}
- private void validateBudget(BigDecimal budget, VespaModel model, Zone zone) {
- var maxSpend = model.allClusters().stream()
- .filter(id -> !adminClusterIds(model).contains(id))
- .map(id -> model.provisioned().all().getOrDefault(id, zeroCapacity))
- .mapToDouble(c -> c.maxResources().cost()) // TODO: This may be unspecified -> 0
- .sum();
+ private void validateBudget(BigDecimal budget, Context context,
+ CapacityPolicies capacityPolicies) {
+ var zone = context.deployState().getProperties().zone();
+ var application = context.model().applicationPackage().getApplicationId();
+
+ var maxSpend = 0.0;
+ for (var id : context.model().allClusters()) {
+ if (adminClusterIds(context.model()).contains(id)) continue;
+ var cluster = context.model().provisioned().clusters().get(id);
+ var capacity = context.model().provisioned().capacities().getOrDefault(id, zeroCapacity);
+ maxSpend += capacityPolicies.applyOn(capacity, cluster.isExclusive()).maxResources().cost();
+ }
- var actualSpend = model.allocatedHosts().getHosts().stream()
+ var actualSpend = context.model().allocatedHosts().getHosts().stream()
.filter(hostSpec -> hostSpec.membership().get().cluster().type() != ClusterSpec.Type.admin)
.mapToDouble(hostSpec -> hostSpec.advertisedResources().cost())
.sum();
if (Math.abs(actualSpend) < 0.01) {
- log.warning("Deploying application " + model.applicationPackage().getApplicationId() + " with zero budget use. This is suspicious, but not blocked");
+ log.warning("Deploying application " + application + " with zero budget use. This is suspicious, but not blocked");
return;
}
@@ -69,7 +80,7 @@ public class QuotaValidator implements Validator {
/** Check that all clusters in the application do not exceed the quota max cluster size. */
private void validateMaxClusterSize(int maxClusterSize, VespaModel model) {
- var invalidClusters = model.provisioned().all().entrySet().stream()
+ var invalidClusters = model.provisioned().capacities().entrySet().stream()
.filter(entry -> entry.getValue() != null)
.filter(entry -> {
var cluster = entry.getValue();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
index ed0804f7420..7f624032627 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
@@ -19,6 +19,7 @@ import com.yahoo.vespa.model.application.validation.change.IndexingModeChangeVal
import com.yahoo.vespa.model.application.validation.change.NodeResourceChangeValidator;
import com.yahoo.vespa.model.application.validation.change.RedundancyIncreaseValidator;
import com.yahoo.vespa.model.application.validation.change.ResourcesReductionValidator;
+import com.yahoo.vespa.model.application.validation.change.RestartOnDeployForLocalLLMValidator;
import com.yahoo.vespa.model.application.validation.change.RestartOnDeployForOnnxModelChangesValidator;
import com.yahoo.vespa.model.application.validation.change.StartupCommandChangeValidator;
import com.yahoo.vespa.model.application.validation.change.StreamingSearchClusterChangeValidator;
@@ -129,6 +130,7 @@ public class Validation {
new CertificateRemovalChangeValidator().validate(execution);
new RedundancyValidator().validate(execution);
new RestartOnDeployForOnnxModelChangesValidator().validate(execution);
+ new RestartOnDeployForLocalLLMValidator().validate(execution);
}
public interface Context {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java
index 5d7a8779005..42410dc3acf 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java
@@ -60,9 +60,9 @@ public class ResourcesReductionValidator implements ChangeValidator {
* This will always yield specified node resources on hosted instances and never on self-hosted instances.
*/
private ClusterResources clusterResources(ClusterSpec.Id id, VespaModel model) {
- if ( ! model.provisioned().all().containsKey(id)) return null;
+ if ( ! model.provisioned().capacities().containsKey(id)) return null;
- ClusterResources resources = model.provisioned().all().get(id).maxResources();
+ ClusterResources resources = model.provisioned().capacities().get(id).maxResources();
if ( ! resources.nodeResources().isUnspecified()) return resources;
var containerCluster = model.getContainerClusters().get(id.value());
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForLocalLLMValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForLocalLLMValidator.java
new file mode 100644
index 00000000000..ccfc611c3dc
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForLocalLLMValidator.java
@@ -0,0 +1,55 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.application.validation.change;
+
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.application.validation.Validation.ChangeContext;
+import com.yahoo.vespa.model.container.ApplicationContainerCluster;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import static java.util.logging.Level.INFO;
+import static java.util.stream.Collectors.toUnmodifiableSet;
+
+/**
+ * If using local LLMs, this validator will make sure that restartOnDeploy is set for
+ * configs for this cluster.
+ *
+ * @author lesters
+ */
+public class RestartOnDeployForLocalLLMValidator implements ChangeValidator {
+
+ public static final String LOCAL_LLM_COMPONENT = ai.vespa.llm.clients.LocalLLM.class.getName();
+
+ private static final Logger log = Logger.getLogger(RestartOnDeployForLocalLLMValidator.class.getName());
+
+ @Override
+ public void validate(ChangeContext context) {
+ var previousClustersWithLocalLLM = findClustersWithLocalLLMs(context.previousModel());
+ var nextClustersWithLocalLLM = findClustersWithLocalLLMs(context.model());
+
+ // Only restart services if we use a local LLM in both the next and previous generation
+ for (var clusterId : intersect(previousClustersWithLocalLLM, nextClustersWithLocalLLM)) {
+ String message = "Need to restart services in %s due to use of local LLM".formatted(clusterId);
+ context.require(new VespaRestartAction(clusterId, message));
+ log.log(INFO, message);
+ }
+ }
+
+ private Set<ClusterSpec.Id> findClustersWithLocalLLMs(VespaModel model) {
+ return model.getContainerClusters().values().stream()
+ .filter(cluster -> cluster.getAllComponents().stream()
+ .anyMatch(component -> component.getClassId().getName().equals(LOCAL_LLM_COMPONENT)))
+ .map(ApplicationContainerCluster::id)
+ .collect(toUnmodifiableSet());
+ }
+
+ private Set<ClusterSpec.Id> intersect(Set<ClusterSpec.Id> a, Set<ClusterSpec.Id> b) {
+ Set<ClusterSpec.Id> result = new HashSet<>(a);
+ result.retainAll(b);
+ return result;
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForOnnxModelChangesValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForOnnxModelChangesValidator.java
index 008a3fc5547..e57110e44e5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForOnnxModelChangesValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/RestartOnDeployForOnnxModelChangesValidator.java
@@ -115,7 +115,7 @@ public class RestartOnDeployForOnnxModelChangesValidator implements ChangeValida
double memoryUsedByModels = currentModelCostInGb + nextModelCostInGb;
double availableMemory = Math.max(0, totalMemory - Host.memoryOverheadGb - memoryUsedByModels);
- var availableMemoryPercentage = cluster.availableMemoryPercentage();
+ var availableMemoryPercentage = cluster.heapSizePercentageOfAvailable();
int memoryPercentage = (int) (availableMemory / totalMemory * availableMemoryPercentage);
var prefix = "Validating Onnx models memory usage for %s".formatted(cluster);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
index d877600db13..11f4c9794aa 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
@@ -137,7 +137,7 @@ public class NodesSpecification {
int defaultMinGroups = nodes.from().orElse(1) / groupSize.to().orElse(nodes.from().orElse(1));
int defaultMaxGroups = groupSize.isEmpty() ? 1 : nodes.to().orElse(1) / groupSize.from().orElse(1);
- var min = new ClusterResources(nodes.from().orElse(1), groups.from().orElse(defaultMinGroups), nodeResources(nodesElement).getFirst());
+ var min = new ClusterResources(nodes.from().orElse(1), groups.from().orElse(defaultMinGroups), nodeResources(nodesElement).getFirst());
var max = new ClusterResources(nodes.to().orElse(1), groups.to().orElse(defaultMaxGroups), nodeResources(nodesElement).getSecond());
return new ResourceConstraints(min, max, groupSize);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
index e3fc680ad6a..1f8d7fd7520 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
@@ -82,11 +82,6 @@ public final class ApplicationContainer extends Container implements
builder.myid(index());
}
- @Override
- protected String jvmOmitStackTraceInFastThrowOption(ModelContext.FeatureFlags featureFlags) {
- return featureFlags.jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type.container);
- }
-
@Override public Optional<String> getPreShutdownCommand() { return Optional.of(prepareStopCommand(Duration.ofMinutes(6))); }
private Optional<NodeResources> realResources() {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
index ed7646b3066..531dc8f0fcf 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
@@ -209,22 +209,27 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
if (memoryPercentage != null) return Optional.of(JvmMemoryPercentage.of(memoryPercentage));
if (isHostedVespa()) {
- int availableMemoryPercentage = availableMemoryPercentage();
- if (getContainers().isEmpty()) return Optional.of(JvmMemoryPercentage.of(availableMemoryPercentage)); // Node memory is not known
-
- // Node memory is known so convert available memory percentage to node memory percentage
- double totalMemory = getContainers().stream().mapToDouble(c -> c.getHostResource().realResources().memoryGb()).min().orElseThrow();
- double jvmHeapDeductionGb = onnxModelCostCalculator.aggregatedModelCostInBytes() / (1024D * 1024 * 1024);
- double availableMemory = Math.max(0, totalMemory - Host.memoryOverheadGb - jvmHeapDeductionGb);
- int memoryPercentage = (int) (availableMemory / totalMemory * availableMemoryPercentage);
- logger.log(FINE, () -> "cluster id '%s': memoryPercentage=%d, availableMemory=%f, totalMemory=%f, availableMemoryPercentage=%d, jvmHeapDeductionGb=%f"
- .formatted(id(), memoryPercentage, availableMemory, totalMemory, availableMemoryPercentage, jvmHeapDeductionGb));
- return Optional.of(JvmMemoryPercentage.of(memoryPercentage, availableMemory));
+ int heapSizePercentageOfAvailable = heapSizePercentageOfAvailable();
+ if (getContainers().isEmpty()) return Optional.of(JvmMemoryPercentage.of(heapSizePercentageOfAvailable)); // Node memory is not known
+
+ // Node memory is known, so compute heap size as a percentage of available memory (excluding overhead, which the startup scripts also account for)
+ double totalMemoryGb = getContainers().stream().mapToDouble(c -> c.getHostResource().realResources().memoryGb()).min().orElseThrow();
+ double totalMemoryMinusOverhead = Math.max(0, totalMemoryGb - Host.memoryOverheadGb);
+ double onnxModelCostGb = onnxModelCostCalculator.aggregatedModelCostInBytes() / (1024D * 1024 * 1024);
+ double availableMemoryGb = Math.max(0, totalMemoryMinusOverhead - onnxModelCostGb);
+ int memoryPercentageOfAvailable = (int) (heapSizePercentageOfAvailable * availableMemoryGb / totalMemoryMinusOverhead);
+ int memoryPercentageOfTotal = (int) (heapSizePercentageOfAvailable * availableMemoryGb / totalMemoryGb);
+ logger.log(FINE, () -> ("cluster id '%s': memoryPercentageOfAvailable=%d, memoryPercentageOfTotal=%d, " +
+ "availableMemoryGb=%f, totalMemoryGb=%f, heapSizePercentageOfAvailable=%d, onnxModelCostGb=%f")
+ .formatted(id(), memoryPercentageOfAvailable, memoryPercentageOfTotal,
+ availableMemoryGb, totalMemoryGb, heapSizePercentageOfAvailable, onnxModelCostGb));
+ return Optional.of(JvmMemoryPercentage.of(memoryPercentageOfAvailable, memoryPercentageOfTotal,
+ availableMemoryGb * heapSizePercentageOfAvailable * 1e-2));
}
return Optional.empty();
}
- public int availableMemoryPercentage() {
+ public int heapSizePercentageOfAvailable() {
return getHostClusterId().isPresent() ?
heapSizePercentageOfTotalAvailableMemoryWhenCombinedCluster :
heapSizePercentageOfAvailableMemory;
@@ -310,14 +315,14 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
public void getConfig(QrStartConfig.Builder builder) {
super.getConfig(builder);
var memoryPct = getMemoryPercentage().orElse(null);
- int heapsize = memoryPct != null && memoryPct.availableMemoryGb().isPresent()
- ? (int) (memoryPct.availableMemoryGb().getAsDouble() * 1024) : 1536;
+ int heapsize = memoryPct != null && memoryPct.asAbsoluteGb().isPresent()
+ ? (int) (memoryPct.asAbsoluteGb().getAsDouble() * 1024) : 1536;
builder.jvm.verbosegc(true)
.availableProcessors(0)
.compressedClassSpaceSize(0)
.minHeapsize(heapsize)
.heapsize(heapsize);
- if (memoryPct != null) builder.jvm.heapSizeAsPercentageOfPhysicalMemory(memoryPct.percentage());
+ if (memoryPct != null) builder.jvm.heapSizeAsPercentageOfPhysicalMemory(memoryPct.ofContainerAvailable());
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
index f1945ca5341..c2368fd29a2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
@@ -105,15 +105,9 @@ public abstract class Container extends AbstractService implements
addBuiltinHandlers();
addChild(new SimpleComponent("com.yahoo.container.jdisc.ConfiguredApplication$ApplicationContext"));
-
- appendJvmOptions(jvmOmitStackTraceInFastThrowOption(deployState.featureFlags()));
addEnvironmentVariable("VESPA_MALLOC_MMAP_THRESHOLD","0x1000000"); // 16M
}
- protected String jvmOmitStackTraceInFastThrowOption(ModelContext.FeatureFlags featureFlags) {
- return featureFlags.jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type.container);
- }
-
void setOwner(ContainerCluster<?> owner) { this.owner = owner; }
/** True if this container is retired (slated for removal) */
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
index aeb6c030a49..00ab47e8ddb 100755
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
@@ -73,6 +73,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
+import java.util.OptionalInt;
import java.util.Set;
import java.util.TreeSet;
@@ -721,10 +722,10 @@ public abstract class ContainerCluster<CONTAINER extends Container>
* Returns the percentage of host physical memory this application has specified for nodes in this cluster,
* or empty if this is not specified by the application.
*/
- public record JvmMemoryPercentage(int percentage, OptionalDouble availableMemoryGb) {
- static JvmMemoryPercentage of(int percentage) { return new JvmMemoryPercentage(percentage, OptionalDouble.empty()); }
- static JvmMemoryPercentage of(int percentage, double availableMemoryGb) {
- return new JvmMemoryPercentage(percentage, OptionalDouble.of(availableMemoryGb));
+ public record JvmMemoryPercentage(int ofContainerAvailable, OptionalInt ofContainerTotal, OptionalDouble asAbsoluteGb) { // optionalInt pctOfTotal < int pctOfAvailable
+ static JvmMemoryPercentage of(int percentageOfAvailable) { return new JvmMemoryPercentage(percentageOfAvailable, OptionalInt.empty(), OptionalDouble.empty()); }
+ static JvmMemoryPercentage of(int percentageOfAvailable, int percentageOfTotal, double absoluteMemoryGb) {
+ return new JvmMemoryPercentage(percentageOfAvailable, OptionalInt.of(percentageOfTotal), OptionalDouble.of(absoluteMemoryGb));
}
}
public Optional<JvmMemoryPercentage> getMemoryPercentage() { return Optional.empty(); }
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/SignificanceModelRegistry.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/SignificanceModelRegistry.java
index c210c2621a6..693eebd75a8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/SignificanceModelRegistry.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/SignificanceModelRegistry.java
@@ -33,17 +33,15 @@ public class SignificanceModelRegistry extends SimpleComponent implements Signif
if (spec != null) {
for (Element modelElement : XML.getChildren(spec, "model")) {
- addConfig(
- modelElement.getAttribute("language"),
- Model.fromXml(deployState, modelElement, Set.of(SIGNIFICANCE_MODEL)).modelReference());
+ addConfig(Model.fromXml(deployState, modelElement, Set.of(SIGNIFICANCE_MODEL)).modelReference());
}
}
}
- public void addConfig(String language, ModelReference path) {
+ public void addConfig(ModelReference path) {
configList.add(
- new SignificanceModelConfig(language, path)
+ new SignificanceModelConfig(path)
);
}
@@ -53,19 +51,16 @@ public class SignificanceModelRegistry extends SimpleComponent implements Signif
builder.model(
configList.stream()
.map(config -> new SignificanceConfig.Model.Builder()
- .language(config.language)
.path(config.path)
).toList()
);
}
- class SignificanceModelConfig {
- private final String language;
+ static class SignificanceModelConfig {
private final ModelReference path;
- public SignificanceModelConfig(String language, ModelReference path) {
- this.language = language;
+ public SignificanceModelConfig(ModelReference path) {
this.path = path;
}
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 56e2a21e38b..4983b36bee1 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
@@ -815,8 +815,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
!container.getHostResource().realResources().gpuResources().isZero());
onnxModel.setGpuDevice(gpuDevice, hasGpu);
}
- cluster.onnxModelCostCalculator().registerModel(context.getApplicationPackage().getFile(onnxModel.getFilePath()), onnxModel.onnxModelOptions());
}
+ for (OnnxModel onnxModel : models.asMap().values())
+ cluster.onnxModelCostCalculator().registerModel(context.getApplicationPackage().getFile(onnxModel.getFilePath()), onnxModel.onnxModelOptions());
cluster.setModelEvaluation(new ContainerModelEvaluation(cluster, profiles, models));
}
@@ -1037,7 +1038,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
}
catch (NumberFormatException e) {
throw new IllegalArgumentException("The memory percentage given for nodes in " + cluster +
- " must be an integer percentage ending by the '%' sign", e);
+ " must be given as an integer followe by '%'", e);
}
}
@@ -1072,9 +1073,21 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
return List.of(node);
}
+ private static void requireFixedSizeSingularNodeIfTester(ConfigModelContext context, NodesSpecification nodes) {
+ if ( ! context.properties().hostedVespa() || ! context.properties().applicationId().instance().isTester())
+ return;
+
+ if ( ! nodes.maxResources().equals(nodes.minResources()))
+ throw new IllegalArgumentException("tester resources must be absolute, but min and max resources differ: " + nodes);
+
+ if (nodes.maxResources().nodes() > 1)
+ throw new IllegalArgumentException("tester cannot run on more than 1 node, but " + nodes.maxResources().nodes() + " nodes were specified");
+ }
+
private List<ApplicationContainer> createNodesFromNodeCount(ApplicationContainerCluster cluster, Element containerElement, Element nodesElement, ConfigModelContext context) {
try {
var nodesSpecification = NodesSpecification.from(new ModelElement(nodesElement), context);
+ requireFixedSizeSingularNodeIfTester(context, nodesSpecification);
var clusterId = ClusterSpec.Id.from(cluster.name());
Map<HostResource, ClusterMembership> hosts = nodesSpecification.provision(cluster.getRoot().hostSystem(),
ClusterSpec.Type.container,
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java b/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java
index 15f3eece8a9..c39da176828 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java
@@ -22,11 +22,6 @@ public class DispatchTuning {
private final Integer maxHitsPerPartition;
private final DispatchPolicy dispatchPolicy;
private final Double minActiveDocsCoverage;
-
- public Double getTopkProbability() {
- return topkProbability;
- }
-
private final Double topkProbability;
private DispatchTuning(Builder builder) {
@@ -45,6 +40,8 @@ public class DispatchTuning {
/** Returns the percentage of documents which must be available in a group for that group to receive queries */
public Double getMinActiveDocsCoverage() { return minActiveDocsCoverage; }
+ public Double getTopkProbability() { return topkProbability; }
+
public static class Builder {
private Integer maxHitsPerPartition;
@@ -71,14 +68,14 @@ public class DispatchTuning {
}
public static DispatchPolicy toDispatchPolicy(String policy) {
- switch (policy.toLowerCase()) {
- case "adaptive": case "random": return DispatchPolicy.ADAPTIVE; // TODO: Deprecate 'random' on Vespa 9
- case "round-robin": return DispatchPolicy.ROUNDROBIN;
- case "latency-amortized-over-requests" : return DispatchPolicy.LATENCY_AMORTIZED_OVER_REQUESTS;
- case "latency-amortized-over-time" : return DispatchPolicy.LATENCY_AMORTIZED_OVER_TIME;
- case "best-of-random-2" : return DispatchPolicy.BEST_OF_RANDOM_2;
- default: throw new IllegalArgumentException("Unknown dispatch policy '" + policy + "'");
- }
+ return switch (policy.toLowerCase()) {
+ case "adaptive", "random" -> DispatchPolicy.ADAPTIVE; // TODO: Deprecate 'random' on Vespa 9
+ case "round-robin" -> DispatchPolicy.ROUNDROBIN;
+ case "latency-amortized-over-requests" -> DispatchPolicy.LATENCY_AMORTIZED_OVER_REQUESTS;
+ case "latency-amortized-over-time" -> DispatchPolicy.LATENCY_AMORTIZED_OVER_TIME;
+ case "best-of-random-2" -> DispatchPolicy.BEST_OF_RANDOM_2;
+ default -> throw new IllegalArgumentException("Unknown dispatch policy '" + policy + "'");
+ };
}
public Builder setMinActiveDocsCoverage(Double minCoverage) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java
index c182f0e0507..1e35a94db59 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/DistributorCluster.java
@@ -35,6 +35,8 @@ public class DistributorCluster extends TreeConfigProducer<Distributor> implemen
private final boolean hasIndexedDocumentType;
private final int maxActivationInhibitedOutOfSyncGroups;
private final int contentLayerMetadataFeatureLevel;
+ private final boolean symmetricPutAndActivateReplicaSelection;
+
public static class Builder extends VespaDomBuilder.DomConfigProducerBuilderBase<DistributorCluster> {
ContentCluster parent;
@@ -97,19 +99,22 @@ public class DistributorCluster extends TreeConfigProducer<Distributor> implemen
var featureFlags = deployState.getProperties().featureFlags();
int maxInhibitedGroups = featureFlags.maxActivationInhibitedOutOfSyncGroups();
int contentLayerMetadataFeatureLevel = featureFlags.contentLayerMetadataFeatureLevel();
+ boolean symmetricPutAndActivateReplicaSelection = featureFlags.symmetricPutAndActivateReplicaSelection();
return new DistributorCluster(parent,
new BucketSplitting.Builder().build(new ModelElement(producerSpec)), gc,
hasIndexedDocumentType,
maxInhibitedGroups,
- contentLayerMetadataFeatureLevel);
+ contentLayerMetadataFeatureLevel,
+ symmetricPutAndActivateReplicaSelection);
}
}
private DistributorCluster(ContentCluster parent, BucketSplitting bucketSplitting,
GcOptions gc, boolean hasIndexedDocumentType,
int maxActivationInhibitedOutOfSyncGroups,
- int contentLayerMetadataFeatureLevel)
+ int contentLayerMetadataFeatureLevel,
+ boolean symmetricPutAndActivateReplicaSelection)
{
super(parent, "distributor");
this.parent = parent;
@@ -118,6 +123,7 @@ public class DistributorCluster extends TreeConfigProducer<Distributor> implemen
this.hasIndexedDocumentType = hasIndexedDocumentType;
this.maxActivationInhibitedOutOfSyncGroups = maxActivationInhibitedOutOfSyncGroups;
this.contentLayerMetadataFeatureLevel = contentLayerMetadataFeatureLevel;
+ this.symmetricPutAndActivateReplicaSelection = symmetricPutAndActivateReplicaSelection;
}
@Override
@@ -132,6 +138,7 @@ public class DistributorCluster extends TreeConfigProducer<Distributor> implemen
if (contentLayerMetadataFeatureLevel > 0) {
builder.enable_operation_cancellation(true);
}
+ builder.symmetric_put_and_activate_replica_selection(symmetricPutAndActivateReplicaSelection);
bucketSplitting.getConfig(builder);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
index bac86e37e8f..d37e5d5382a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
@@ -46,6 +46,7 @@ import com.yahoo.vespa.model.content.IndexedHierarchicDistributionValidator;
import com.yahoo.vespa.model.content.Redundancy;
import com.yahoo.vespa.model.content.ReservedDocumentTypeNameValidator;
import com.yahoo.vespa.model.content.StorageGroup;
+import com.yahoo.vespa.model.content.StorageNode;
import com.yahoo.vespa.model.content.engines.PersistenceEngine;
import com.yahoo.vespa.model.content.engines.ProtonEngine;
import com.yahoo.vespa.model.content.storagecluster.StorageCluster;
@@ -137,6 +138,7 @@ public class ContentCluster extends TreeConfigProducer<AnyConfigProducer> implem
c.rootGroup = new StorageGroup.Builder(contentElement, context).buildRootGroup(deployState, c, c.search.isStreaming());
c.clusterControllerConfig = createClusterControllerConfig(contentElement, deployState, c, resourceLimits);
validateThatGroupSiblingsAreUnique(c.clusterId, c.rootGroup);
+ warnIfDistributionKeyRangeIsSuboptimal(c.clusterId, c.rootGroup, deployState);
c.search.handleRedundancy(c.redundancy);
setupSearchCluster(c.search, contentElement, deployState.getDeployLogger());
@@ -247,7 +249,7 @@ public class ContentCluster extends TreeConfigProducer<AnyConfigProducer> implem
for (ContainerModel containerModel : containers) {
Optional<String> hostClusterId = containerModel.getCluster().getHostClusterId();
if (hostClusterId.isPresent() && hostClusterId.get().equals(clusterId) && containerModel.getCluster().getMemoryPercentage().isPresent()) {
- return containerModel.getCluster().getMemoryPercentage().get().percentage() * 0.01;
+ return containerModel.getCluster().getMemoryPercentage().get().ofContainerAvailable() * 0.01;
}
}
return 0.0;
@@ -275,6 +277,38 @@ public class ContentCluster extends TreeConfigProducer<AnyConfigProducer> implem
}
}
+ private static class HighestDistributionKeyAggregator {
+ public int nodeCount = 0;
+ public int highestNodeDistributionKey = 0;
+
+ void aggregateNodeStats(StorageGroup group) {
+ for (StorageNode n : group.getNodes()) {
+ nodeCount++;
+ highestNodeDistributionKey = Math.max(highestNodeDistributionKey, n.getDistributionKey());
+ }
+ for (StorageGroup g : group.getSubgroups()) {
+ aggregateNodeStats(g);
+ }
+ }
+ }
+
+ private void warnIfDistributionKeyRangeIsSuboptimal(String clusterId, StorageGroup rootGroup, DeployState deployState) {
+ if (rootGroup == null) {
+ return; // Unit testing case
+ }
+ var aggr = new HighestDistributionKeyAggregator();
+ aggr.aggregateNodeStats(rootGroup);
+ int warnThreshold = 100; // ... Not scientifically chosen
+ if ((aggr.highestNodeDistributionKey - aggr.nodeCount) >= warnThreshold) {
+ deployState.getDeployLogger().logApplicationPackage(WARNING,
+ ("Content cluster '%s' has %d node(s), but the highest distribution key is %d. " +
+ "Having much higher distribution keys than the number of nodes is not recommended, " +
+ "as it may negatively affect performance. " +
+ "See https://docs.vespa.ai/en/reference/services-content.html#node")
+ .formatted(clusterId, aggr.nodeCount, aggr.highestNodeDistributionKey));
+ }
+ }
+
private void addClusterControllers(ConfigModelContext context,
ModelElement contentElement,
ContentCluster contentCluster,