summaryrefslogtreecommitdiffstats
path: root/config-model
diff options
context:
space:
mode:
Diffstat (limited to 'config-model')
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainer.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java27
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java2
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java25
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java21
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java19
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java32
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java51
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java103
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java19
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java33
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java8
-rw-r--r--config-model/src/main/resources/schema/content.rnc3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java17
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java7
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java101
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java43
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java46
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java45
26 files changed, 532 insertions, 117 deletions
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 880e0e8c574..081f655b369 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
@@ -15,9 +15,11 @@ import com.yahoo.vespa.model.container.component.AccessLogComponent.CompressionT
*/
public class LogserverContainer extends Container {
- public LogserverContainer(AbstractConfigProducer parent, FeatureFlags featureFlags, boolean isHostedVespa) {
+ public LogserverContainer(AbstractConfigProducer<?> parent, FeatureFlags featureFlags, boolean isHostedVespa) {
super(parent, featureFlags, "" + 0, 0, isHostedVespa);
- addComponent(new AccessLogComponent(AccessLogType.jsonAccessLog, CompressionType.GZIP, ((LogserverContainerCluster) parent).getName(), true));
+ LogserverContainerCluster cluster = (LogserverContainerCluster) parent;
+ addComponent(new AccessLogComponent(
+ cluster, AccessLogType.jsonAccessLog, CompressionType.GZIP, cluster.getName(), true));
}
@Override
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 2ad3bc8f84a..4cb3dde0833 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
@@ -55,7 +55,7 @@ public class ClusterControllerContainer extends Container implements
"com.yahoo.vespa.clustercontroller.apps.clustercontroller.StateRestApiV2Handler",
"/cluster/v2/*",
CLUSTERCONTROLLER_BUNDLE);
- addComponent(new AccessLogComponent(AccessLogComponent.AccessLogType.jsonAccessLog,
+ addComponent(new AccessLogComponent(containerCluster().orElse(null), AccessLogComponent.AccessLogType.jsonAccessLog,
AccessLogComponent.CompressionType.GZIP,
"controller",
deployState.isHosted()));
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 2bb12654fc4..11039528fc7 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
@@ -11,6 +11,7 @@ import java.math.BigDecimal;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
@@ -19,6 +20,8 @@ import java.util.stream.Collectors;
* @author ogronnesby
*/
public class QuotaValidator extends Validator {
+ private static final Logger log = Logger.getLogger(QuotaValidator.class.getName());
+
@Override
public void validate(VespaModel model, DeployState deployState) {
var quota = deployState.getProperties().quota();
@@ -33,9 +36,13 @@ public class QuotaValidator extends Validator {
.mapToDouble(clusterCapacity -> clusterCapacity.nodeResources().cost() * clusterCapacity.nodes())
.sum();
- if (budget.doubleValue() < spend) {
- throwBudgetExceeded(spend, budget, systemName);
+ if (Math.abs(spend) < 0.01) {
+ log.warning("Deploying application " + model.applicationPackage().getApplicationId() + " with zero budget use. This is suspicious, but not blocked");
+ return;
}
+
+ throwIfBudgetNegative(spend, budget, systemName);
+ throwIfBudgetExceeded(spend, budget, systemName);
}
/** Check that all clusters in the application do not exceed the quota max cluster size. */
@@ -57,8 +64,20 @@ public class QuotaValidator extends Validator {
}
}
- private void throwBudgetExceeded(double spend, BigDecimal budget, SystemName systemName) {
- var message = String.format(Locale.US, "Hourly spend for maximum specified resources ($%.2f) exceeds budget from quota ($%.2f)!", spend, budget);
+ private void throwIfBudgetNegative(double spend, BigDecimal budget, SystemName systemName) {
+ if (budget.doubleValue() < 0) {
+ throwBudgetException("Please free up some capacity! This deployment's quota use is ($%.2f) and reserved quota is below zero! ($%.2f)", spend, budget, systemName);
+ }
+ }
+
+ private void throwIfBudgetExceeded(double spend, BigDecimal budget, SystemName systemName) {
+ if (budget.doubleValue() < spend) {
+ throwBudgetException("Please free up some capacity! This deployment's quota use ($%.2f) exceeds reserved quota ($%.2f)!", spend, budget, systemName);
+ }
+ }
+
+ private void throwBudgetException(String formatMessage, double spend, BigDecimal budget, SystemName systemName) {
+ var message = String.format(Locale.US, formatMessage, spend, budget);
var messageWithSystem = (systemName.equals(SystemName.Public) ? "" : systemName.value() + ": ") + message;
throw new IllegalArgumentException(messageWithSystem);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java
index 0add9f243fe..a462cb4fdb3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java
@@ -39,7 +39,7 @@ public class NodeResourceChangeValidator implements ChangeValidator {
}
private boolean changeRequiresRestart(NodeResources currentResources, NodeResources nextResources) {
- return currentResources.memoryGb() != nextResources.memoryGb();
+ return !currentResources.equals(nextResources);
}
private Optional<NodeResources> resourcesOf(ClusterSpec.Id clusterId, VespaModel model) {
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 8b85c0a46ae..a6d0dad5ff6 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
@@ -534,7 +534,7 @@ public abstract class ContainerCluster<CONTAINER extends Container>
var compressionType = isHostedVespa && deployState.featureFlags().enableZstdCompressionAccessLog()
? AccessLogComponent.CompressionType.ZSTD
: AccessLogComponent.CompressionType.GZIP;
- addComponent(new AccessLogComponent(AccessLogComponent.AccessLogType.jsonAccessLog, compressionType, getName(), isHostedVespa));
+ addComponent(new AccessLogComponent(this, AccessLogComponent.AccessLogType.jsonAccessLog, compressionType, getName(), isHostedVespa));
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java
index ffb7b876fa2..2905471b02e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/AccessLogComponent.java
@@ -3,9 +3,12 @@ package com.yahoo.vespa.model.container.component;
import com.yahoo.container.core.AccessLogConfig;
import com.yahoo.container.core.AccessLogConfig.FileHandler.CompressionFormat;
-import com.yahoo.container.logging.VespaAccessLog;
import com.yahoo.container.logging.JSONAccessLog;
+import com.yahoo.container.logging.VespaAccessLog;
import com.yahoo.osgi.provider.model.ComponentModel;
+import com.yahoo.vespa.model.container.ContainerCluster;
+
+import java.util.OptionalInt;
/**
* @author Tony Vaagenes
@@ -13,6 +16,7 @@ import com.yahoo.osgi.provider.model.ComponentModel;
*/
public final class AccessLogComponent extends SimpleComponent implements AccessLogConfig.Producer {
+
public enum AccessLogType { queryAccessLog, yApacheAccessLog, jsonAccessLog }
public enum CompressionType { GZIP, ZSTD }
@@ -22,10 +26,11 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL
private final boolean isHostedVespa;
private final String symlinkName;
private final CompressionType compressionType;
+ private final int queueSize;
- public AccessLogComponent(AccessLogType logType, CompressionType compressionType, String clusterName, boolean isHostedVespa)
+ public AccessLogComponent(ContainerCluster<?> cluster, AccessLogType logType, CompressionType compressionType, String clusterName, boolean isHostedVespa)
{
- this(logType, compressionType,
+ this(cluster, logType, compressionType,
String.format("logs/vespa/qrs/%s.%s.%s", capitalize(logType.name()), clusterName, "%Y%m%d%H%M%S"),
null, null, isHostedVespa,
capitalize(logType.name()) + "." + clusterName);
@@ -35,7 +40,8 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
- public AccessLogComponent(AccessLogType logType,
+ public AccessLogComponent(ContainerCluster<?> cluster,
+ AccessLogType logType,
CompressionType compressionType,
String fileNamePattern,
String rotationInterval,
@@ -50,11 +56,19 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL
this.isHostedVespa = isHostedVespa;
this.symlinkName = symlinkName;
this.compressionType = compressionType;
+ this.queueSize = queueSize(cluster).orElse(-1);
if (fileNamePattern == null)
throw new RuntimeException("File name pattern required when configuring access log.");
}
+ private static OptionalInt queueSize(ContainerCluster<?> cluster) {
+ if (cluster == null) return OptionalInt.empty();
+ double vcpu = cluster.vcpu().orElse(0);
+ if (vcpu <= 0) return OptionalInt.empty();
+ return OptionalInt.of((int) Math.max(4096, Math.ceil(vcpu * 256.0)));
+ }
+
private static String accessLogClass(AccessLogType logType) {
switch (logType) {
case queryAccessLog:
@@ -84,6 +98,9 @@ public final class AccessLogComponent extends SimpleComponent implements AccessL
} else if (isHostedVespa) {
builder.compressOnRotation(true);
}
+ if (queueSize >= 0) {
+ builder.queueSize(queueSize);
+ }
switch (compressionType) {
case GZIP:
builder.compressionFormat(CompressionFormat.GZIP);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java
index bf279c5c364..9977e955f85 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConnectionLogComponent.java
@@ -5,18 +5,37 @@ package com.yahoo.vespa.model.container.component;
import com.yahoo.container.logging.ConnectionLog;
import com.yahoo.container.logging.ConnectionLogConfig;
import com.yahoo.osgi.provider.model.ComponentModel;
+import com.yahoo.vespa.model.container.ContainerCluster;
+
+import java.util.OptionalInt;
public class ConnectionLogComponent extends SimpleComponent implements ConnectionLogConfig.Producer {
private final String clusterName;
+ private final int queueSize;
+
+ public ConnectionLogComponent(ContainerCluster<?> cluster, Class<? extends ConnectionLog> cls) {
+ this(cluster, cls, cluster.getName());
+ }
- public ConnectionLogComponent(Class<? extends ConnectionLog> cls, String clusterName) {
+ public ConnectionLogComponent(ContainerCluster<?> cluster, Class<? extends ConnectionLog> cls, String clusterName) {
super(new ComponentModel(cls.getName(), null, "jdisc_http_service", null));
this.clusterName = clusterName;
+ this.queueSize = queueSize(cluster).orElse(-1);
+ }
+
+ private static OptionalInt queueSize(ContainerCluster<?> cluster) {
+ if (cluster == null) return OptionalInt.empty();
+ double vcpu = cluster.vcpu().orElse(0);
+ if (vcpu <= 0) return OptionalInt.empty();
+ return OptionalInt.of((int) Math.max(4096, Math.ceil(vcpu * 512.0)));
}
@Override
public void getConfig(ConnectionLogConfig.Builder builder) {
builder.cluster(clusterName);
+ if (queueSize >= 0) {
+ builder.queueSize(queueSize);
+ }
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java
index 901c422f843..b6ebd73b442 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/JettyHttpServer.java
@@ -25,7 +25,7 @@ public class JettyHttpServer extends SimpleComponent implements ServerConfig.Pro
private final ContainerCluster<?> cluster;
private final boolean isHostedVespa;
private final List<ConnectorFactory> connectorFactories = new ArrayList<>();
- private final boolean enableJdiscConnectionLog;
+ private volatile boolean enableJdiscConnectionLog;
public JettyHttpServer(ComponentId id, ContainerCluster<?> cluster, ModelContext.FeatureFlags featureFlags, boolean isHostedVespa) {
super(new ComponentModel(
@@ -54,6 +54,8 @@ public class JettyHttpServer extends SimpleComponent implements ServerConfig.Pro
connectorFactories.remove(connectorFactory);
}
+ public void enableConnectionLog(boolean enabled) { this.enableJdiscConnectionLog = enabled; }
+
public List<ConnectorFactory> getConnectorFactories() {
return Collections.unmodifiableList(connectorFactories);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
index 936b9f1c851..81e96760684 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
@@ -46,19 +46,18 @@ public class AccessLogBuilder {
private static class DomBuilder extends VespaDomBuilder.DomConfigProducerBuilder<AccessLogComponent> {
private final AccessLogType accessLogType;
private final boolean isHostedVespa;
- private final boolean isConfigserver;
- public DomBuilder(AccessLogType accessLogType, boolean isHostedVespa, boolean isConfigserver) {
+ public DomBuilder(AccessLogType accessLogType, boolean isHostedVespa) {
this.accessLogType = accessLogType;
this.isHostedVespa = isHostedVespa;
- this.isConfigserver = isConfigserver;
}
@Override
protected AccessLogComponent doBuild(DeployState deployState, AbstractConfigProducer<?> ancestor, Element spec) {
return new AccessLogComponent(
+ (ContainerCluster<?>) ancestor,
accessLogType,
- compressionType(spec, deployState, isHostedVespa, isConfigserver),
+ compressionType(spec, deployState, isHostedVespa),
fileNamePattern(spec),
rotationInterval(spec),
compressOnRotation(spec),
@@ -83,14 +82,13 @@ public class AccessLogBuilder {
return nullIfEmpty(spec.getAttribute("fileNamePattern"));
}
- private static CompressionType compressionType(Element spec, DeployState deployState, boolean isHostedVespa, boolean isConfigserver) {
+ private static CompressionType compressionType(Element spec, DeployState deployState, boolean isHostedVespa) {
CompressionType fallback;
- if (isHostedVespa && (isConfigserver || deployState.featureFlags().enableZstdCompressionAccessLog())) {
+ if (isHostedVespa && deployState.featureFlags().enableZstdCompressionAccessLog()) {
fallback = CompressionType.ZSTD;
} else {
fallback = CompressionType.GZIP;
}
- if (isConfigserver && isHostedVespa) return CompressionType.ZSTD;
return Optional.ofNullable(spec.getAttribute("compressionType"))
.filter(value -> !value.isBlank())
.map(value -> {
@@ -120,7 +118,7 @@ public class AccessLogBuilder {
}
}
- public static Optional<AccessLogComponent> buildIfNotDisabled(DeployState deployState, ContainerCluster<?> cluster, Element accessLogSpec, boolean isConfigserver) {
+ public static Optional<AccessLogComponent> buildIfNotDisabled(DeployState deployState, ContainerCluster<?> cluster, Element accessLogSpec) {
AccessLogTypeLiteral typeLiteral =
getOptionalAttribute(accessLogSpec, "type").
map(AccessLogTypeLiteral::fromAttributeValue).
@@ -130,9 +128,6 @@ public class AccessLogBuilder {
return Optional.empty();
}
boolean hosted = cluster.isHostedVespa();
- if (hosted && isConfigserver && logType != AccessLogType.jsonAccessLog) {
- return Optional.empty(); // Only enable JSON access logging for hosted configserver/controller
- }
- return Optional.of(new DomBuilder(logType, hosted, isConfigserver).build(deployState, cluster, accessLogSpec));
+ return Optional.of(new DomBuilder(logType, hosted).build(deployState, cluster, accessLogSpec));
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java
index e7db4ab0564..0c5375edf71 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java
@@ -2,8 +2,12 @@
package com.yahoo.vespa.model.container.xml;
import com.yahoo.config.model.ConfigModelContext;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.container.logging.FileConnectionLog;
import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import com.yahoo.vespa.model.container.ContainerModel;
+import com.yahoo.vespa.model.container.component.AccessLogComponent;
+import com.yahoo.vespa.model.container.component.ConnectionLogComponent;
import com.yahoo.vespa.model.container.configserver.ConfigserverCluster;
import com.yahoo.vespa.model.container.configserver.option.CloudConfigOptions;
import org.w3c.dom.Element;
@@ -22,8 +26,6 @@ public class ConfigServerContainerModelBuilder extends ContainerModelBuilder {
this.options = options;
}
- @Override protected boolean isConfigserver() { return true; }
-
@Override
public void doBuild(ContainerModel model, Element spec, ConfigModelContext modelContext) {
ConfigserverCluster cluster = new ConfigserverCluster(modelContext.getParentProducer(), "configserver",
@@ -36,7 +38,31 @@ public class ConfigServerContainerModelBuilder extends ContainerModelBuilder {
// in ConfigModelContext.DeployState.properties are not set)
@Override
protected void addStatusHandlers(ApplicationContainerCluster cluster, boolean isHostedVespa) {
- super.addStatusHandlers(cluster, options.hostedVespa().orElse(Boolean.FALSE));
+ super.addStatusHandlers(cluster, isHosted());
+ }
+
+ // Override access log configuration for hosted configserver/controller
+ @Override
+ protected void addAccessLogs(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
+ if (isHosted()){
+ cluster.addComponent(
+ new AccessLogComponent(
+ cluster, AccessLogComponent.AccessLogType.jsonAccessLog, AccessLogComponent.CompressionType.ZSTD,
+ "logs/vespa/configserver/access-json.log.%Y%m%d%H%M%S", null, true, true, "access-json.log"));
+ cluster.addComponent(new ConnectionLogComponent(cluster, FileConnectionLog.class));
+ } else {
+ super.addAccessLogs(deployState, cluster, spec);
+ }
+ }
+
+ @Override
+ protected void addHttp(DeployState deployState, Element spec, ApplicationContainerCluster cluster, ConfigModelContext context) {
+ super.addHttp(deployState, spec, cluster, context);
+ if (isHosted()) {
+ cluster.getHttp().getHttpServer().get().enableConnectionLog(true);
+ }
}
+ /** Note: using {@link CloudConfigOptions} as {@link DeployState#isHosted()} returns <em>false</em> for hosted configserver/controller */
+ private boolean isHosted() { return options.hostedVespa().orElse(Boolean.FALSE); }
}
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 07ccf3808fd..8735b76100d 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
@@ -147,8 +147,6 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
this.httpServerEnabled = networking == Networking.enable;
}
- protected boolean isConfigserver() { return false; }
-
@Override
public List<ConfigModelId> handlesElements() {
return configModelIds;
@@ -340,11 +338,11 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
addConfiguredComponents(deployState, cluster, spec, "server");
}
- private void addAccessLogs(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
+ protected void addAccessLogs(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
List<Element> accessLogElements = getAccessLogElements(spec);
for (Element accessLog : accessLogElements) {
- AccessLogBuilder.buildIfNotDisabled(deployState, cluster, accessLog, isConfigserver()).ifPresent(cluster::addComponent);
+ AccessLogBuilder.buildIfNotDisabled(deployState, cluster, accessLog).ifPresent(cluster::addComponent);
}
if (accessLogElements.isEmpty() && deployState.getAccessLoggingEnabledByDefault())
@@ -352,9 +350,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
// Add connection log if access log is configured
if (cluster.getAllComponents().stream().anyMatch(component -> component instanceof AccessLogComponent)) {
- cluster.addComponent(new ConnectionLogComponent(FileConnectionLog.class, cluster.getName()));
+ cluster.addComponent(new ConnectionLogComponent(cluster, FileConnectionLog.class));
} else {
- cluster.addComponent(new ConnectionLogComponent(VoidConnectionLog.class, cluster.getName()));
+ cluster.addComponent(new ConnectionLogComponent(cluster, VoidConnectionLog.class));
}
}
@@ -363,7 +361,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
}
- private void addHttp(DeployState deployState, Element spec, ApplicationContainerCluster cluster, ConfigModelContext context) {
+ protected void addHttp(DeployState deployState, Element spec, ApplicationContainerCluster cluster, ConfigModelContext context) {
Element httpElement = XML.getChild(spec, "http");
if (httpElement != null) {
cluster.setHttp(buildHttp(deployState, cluster, httpElement));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java
index 8d6127970c8..66ec0d81947 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java
@@ -19,12 +19,14 @@ import org.w3c.dom.Element;
public class ClusterControllerConfig extends AbstractConfigProducer<ClusterControllerConfig> implements FleetcontrollerConfig.Producer {
public static class Builder extends VespaDomBuilder.DomConfigProducerBuilder<ClusterControllerConfig> {
- String clusterName;
- ModelElement clusterElement;
+ private final String clusterName;
+ private final ModelElement clusterElement;
+ private final ResourceLimits resourceLimits;
- public Builder(String clusterName, ModelElement clusterElement) {
+ public Builder(String clusterName, ModelElement clusterElement, ResourceLimits resourceLimits) {
this.clusterName = clusterName;
this.clusterElement = clusterElement;
+ this.resourceLimits = resourceLimits;
}
@Override
@@ -51,27 +53,29 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
tuning.childAsDouble("min-storage-up-ratio"),
bucketSplittingMinimumBits,
minNodeRatioPerGroup,
- enableClusterFeedBlock);
+ enableClusterFeedBlock,
+ resourceLimits);
} else {
return new ClusterControllerConfig(ancestor, clusterName,
null, null, null, null, null, null,
bucketSplittingMinimumBits,
minNodeRatioPerGroup,
- enableClusterFeedBlock);
+ enableClusterFeedBlock, resourceLimits);
}
}
}
- String clusterName;
- Duration initProgressTime;
- Duration transitionTime;
- Long maxPrematureCrashes;
- Duration stableStateTimePeriod;
- Double minDistributorUpRatio;
- Double minStorageUpRatio;
- Integer minSplitBits;
- private Double minNodeRatioPerGroup;
- private boolean enableClusterFeedBlock = false;
+ private final String clusterName;
+ private final Duration initProgressTime;
+ private final Duration transitionTime;
+ private final Long maxPrematureCrashes;
+ private final Duration stableStateTimePeriod;
+ private final Double minDistributorUpRatio;
+ private final Double minStorageUpRatio;
+ private final Integer minSplitBits;
+ private final Double minNodeRatioPerGroup;
+ private final boolean enableClusterFeedBlock;
+ private final ResourceLimits resourceLimits;
// TODO refactor; too many args
private ClusterControllerConfig(AbstractConfigProducer parent,
@@ -84,7 +88,8 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
Double minStorageUpRatio,
Integer minSplitBits,
Double minNodeRatioPerGroup,
- boolean enableClusterFeedBlock) {
+ boolean enableClusterFeedBlock,
+ ResourceLimits resourceLimits) {
super(parent, "fleetcontroller");
this.clusterName = clusterName;
@@ -97,6 +102,7 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
this.minSplitBits = minSplitBits;
this.minNodeRatioPerGroup = minNodeRatioPerGroup;
this.enableClusterFeedBlock = enableClusterFeedBlock;
+ this.resourceLimits = resourceLimits;
}
@Override
@@ -139,18 +145,7 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
builder.min_node_ratio_per_group(minNodeRatioPerGroup);
}
builder.enable_cluster_feed_block(enableClusterFeedBlock);
- setDefaultClusterFeedBlockLimits(builder);
+ resourceLimits.getConfig(builder);
}
- private static void setDefaultClusterFeedBlockLimits(FleetcontrollerConfig.Builder builder) {
- // TODO: Override these based on resource-limits in services.xml (if they are specified).
- // TODO: Choose other defaults when this is default enabled.
- // Note: The resource categories must match the ones used in host info reporting
- // between content nodes and cluster controller:
- // storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp
- builder.cluster_feed_block_limit.put("memory", 0.79);
- builder.cluster_feed_block_limit.put("disk", 0.79);
- builder.cluster_feed_block_limit.put("attribute-enum-store", 0.89);
- builder.cluster_feed_block_limit.put("attribute-multi-value", 0.89);
- }
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java
new file mode 100644
index 00000000000..5324ee171ec
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java
@@ -0,0 +1,103 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.content;
+
+import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
+import com.yahoo.vespa.model.content.cluster.DomResourceLimitsBuilder;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Class tracking the feed block resource limits for a content cluster.
+ *
+ * This includes the limits used by the cluster controller and the content nodes (proton).
+ *
+ * @author geirst
+ */
+public class ClusterResourceLimits {
+
+ private final ResourceLimits clusterControllerLimits;
+ private final ResourceLimits contentNodeLimits;
+
+ private ClusterResourceLimits(Builder builder) {
+ clusterControllerLimits = builder.ctrlBuilder.build();
+ contentNodeLimits = builder.nodeBuilder.build();
+ }
+
+ public ResourceLimits getClusterControllerLimits() {
+ return clusterControllerLimits;
+ }
+
+ public ResourceLimits getContentNodeLimits() {
+ return contentNodeLimits;
+ }
+
+ public static class Builder {
+
+ private ResourceLimits.Builder ctrlBuilder = new ResourceLimits.Builder();
+ private ResourceLimits.Builder nodeBuilder = new ResourceLimits.Builder();
+
+ public ClusterResourceLimits build(ModelElement clusterElem) {
+
+ ModelElement tuningElem = clusterElem.childByPath("tuning");
+ if (tuningElem != null) {
+ ctrlBuilder = DomResourceLimitsBuilder.createBuilder(tuningElem);
+ }
+
+ ModelElement protonElem = clusterElem.childByPath("engine.proton");
+ if (protonElem != null) {
+ nodeBuilder = DomResourceLimitsBuilder.createBuilder(protonElem);
+ }
+
+ deriveLimits();
+ return new ClusterResourceLimits(this);
+ }
+
+ public void setClusterControllerBuilder(ResourceLimits.Builder builder) {
+ ctrlBuilder = builder;
+ }
+
+ public void setContentNodeBuilder(ResourceLimits.Builder builder) {
+ nodeBuilder = builder;
+ }
+
+ public ClusterResourceLimits build() {
+ deriveLimits();
+ return new ClusterResourceLimits(this);
+ }
+
+ private void deriveLimits() {
+ deriveClusterControllerLimit(ctrlBuilder.getDiskLimit(), nodeBuilder.getDiskLimit(), ctrlBuilder::setDiskLimit);
+ deriveClusterControllerLimit(ctrlBuilder.getMemoryLimit(), nodeBuilder.getMemoryLimit(), ctrlBuilder::setMemoryLimit);
+
+ deriveContentNodeLimit(nodeBuilder.getDiskLimit(), ctrlBuilder.getDiskLimit(), nodeBuilder::setDiskLimit);
+ deriveContentNodeLimit(nodeBuilder.getMemoryLimit(), ctrlBuilder.getMemoryLimit(), nodeBuilder::setMemoryLimit);
+ }
+
+ private void deriveClusterControllerLimit(Optional<Double> clusterControllerLimit,
+ Optional<Double> contentNodeLimit,
+ Consumer<Double> setter) {
+ if (!clusterControllerLimit.isPresent()) {
+ contentNodeLimit.ifPresent(limit ->
+ // TODO: emit warning when using cluster controller resource limits are default enabled.
+ setter.accept(limit));
+ }
+ }
+
+ private void deriveContentNodeLimit(Optional<Double> contentNodeLimit,
+ Optional<Double> clusterControllerLimit,
+ Consumer<Double> setter) {
+ if (!contentNodeLimit.isPresent()) {
+ clusterControllerLimit.ifPresent(limit ->
+ setter.accept(calcContentNodeLimit(limit)));
+ }
+ }
+
+ private double calcContentNodeLimit(double clusterControllerLimit) {
+ // Note that validation in the range [0.0-1.0] is handled by the rnc schema.
+ return clusterControllerLimit + ((1.0 - clusterControllerLimit) / 2);
+ }
+
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
index dd29df61f35..7ce72b138c1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
@@ -79,13 +79,15 @@ public class ContentSearchCluster extends AbstractConfigProducer<SearchCluster>
private final Map<String, NewDocumentType> documentDefinitions;
private final Set<NewDocumentType> globallyDistributedDocuments;
private final boolean combined;
+ private final ResourceLimits resourceLimits;
public Builder(Map<String, NewDocumentType> documentDefinitions,
Set<NewDocumentType> globallyDistributedDocuments,
- boolean combined) {
+ boolean combined, ResourceLimits resourceLimits) {
this.documentDefinitions = documentDefinitions;
this.globallyDistributedDocuments = globallyDistributedDocuments;
this.combined = combined;
+ this.resourceLimits = resourceLimits;
}
@Override
@@ -106,10 +108,7 @@ public class ContentSearchCluster extends AbstractConfigProducer<SearchCluster>
if (tuning != null) {
search.setTuning(new DomSearchTuningBuilder().build(deployState, search, tuning.getXml()));
}
- ModelElement protonElem = clusterElem.childByPath("engine.proton");
- if (protonElem != null) {
- search.setResourceLimits(DomResourceLimitsBuilder.build(protonElem));
- }
+ search.setResourceLimits(resourceLimits);
buildAllStreamingSearchClusters(deployState, clusterElem, clusterName, search);
buildIndexedSearchCluster(deployState, clusterElem, clusterName, search);
@@ -190,7 +189,7 @@ public class ContentSearchCluster extends AbstractConfigProducer<SearchCluster>
}
}
- private ContentSearchCluster(AbstractConfigProducer parent,
+ private ContentSearchCluster(AbstractConfigProducer<?> parent,
String clusterName,
ModelContext.FeatureFlags featureFlags,
Map<String, NewDocumentType> documentDefinitions,
@@ -233,8 +232,8 @@ public class ContentSearchCluster extends AbstractConfigProducer<SearchCluster>
NamedSchema searchDefinition =
schemaDefinitionXMLHandler.getResponsibleSearchDefinition(deployState.getSchemas());
if (searchDefinition == null)
- throw new RuntimeException("Search definition parsing error or file does not exist: '" +
- schemaDefinitionXMLHandler.getName() + "'");
+ throw new RuntimeException("Schema '" + schemaDefinitionXMLHandler.getName() + "' referenced in " +
+ this + " does not exist");
// TODO: remove explicit building of user configs when the complete content model is built using builders.
sc.getLocalSDS().add(new AbstractSearchCluster.SchemaSpec(searchDefinition,
@@ -445,4 +444,8 @@ public class ContentSearchCluster extends AbstractConfigProducer<SearchCluster>
public IndexedSearchCluster getIndexed() { return indexedCluster; }
public boolean hasIndexedCluster() { return indexedCluster != null; }
public String getClusterName() { return clusterName; }
+
+ @Override
+ public String toString() { return "content cluster '" + clusterName + "'"; }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
index 28e8c36d202..e96ba47c6b3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
@@ -1,16 +1,17 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
+import com.yahoo.vespa.config.content.FleetcontrollerConfig;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import java.util.Optional;
/**
- * Class tracking resource limits for a content cluster with engine proton.
+ * Class tracking feed block resource limits used by a component in a content cluster (e.g. cluster controller or content node).
*
* @author geirst
*/
-public class ResourceLimits implements ProtonConfig.Producer {
+public class ResourceLimits implements FleetcontrollerConfig.Producer, ProtonConfig.Producer {
private final Optional<Double> diskLimit;
private final Optional<Double> memoryLimit;
@@ -20,6 +21,26 @@ public class ResourceLimits implements ProtonConfig.Producer {
this.memoryLimit = builder.memoryLimit;
}
+ public Optional<Double> getDiskLimit() {
+ return diskLimit;
+ }
+
+ public Optional<Double> getMemoryLimit() {
+ return memoryLimit;
+ }
+
+ @Override
+ public void getConfig(FleetcontrollerConfig.Builder builder) {
+ // TODO: Choose other defaults when this is default enabled.
+ // Note: The resource categories must match the ones used in host info reporting
+ // between content nodes and cluster controller:
+ // storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp
+ builder.cluster_feed_block_limit.put("memory", memoryLimit.orElse(0.79));
+ builder.cluster_feed_block_limit.put("disk", diskLimit.orElse(0.79));
+ builder.cluster_feed_block_limit.put("attribute-enum-store", 0.89);
+ builder.cluster_feed_block_limit.put("attribute-multi-value", 0.89);
+ }
+
@Override
public void getConfig(ProtonConfig.Builder builder) {
if (diskLimit.isPresent()) {
@@ -39,11 +60,19 @@ public class ResourceLimits implements ProtonConfig.Producer {
return new ResourceLimits(this);
}
+ public Optional<Double> getDiskLimit() {
+ return diskLimit;
+ }
+
public Builder setDiskLimit(double diskLimit) {
this.diskLimit = Optional.of(diskLimit);
return this;
}
+ public Optional<Double> getMemoryLimit() {
+ return memoryLimit;
+ }
+
public Builder setMemoryLimit(double memoryLimit) {
this.memoryLimit = Optional.of(memoryLimit);
return this;
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 a627e030156..44de4a1abec 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
@@ -38,6 +38,7 @@ import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.ContainerModel;
import com.yahoo.vespa.model.content.ClusterControllerConfig;
+import com.yahoo.vespa.model.content.ClusterResourceLimits;
import com.yahoo.vespa.model.content.ContentSearch;
import com.yahoo.vespa.model.content.ContentSearchCluster;
import com.yahoo.vespa.model.content.DistributionBitCalculator;
@@ -134,11 +135,14 @@ public class ContentCluster extends AbstractConfigProducer implements
ContentCluster c = new ContentCluster(context.getParentProducer(), getClusterId(contentElement), documentDefinitions,
globallyDistributedDocuments, routingSelection,
deployState.zone(), deployState.isHosted());
- c.clusterControllerConfig = new ClusterControllerConfig.Builder(getClusterId(contentElement), contentElement).build(deployState, c, contentElement.getXml());
+ var resourceLimits = new ClusterResourceLimits.Builder().build(contentElement);
+ c.clusterControllerConfig = new ClusterControllerConfig.Builder(getClusterId(contentElement),
+ contentElement,
+ resourceLimits.getClusterControllerLimits()).build(deployState, c, contentElement.getXml());
c.search = new ContentSearchCluster.Builder(documentDefinitions,
- globallyDistributedDocuments,
- isCombined(getClusterId(contentElement), containers))
- .build(deployState, c, contentElement.getXml());
+ globallyDistributedDocuments,
+ isCombined(getClusterId(contentElement), containers),
+ resourceLimits.getContentNodeLimits()).build(deployState, c, contentElement.getXml());
c.persistenceFactory = new EngineFactoryBuilder().build(contentElement, c);
c.storageNodes = new StorageCluster.Builder().build(deployState, c, w3cContentElement);
c.distributorNodes = new DistributorCluster.Builder(c).build(deployState, c, w3cContentElement);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
index 8e91f14238e..210f062f9b2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
@@ -5,17 +5,17 @@ import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import com.yahoo.vespa.model.content.ResourceLimits;
/**
- * Builder for resource limits for a content cluster with engine proton.
+ * Builder for feed block resource limits.
*
* @author geirst
*/
public class DomResourceLimitsBuilder {
- public static ResourceLimits build(ModelElement contentXml) {
+ public static ResourceLimits.Builder createBuilder(ModelElement contentXml) {
ResourceLimits.Builder builder = new ResourceLimits.Builder();
ModelElement resourceLimits = contentXml.child("resource-limits");
if (resourceLimits == null) {
- return builder.build();
+ return builder;
}
if (resourceLimits.child("disk") != null) {
builder.setDiskLimit(resourceLimits.childAsDouble("disk"));
@@ -23,7 +23,7 @@ public class DomResourceLimitsBuilder {
if (resourceLimits.child("memory") != null) {
builder.setMemoryLimit(resourceLimits.childAsDouble("memory"));
}
- return builder.build();
+ return builder;
}
}
diff --git a/config-model/src/main/resources/schema/content.rnc b/config-model/src/main/resources/schema/content.rnc
index 5646bc72056..a48d38b9f2c 100644
--- a/config-model/src/main/resources/schema/content.rnc
+++ b/config-model/src/main/resources/schema/content.rnc
@@ -98,7 +98,8 @@ ClusterTuning = element tuning {
ClusterControllerTuning? &
Maintenance? &
PersistenceThreads? &
- MinNodeRatioPerGroup?
+ MinNodeRatioPerGroup? &
+ ResourceLimits?
}
Content = element content {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java
index d3c41754c8d..6ad74231cae 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java
@@ -47,7 +47,7 @@ public class QuotaValidatorTest {
tester.deploy(null, getServices("testCluster", 10), Environment.prod, null);
fail();
} catch (RuntimeException e) {
- assertEquals("Hourly spend for maximum specified resources ($-.--) exceeds budget from quota ($-.--)!",
+ assertEquals("Please free up some capacity! This deployment's quota use ($-.--) exceeds reserved quota ($-.--)!",
ValidationTester.censorNumbers(e.getMessage()));
}
}
@@ -59,7 +59,20 @@ public class QuotaValidatorTest {
tester.deploy(null, getServices("testCluster", 10), Environment.prod, null);
fail();
} catch (RuntimeException e) {
- assertEquals("publiccd: Hourly spend for maximum specified resources ($-.--) exceeds budget from quota ($-.--)!",
+ assertEquals("publiccd: Please free up some capacity! This deployment's quota use ($-.--) exceeds reserved quota ($-.--)!",
+ ValidationTester.censorNumbers(e.getMessage()));
+ }
+ }
+
+ @Test
+ public void test_deploy_with_negative_budget() {
+ var quota = Quota.unlimited().withBudget(BigDecimal.valueOf(-1));
+ var tester = new ValidationTester(10, false, new TestProperties().setHostedVespa(true).setQuota(quota).setZone(publicZone));
+ try {
+ tester.deploy(null, getServices("testCluster", 10), Environment.prod, null);
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals("Please free up some capacity! This deployment's quota use is ($-.--) and reserved quota is below zero! ($--.--)",
ValidationTester.censorNumbers(e.getMessage()));
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java
index ecf026e7d88..180e4913d5c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java
@@ -61,7 +61,7 @@ public class NodeResourceChangeValidatorTest {
Clock.systemUTC().instant());
}
- private static VespaModel model(int mem1, int mem2, int mem3, int mem4) {
+ private static VespaModel model(int mem1, int mem2, int cpu1, int cpu2) {
var properties = new TestProperties();
properties.setHostedVespa(true);
var deployState = new DeployState.Builder().properties(properties)
@@ -82,7 +82,7 @@ public class NodeResourceChangeValidatorTest {
" </container>\n" +
" <content id='content1' version='1.0'>\n" +
" <nodes count='3'>\n" +
- " <resources vcpu='1' memory='" + mem3 + "Gb' disk='100Gb'/>" +
+ " <resources vcpu='" + cpu1 + "' memory='8Gb' disk='100Gb'/>" +
" </nodes>\n" +
" <documents>\n" +
" <document mode='index' type='test'/>\n" +
@@ -91,7 +91,7 @@ public class NodeResourceChangeValidatorTest {
" </content>\n" +
" <content id='content2' version='1.0'>\n" +
" <nodes count='4'>\n" +
- " <resources vcpu='1' memory='" + mem4 + "Gb' disk='100Gb'/>" +
+ " <resources vcpu='" + cpu2 + "' memory='8Gb' disk='100Gb'/>" +
" </nodes>\n" +
" <documents>\n" +
" <document mode='streaming' type='test'/>\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java
index 1d2dd94d6a3..56f09eefe82 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java
@@ -86,6 +86,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase {
AccessLogConfig.FileHandler fileHandlerConfig = config.fileHandler();
assertEquals("pattern", fileHandlerConfig.pattern());
assertEquals("interval", fileHandlerConfig.rotation());
+ assertEquals(10000, fileHandlerConfig.queueSize());
}
{ // json
@@ -96,6 +97,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase {
AccessLogConfig.FileHandler fileHandlerConfig = config.fileHandler();
assertEquals("pattern", fileHandlerConfig.pattern());
assertEquals("interval", fileHandlerConfig.rotation());
+ assertEquals(10000, fileHandlerConfig.queueSize());
}
}
@@ -114,6 +116,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase {
assertNotNull(connectionLogComponent);
ConnectionLogConfig config = root.getConfig(ConnectionLogConfig.class, "default/component/com.yahoo.container.logging.FileConnectionLog");
assertEquals("default", config.cluster());
+ assertEquals(10000, config.queueSize());
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java
index 294df42bd77..60af25a3087 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java
@@ -78,11 +78,11 @@ public class JvmOptionsTest extends ContainerModelBuilderTestBase {
}
private static void verifyIgnoreJvmGCOptions(boolean isHosted) throws IOException, SAXException {
- verifyIgnoreJvmGCOptionsIfJvmArgs("jvmargs", ContainerCluster.G1GC);
- verifyIgnoreJvmGCOptionsIfJvmArgs( "jvm-options", "-XX:+UseG1GC");
+ verifyIgnoreJvmGCOptionsIfJvmArgs("jvmargs", ContainerCluster.G1GC, isHosted);
+ verifyIgnoreJvmGCOptionsIfJvmArgs( "jvm-options", "-XX:+UseG1GC", isHosted);
}
- private static void verifyIgnoreJvmGCOptionsIfJvmArgs(String jvmOptionsName, String expectedGC) throws IOException, SAXException {
+ private static void verifyIgnoreJvmGCOptionsIfJvmArgs(String jvmOptionsName, String expectedGC, boolean isHosted) throws IOException, SAXException {
String servicesXml =
"<container version='1.0'>" +
" <nodes jvm-gc-options='-XX:+UseG1GC' " + jvmOptionsName + "='-XX:+UseParNewGC'>" +
@@ -95,6 +95,7 @@ public class JvmOptionsTest extends ContainerModelBuilderTestBase {
VespaModel model = new VespaModel(new NullConfigModelRegistry(), new DeployState.Builder()
.applicationPackage(applicationPackage)
.deployLogger(logger)
+ .properties(new TestProperties().setHostedVespa(isHosted))
.build());
QrStartConfig.Builder qrStartBuilder = new QrStartConfig.Builder();
model.getConfig(qrStartBuilder, "container/container.0");
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java
new file mode 100644
index 00000000000..bc830c079d0
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java
@@ -0,0 +1,101 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.content;
+
+import org.junit.Test;
+
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * @author geirst
+ */
+public class ClusterResourceLimitsTest {
+
+ private static class Fixture {
+ ResourceLimits.Builder ctrlBuilder = new ResourceLimits.Builder();
+ ResourceLimits.Builder nodeBuilder = new ResourceLimits.Builder();
+
+ public Fixture ctrlDisk(double limit) {
+ ctrlBuilder.setDiskLimit(limit);
+ return this;
+ }
+ public Fixture ctrlMemory(double limit) {
+ ctrlBuilder.setMemoryLimit(limit);
+ return this;
+ }
+ public Fixture nodeDisk(double limit) {
+ nodeBuilder.setDiskLimit(limit);
+ return this;
+ }
+ public Fixture nodeMemory(double limit) {
+ nodeBuilder.setMemoryLimit(limit);
+ return this;
+ }
+ public ClusterResourceLimits build() {
+ var builder = new ClusterResourceLimits.Builder();
+ builder.setClusterControllerBuilder(ctrlBuilder);
+ builder.setContentNodeBuilder(nodeBuilder);
+ return builder.build();
+ }
+ }
+
+ @Test
+ public void content_node_limits_are_derived_from_cluster_controller_limits_if_not_set() {
+ assertLimits(0.6, 0.7, 0.8, 0.85,
+ new Fixture().ctrlDisk(0.6).ctrlMemory(0.7));
+ assertLimits(0.6, null, 0.8, null,
+ new Fixture().ctrlDisk(0.6));
+ assertLimits(null, 0.7, null, 0.85,
+ new Fixture().ctrlMemory(0.7));
+ }
+
+ @Test
+ public void content_node_limits_can_be_set_explicit() {
+ assertLimits(0.6, 0.7, 0.9, 0.95,
+ new Fixture().ctrlDisk(0.6).ctrlMemory(0.7).nodeDisk(0.9).nodeMemory(0.95));
+ assertLimits(0.6, null, 0.9, null,
+ new Fixture().ctrlDisk(0.6).nodeDisk(0.9));
+ assertLimits(null, 0.7, null, 0.95,
+ new Fixture().ctrlMemory(0.7).nodeMemory(0.95));
+ }
+
+ @Test
+ public void cluster_controller_limits_are_equal_to_content_node_limits_if_not_set() {
+ assertLimits(0.9, 0.95, 0.9, 0.95,
+ new Fixture().nodeDisk(0.9).nodeMemory(0.95));
+ assertLimits(0.9, null, 0.9, null,
+ new Fixture().nodeDisk(0.9));
+ assertLimits(null, 0.95, null, 0.95,
+ new Fixture().nodeMemory(0.95));
+ }
+
+ @Test
+ public void limits_are_derived_from_the_other_if_not_set() {
+ assertLimits(0.6, 0.95, 0.8, 0.95,
+ new Fixture().ctrlDisk(0.6).nodeMemory(0.95));
+ assertLimits(0.9, 0.7, 0.9, 0.85,
+ new Fixture().ctrlMemory(0.7).nodeDisk(0.9));
+ }
+
+ private void assertLimits(Double expCtrlDisk, Double expCtrlMemory, Double expNodeDisk, Double expNodeMemory, Fixture f) {
+ var limits = f.build();
+ assertLimits(expCtrlDisk, expCtrlMemory, limits.getClusterControllerLimits());
+ assertLimits(expNodeDisk, expNodeMemory, limits.getContentNodeLimits());
+ }
+
+ private void assertLimits(Double expDisk, Double expMemory, ResourceLimits limits) {
+ assertLimit(expDisk, limits.getDiskLimit());
+ assertLimit(expMemory, limits.getMemoryLimit());
+ }
+
+ private void assertLimit(Double expLimit, Optional<Double> actLimit) {
+ if (expLimit == null) {
+ assertFalse(actLimit.isPresent());
+ } else {
+ assertEquals(expLimit, actLimit.get(), 0.00001);
+ }
+ }
+
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
index 3415044b088..bc60908e268 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
@@ -69,15 +69,30 @@ public class ContentSearchClusterTest {
}
private static ProtonConfig getProtonConfig(ContentCluster cluster) {
- ProtonConfig.Builder protonCfgBuilder = new ProtonConfig.Builder();
- cluster.getSearch().getConfig(protonCfgBuilder);
- return new ProtonConfig(protonCfgBuilder);
+ var builder = new ProtonConfig.Builder();
+ cluster.getSearch().getConfig(builder);
+ return new ProtonConfig(builder);
}
- private static void assertProtonResourceLimits(double expDiskLimit, double expMemoryLimits, String clusterXml) throws Exception {
- ProtonConfig cfg = getProtonConfig(createCluster(clusterXml));
+ private static void assertProtonResourceLimits(double expDiskLimit, double expMemoryLimit, String clusterXml) throws Exception {
+ assertProtonResourceLimits(expDiskLimit, expMemoryLimit, createCluster(clusterXml));
+ }
+
+ private static void assertProtonResourceLimits(double expDiskLimit, double expMemoryLimit, ContentCluster cluster) {
+ var cfg = getProtonConfig(cluster);
assertEquals(expDiskLimit, cfg.writefilter().disklimit(), EPSILON);
- assertEquals(expMemoryLimits, cfg.writefilter().memorylimit(), EPSILON);
+ assertEquals(expMemoryLimit, cfg.writefilter().memorylimit(), EPSILON);
+ }
+
+ private static void assertClusterControllerResourceLimits(double expDiskLimit, double expMemoryLimit, String clusterXml) throws Exception {
+ assertClusterControllerResourceLimits(expDiskLimit, expMemoryLimit, createCluster(clusterXml));
+ }
+
+ private static void assertClusterControllerResourceLimits(double expDiskLimit, double expMemoryLimit, ContentCluster cluster) {
+ var limits = getFleetcontrollerConfig(cluster).cluster_feed_block_limit();
+ assertEquals(4, limits.size());
+ assertEquals(expDiskLimit, limits.get("disk"), EPSILON);
+ assertEquals(expMemoryLimit, limits.get("memory"), EPSILON);
}
@Test
@@ -105,6 +120,19 @@ public class ContentSearchClusterTest {
}
@Test
+ public void cluster_controller_resource_limits_can_be_set() throws Exception {
+ assertClusterControllerResourceLimits(0.92, 0.93,
+ new ContentClusterBuilder().clusterControllerDiskLimit(0.92).clusterControllerMemoryLimit(0.93).getXml());
+ }
+
+ @Test
+ public void resource_limits_are_derived_from_the_other_if_not_specified() throws Exception {
+ var cluster = createCluster(new ContentClusterBuilder().clusterControllerDiskLimit(0.5).protonMemoryLimit(0.95).getXml());
+ assertProtonResourceLimits(0.75, 0.95, cluster);
+ assertClusterControllerResourceLimits(0.5, 0.95, cluster);
+ }
+
+ @Test
public void requireThatGloballyDistributedDocumentTypeIsTaggedAsSuch() throws Exception {
ProtonConfig cfg = getProtonConfig(createClusterWithGlobalType());
assertEquals(2, cfg.documentdb().size());
@@ -149,8 +177,9 @@ public class ContentSearchClusterTest {
}
private static FleetcontrollerConfig getFleetcontrollerConfig(ContentCluster cluster) {
- FleetcontrollerConfig.Builder builder = new FleetcontrollerConfig.Builder();
+ var builder = new FleetcontrollerConfig.Builder();
cluster.getConfig(builder);
+ cluster.getClusterControllerConfig().getConfig(builder);
builder.cluster_name("unknown");
builder.index(0);
builder.zookeeper_server("unknown");
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
index 01bbffce360..3a59f35ce2e 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
@@ -10,6 +10,7 @@ import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import org.junit.Test;
import org.w3c.dom.Document;
+import static com.yahoo.config.model.test.TestUtil.joinLines;
import static org.junit.Assert.assertEquals;
public class FleetControllerClusterTest {
@@ -19,8 +20,11 @@ public class FleetControllerClusterTest {
var deployState = new DeployState.Builder().properties(
new TestProperties().enableFeedBlockInDistributor(enableFeedBlockInDistributor)).build();
MockRoot root = new MockRoot("", deployState);
- return new ClusterControllerConfig.Builder("storage", new ModelElement(doc.getDocumentElement())).build(root.getDeployState(), root,
- new ModelElement(doc.getDocumentElement()).getXml());
+ var clusterElement = new ModelElement(doc.getDocumentElement());
+ return new ClusterControllerConfig.Builder("storage",
+ clusterElement,
+ new ClusterResourceLimits.Builder().build(clusterElement).getClusterControllerLimits()).
+ build(root.getDeployState(), root, clusterElement.getXml());
}
private ClusterControllerConfig parse(String xml) {
@@ -94,15 +98,43 @@ public class FleetControllerClusterTest {
assertEquals(0.0, config.min_node_ratio_per_group(), 0.01);
}
+
@Test
public void default_cluster_feed_block_limits_are_set() {
- var config = getConfigForBasicCluster();
+ assertLimits(0.79, 0.79, getConfigForBasicCluster());
+ }
+
+ @Test
+ public void resource_limits_can_be_set_in_tuning() {
+ assertLimits(0.6, 0.7, getConfigForResourceLimitsTuning(0.6, 0.7));
+ assertLimits(0.6, 0.79, getConfigForResourceLimitsTuning(0.6, null));
+ assertLimits(0.79, 0.7, getConfigForResourceLimitsTuning(null, 0.7));
+ }
+
+ private static double DELTA = 0.00001;
+
+ private void assertLimits(double expDisk, double expMemory, FleetcontrollerConfig config) {
var limits = config.cluster_feed_block_limit();
assertEquals(4, limits.size());
- assertEquals(0.79, limits.get("memory"), 0.0001);
- assertEquals(0.79, limits.get("disk"), 0.0001);
- assertEquals(0.89, limits.get("attribute-enum-store"), 0.0001);
- assertEquals(0.89, limits.get("attribute-multi-value"), 0.0001);
+ assertEquals(expDisk, limits.get("disk"), DELTA);
+ assertEquals(expMemory, limits.get("memory"), DELTA);
+ assertEquals(0.89, limits.get("attribute-enum-store"), DELTA);
+ assertEquals(0.89, limits.get("attribute-multi-value"), DELTA);
+ }
+
+ private FleetcontrollerConfig getConfigForResourceLimitsTuning(Double diskLimit, Double memoryLimit) {
+ FleetcontrollerConfig.Builder builder = new FleetcontrollerConfig.Builder();
+ parse(joinLines("<cluster id=\"test\">",
+ "<documents/>",
+ "<tuning>",
+ " <resource-limits>",
+ (diskLimit != null ? (" <disk>" + diskLimit + "</disk>") : ""),
+ (memoryLimit != null ? (" <memory>" + memoryLimit + "</memory>") : ""),
+ " </resource-limits>",
+ "</tuning>" +
+ "</cluster>")).
+ getConfig(builder);
+ return new FleetcontrollerConfig(builder);
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
index 866c03d82f0..491326fdc9c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
@@ -26,6 +26,8 @@ public class ContentClusterBuilder {
private Optional<String> dispatchXml = Optional.empty();
private Optional<Double> protonDiskLimit = Optional.empty();
private Optional<Double> protonMemoryLimit = Optional.empty();
+ private Optional<Double> clusterControllerDiskLimit = Optional.empty();
+ private Optional<Double> clusterControllerMemoryLimit = Optional.empty();
public ContentClusterBuilder() {
}
@@ -67,13 +69,23 @@ public class ContentClusterBuilder {
return this;
}
- public ContentClusterBuilder protonDiskLimit(double diskLimit) {
- protonDiskLimit = Optional.of(diskLimit);
+ public ContentClusterBuilder protonDiskLimit(double limit) {
+ protonDiskLimit = Optional.of(limit);
return this;
}
- public ContentClusterBuilder protonMemoryLimit(double memoryLimit) {
- protonMemoryLimit = Optional.of(memoryLimit);
+ public ContentClusterBuilder protonMemoryLimit(double limit) {
+ protonMemoryLimit = Optional.of(limit);
+ return this;
+ }
+
+ public ContentClusterBuilder clusterControllerDiskLimit(double limit) {
+ clusterControllerDiskLimit = Optional.of(limit);
+ return this;
+ }
+
+ public ContentClusterBuilder clusterControllerMemoryLimit(double limit) {
+ clusterControllerMemoryLimit = Optional.of(limit);
return this;
}
@@ -88,14 +100,17 @@ public class ContentClusterBuilder {
" <engine>",
" <proton>",
" <searchable-copies>" + searchableCopies + "</searchable-copies>",
- getResourceLimitsXml(" "),
+ getProtonResourceLimitsXml(" "),
" </proton>",
" </engine>");
if (dispatchXml.isPresent()) {
xml += dispatchXml.get();
}
- return xml + groupXml +
- "</content>";
+ xml += groupXml;
+ xml += joinLines(" <tuning>",
+ getTuningResourceLimitsXml(" "),
+ " </tuning>");
+ return xml + "</content>";
}
private static String getSimpleGroupXml() {
@@ -104,11 +119,19 @@ public class ContentClusterBuilder {
" </group>");
}
- private String getResourceLimitsXml(String indent) {
- if (protonDiskLimit.isPresent() || protonMemoryLimit.isPresent()) {
+ private String getProtonResourceLimitsXml(String indent) {
+ return getResourceLimitsXml(indent, protonDiskLimit, protonMemoryLimit);
+ }
+
+ private String getTuningResourceLimitsXml(String indent) {
+ return getResourceLimitsXml(indent, clusterControllerDiskLimit, clusterControllerMemoryLimit);
+ }
+
+ private String getResourceLimitsXml(String indent, Optional<Double> diskLimit, Optional<Double> memoryLimit) {
+ if (diskLimit.isPresent() || memoryLimit.isPresent()) {
String xml = joinLines(indent + "<resource-limits>",
- getXmlLine("disk", protonDiskLimit, indent + " "),
- getXmlLine("memory", protonMemoryLimit, indent + " "),
+ getXmlLine("disk", diskLimit, indent + " "),
+ getXmlLine("memory", memoryLimit, indent + " "),
indent + "</resource-limits>");
return xml;
}