diff options
Diffstat (limited to 'config-model')
64 files changed, 581 insertions, 317 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java index abd7ba3be9f..c88c1cdfc48 100644 --- a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java +++ b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java @@ -1,22 +1,22 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.config.model.test; +import com.yahoo.component.Version; +import com.yahoo.config.application.api.ApplicationFile; import com.yahoo.config.application.api.ApplicationMetaData; +import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.application.api.ComponentInfo; import com.yahoo.config.application.api.UnparsedConfigDefinition; -import com.yahoo.config.application.api.ApplicationFile; -import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.TenantName; import com.yahoo.io.IOUtils; -import com.yahoo.path.Path; import com.yahoo.io.reader.NamedReader; +import com.yahoo.path.Path; import com.yahoo.search.query.profile.QueryProfileRegistry; import com.yahoo.search.query.profile.config.QueryProfileXMLReader; import com.yahoo.vespa.config.ConfigDefinitionKey; -import com.yahoo.config.application.api.ApplicationPackage; import java.io.BufferedInputStream; import java.io.File; @@ -165,7 +165,7 @@ public class MockApplicationPackage implements ApplicationPackage { @Override public ApplicationFile getFile(Path file) { if (files.containsKey(file)) return files.get(file); - return new MockApplicationFile(file, Path.fromString(root.toString())); + return new MockApplicationFile(file, root); } @Override @@ -288,7 +288,7 @@ public class MockApplicationPackage implements ApplicationPackage { Map<Path, MockApplicationFile> mockFiles = new HashMap<>(); for (var file : files.entrySet()) mockFiles.put(file.getKey(), new MockApplicationFile(file.getKey(), - Path.fromString(root.toString()), file.getValue())); + root, file.getValue())); this.files = mockFiles; return this; } @@ -374,8 +374,8 @@ public class MockApplicationPackage implements ApplicationPackage { public static class MockApplicationFile extends ApplicationFile { - /** The path to the application package root */ - private final Path root; + /** The application package root */ + private final File root; /** The File pointing to the actual file represented by this */ private final File file; @@ -383,14 +383,14 @@ public class MockApplicationPackage implements ApplicationPackage { /** The content of this file, or null to read it from the file system. */ private final String content; - public MockApplicationFile(Path filePath, Path applicationPackagePath) { - this(filePath, applicationPackagePath, null); + public MockApplicationFile(Path relativeFile, File root) { + this(relativeFile, root, null); } - private MockApplicationFile(Path filePath, Path applicationPackagePath, String content) { - super(filePath); - this.root = applicationPackagePath; - file = applicationPackagePath.append(filePath).toFile(); + private MockApplicationFile(Path relativeFile, File root, String content) { + super(relativeFile); + this.root = root; + this.file = root.toPath().resolve(relativeFile.toString()).toFile(); this.content = content; } @@ -491,7 +491,7 @@ public class MockApplicationPackage implements ApplicationPackage { Iterator<String> pathIterator = path.iterator(); // Skip the path elements this shares with the root - for (Iterator<String> rootIterator = root.iterator(); rootIterator.hasNext(); ) { + for (Iterator<String> rootIterator = Path.fromString(root.toString()).iterator(); rootIterator.hasNext(); ) { String rootElement = rootIterator.next(); String pathElement = pathIterator.next(); if ( ! rootElement.equals(pathElement)) throw new RuntimeException("Assumption broken"); diff --git a/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java b/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java index 7e2953e6606..74a5e92d2ba 100644 --- a/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java +++ b/config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java @@ -61,7 +61,7 @@ public class MockRoot extends AbstractConfigProducerRoot { public MockRoot(String rootConfigId, DeployState deployState) { super(rootConfigId); - hostSystem = new HostSystem(this, "hostsystem", deployState.getProvisioner(), deployState.getDeployLogger()); + hostSystem = new HostSystem(this, "hostsystem", deployState.getProvisioner(), deployState.getDeployLogger(), deployState.isHosted()); this.deployState = deployState; fileReferencesRepository = new FileReferencesRepository(deployState.getFileRegistry()); } diff --git a/config-model/src/main/java/com/yahoo/schema/derived/SummaryClass.java b/config-model/src/main/java/com/yahoo/schema/derived/SummaryClass.java index 193c6893203..efc64a5aa40 100644 --- a/config-model/src/main/java/com/yahoo/schema/derived/SummaryClass.java +++ b/config-model/src/main/java/com/yahoo/schema/derived/SummaryClass.java @@ -59,7 +59,7 @@ public class SummaryClass extends Derived { /** MUST be called after all other fields are added */ private void deriveImplicitFields(DocumentSummary summary, Map<String, SummaryClassField> fields) { if (summary.getName().equals("default")) { - addField(SummaryClass.DOCUMENT_ID_FIELD, DataType.STRING, fields); + addField(SummaryClass.DOCUMENT_ID_FIELD, DataType.STRING, SummaryTransform.DOCUMENT_ID, fields); } } @@ -72,10 +72,6 @@ public class SummaryClass extends Derived { } } - private void addField(String name, DataType type, Map<String, SummaryClassField> fields) { - addField(name, type, null, fields); - } - private void addField(String name, DataType type, SummaryTransform transform, Map<String, SummaryClassField> fields) { diff --git a/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java b/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java index 335942de99d..221280dc1e4 100644 --- a/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java +++ b/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java @@ -83,6 +83,11 @@ public class ImmutableImportedSDField implements ImmutableSDField { } @Override + public boolean wasConfiguredToDoIndexing() { + return importedField.targetField().wasConfiguredToDoIndexing(); + } + + @Override public DataType getDataType() { return importedField.targetField().getDataType(); } diff --git a/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java b/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java index 44e442811ba..37cbb3a8171 100644 --- a/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java +++ b/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java @@ -45,6 +45,14 @@ public interface ImmutableSDField { */ boolean wasConfiguredToDoAttributing(); + /** + * Whether this field at some time was configured to do indexing. + * + * This function can typically return a different value than doesIndexing(), + * which uses the final state of the underlying indexing script instead. + */ + boolean wasConfiguredToDoIndexing(); + DataType getDataType(); Index getIndex(String name); diff --git a/config-model/src/main/java/com/yahoo/schema/document/SDField.java b/config-model/src/main/java/com/yahoo/schema/document/SDField.java index 943d6c6fc14..2d79d89a2a0 100644 --- a/config-model/src/main/java/com/yahoo/schema/document/SDField.java +++ b/config-model/src/main/java/com/yahoo/schema/document/SDField.java @@ -117,6 +117,7 @@ public class SDField extends Field implements TypedKey, ImmutableSDField { private boolean isExtraField = false; private boolean wasConfiguredToDoAttributing = false; + private boolean wasConfiguredToDoIndexing = false; /** * Creates a new field. This method is only used to create reserved fields. @@ -381,6 +382,11 @@ public class SDField extends Field implements TypedKey, ImmutableSDField { return wasConfiguredToDoAttributing; } + @Override + public boolean wasConfiguredToDoIndexing() { + return wasConfiguredToDoIndexing; + } + /** Parse an indexing expression which will use the simple linguistics implementation suitable for testing */ public void parseIndexingScript(String script) { parseIndexingScript(script, new SimpleLinguistics(), Embedder.throwsOnUse.asMap()); @@ -408,6 +414,9 @@ public class SDField extends Field implements TypedKey, ImmutableSDField { if (!wasConfiguredToDoAttributing()) { wasConfiguredToDoAttributing = doesAttributing(); } + if (!wasConfiguredToDoIndexing()) { + wasConfiguredToDoIndexing = doesIndexing(); + } if (!usesStructOrMap()) { new ExpressionVisitor() { diff --git a/config-model/src/main/java/com/yahoo/schema/processing/AddExtraFieldsToDocument.java b/config-model/src/main/java/com/yahoo/schema/processing/AddExtraFieldsToDocument.java index ca81301da73..471d338a5f6 100644 --- a/config-model/src/main/java/com/yahoo/schema/processing/AddExtraFieldsToDocument.java +++ b/config-model/src/main/java/com/yahoo/schema/processing/AddExtraFieldsToDocument.java @@ -39,6 +39,7 @@ public class AddExtraFieldsToDocument extends Processor { case DYNAMICBOLDED: case DYNAMICTEASER: case TEXTEXTRACTOR: + case DOCUMENT_ID: // TODO: Adding the 'documentid' field should no longer be needed when the docsum framework in the backend has been simplified and the transform is always used. addSummaryField(schema, document, summaryField, validate); break; default: diff --git a/config-model/src/main/java/com/yahoo/schema/processing/Processing.java b/config-model/src/main/java/com/yahoo/schema/processing/Processing.java index 63eca2121c1..8f7e8daeed0 100644 --- a/config-model/src/main/java/com/yahoo/schema/processing/Processing.java +++ b/config-model/src/main/java/com/yahoo/schema/processing/Processing.java @@ -52,6 +52,7 @@ public class Processing { ImplicitSummaries::new, ImplicitSummaryFields::new, AdjustPositionSummaryFields::new, + SummaryTransformForDocumentId::new, SummaryConsistency::new, SummaryNamesFieldCollisions::new, SummaryFieldsMustHaveValidSource::new, diff --git a/config-model/src/main/java/com/yahoo/schema/processing/RankingExpressionTypeResolver.java b/config-model/src/main/java/com/yahoo/schema/processing/RankingExpressionTypeResolver.java index 07f79f16334..d985089b2cb 100644 --- a/config-model/src/main/java/com/yahoo/schema/processing/RankingExpressionTypeResolver.java +++ b/config-model/src/main/java/com/yahoo/schema/processing/RankingExpressionTypeResolver.java @@ -93,9 +93,9 @@ public class RankingExpressionTypeResolver extends Processor { throw new IllegalArgumentException(profile + " is strict but is missing a query profile type " + "declaration of features " + context.queryFeaturesNotDeclared()); else - deployLogger.logApplicationPackage(Level.WARNING, "The following query features used in " + profile + - " are not declared in query profile " + - "types and will be interpreted as scalars, not tensors: " + + deployLogger.logApplicationPackage(Level.WARNING, "The following query features used in " + + profile + " are not declared " + + "and will be interpreted as scalars, not tensors: " + context.queryFeaturesNotDeclared()); warnedAbout.addAll(context.queryFeaturesNotDeclared()); } diff --git a/config-model/src/main/java/com/yahoo/schema/processing/SummaryTransformForDocumentId.java b/config-model/src/main/java/com/yahoo/schema/processing/SummaryTransformForDocumentId.java new file mode 100644 index 00000000000..99419ecd526 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/schema/processing/SummaryTransformForDocumentId.java @@ -0,0 +1,32 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.schema.processing; + +import com.yahoo.config.application.api.DeployLogger; +import com.yahoo.schema.RankProfileRegistry; +import com.yahoo.schema.Schema; +import com.yahoo.schema.derived.SummaryClass; +import com.yahoo.vespa.documentmodel.SummaryTransform; +import com.yahoo.vespa.model.container.search.QueryProfiles; + +/** + * Adds the corresponding summary transform for all "documentid" summary fields. + * + * @author geirst + */ +public class SummaryTransformForDocumentId extends Processor { + + public SummaryTransformForDocumentId(Schema schema, DeployLogger deployLogger, RankProfileRegistry rankProfileRegistry, QueryProfiles queryProfiles) { + super(schema, deployLogger, rankProfileRegistry, queryProfiles); + } + + @Override + public void process(boolean validate, boolean documentsOnly) { + for (var summary : schema.getSummaries().values()) { + for (var summaryField : summary.getSummaryFields().values()) { + if (summaryField.getName().equals(SummaryClass.DOCUMENT_ID_FIELD)) { + summaryField.setTransform(SummaryTransform.DOCUMENT_ID); + } + } + } + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java index a1ebc9e4bec..5c3aef89edc 100644 --- a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java +++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java @@ -23,7 +23,8 @@ public enum SummaryTransform { ATTRIBUTECOMBINER("attributecombiner"), MATCHED_ELEMENTS_FILTER("matchedelementsfilter"), MATCHED_ATTRIBUTE_ELEMENTS_FILTER("matchedattributeelementsfilter"), - COPY("copy"); + COPY("copy"), + DOCUMENT_ID("documentid"); private final String name; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java b/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java index 1042400442b..fcad1e20c16 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -39,30 +40,34 @@ public class HostSystem extends AbstractConfigProducer<Host> { private final Map<String, HostResource> hostname2host = new LinkedHashMap<>(); private final HostProvisioner provisioner; private final DeployLogger deployLogger; + private final boolean isHosted; static { String checkIpProperty = System.getProperty("config_model.ip_check", "true"); doCheckIp = ! checkIpProperty.equalsIgnoreCase("false"); } - public HostSystem(AbstractConfigProducer<?> parent, String name, HostProvisioner provisioner, DeployLogger deployLogger) { + public HostSystem(AbstractConfigProducer<?> parent, String name, HostProvisioner provisioner, DeployLogger deployLogger, boolean isHosted) { super(parent, name); this.provisioner = provisioner; this.deployLogger = deployLogger; + this.isHosted = isHosted; } void checkName(String hostname) { if (doCheckIp) { + // Bad DNS config in a hosted system isn't actionable by the tenant, so we log any warnings internally + BiConsumer<Level, String> logFunction = isHosted ? deployLogger::log : deployLogger::logApplicationPackage; // Give a warning if the host does not exist try { var inetAddr = java.net.InetAddress.getByName(hostname); String canonical = inetAddr.getCanonicalHostName(); if (!hostname.equals(canonical)) { - deployLogger.logApplicationPackage(Level.WARNING, "Host named '" + hostname + "' may not receive any config " + - "since it differs from its canonical hostname '" + canonical + "' (check DNS and /etc/hosts)."); + logFunction.accept(Level.WARNING, "Host named '" + hostname + "' may not receive any config " + + "since it differs from its canonical hostname '" + canonical + "' (check DNS and /etc/hosts)."); } } catch (UnknownHostException e) { - deployLogger.logApplicationPackage(Level.WARNING, "Unable to lookup IP address of host: " + hostname); + logFunction.accept(Level.WARNING, "Unable to lookup IP address of host: " + hostname); } } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java index 75b13a89e83..c50b9b7f842 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java @@ -36,7 +36,7 @@ public class LogserverContainerCluster extends ContainerCluster<LogserverContain protected boolean messageBusEnabled() { return false; } private void addLogHandler() { - Handler<?> logHandler = Handler.fromClassName(ContainerCluster.LOG_HANDLER_CLASS); + Handler logHandler = Handler.fromClassName(ContainerCluster.LOG_HANDLER_CLASS); logHandler.addServerBindings(SystemBindingPattern.fromHttpPath("/logs")); addComponent(logHandler); } 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 1da5e190c70..5d60cec0679 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 @@ -118,7 +118,7 @@ public class ClusterControllerContainer extends Container implements ZOOKEEPER_SERVER_BUNDLE); } - private void addHandler(Handler<?> h, String path) { + private void addHandler(Handler h, String path) { h.addServerBindings(SystemBindingPattern.fromHttpPath(path)); super.addHandler(h); } @@ -138,7 +138,7 @@ public class ClusterControllerContainer extends Container implements } private void addHandler(String id, String className, String path, ComponentSpecification bundle) { - addHandler(new Handler<>(createComponentModel(id, className, bundle)), path); + addHandler(new Handler(createComponentModel(id, className, bundle)), path); } private ReindexingContext reindexingContext() { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java index a29647b062a..680a4b97f86 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java @@ -119,12 +119,12 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC } private void addHttpHandler(Class<? extends ThreadedHttpRequestHandler> clazz, String bindingPath) { - Handler<AbstractConfigProducer<?>> metricsHandler = createMetricsHandler(clazz, bindingPath); + Handler metricsHandler = createMetricsHandler(clazz, bindingPath); addComponent(metricsHandler); } - static Handler<AbstractConfigProducer<?>> createMetricsHandler(Class<? extends ThreadedHttpRequestHandler> clazz, String bindingPath) { - Handler<AbstractConfigProducer<?>> metricsHandler = new Handler<>( + static Handler createMetricsHandler(Class<? extends ThreadedHttpRequestHandler> clazz, String bindingPath) { + Handler metricsHandler = new Handler( new ComponentModel(clazz.getName(), null, METRICS_PROXY_BUNDLE_NAME, null)); metricsHandler.addServerBindings( SystemBindingPattern.fromHttpPath(bindingPath), diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldAttributesValidator.java index 01bf846d4cb..8515c34a377 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldAttributesValidator.java @@ -22,7 +22,7 @@ import java.util.stream.Collectors; * * @author geirst */ -public class ComplexAttributeFieldsValidator extends Validator { +public class ComplexFieldsWithStructFieldAttributesValidator extends Validator { @Override public void validate(VespaModel model, DeployState deployState) { @@ -39,7 +39,7 @@ public class ComplexAttributeFieldsValidator extends Validator { private static void validateComplexFields(String clusterName, Schema schema) { String unsupportedFields = schema.allFields() .filter(field -> isUnsupportedComplexField(field)) - .map(ComplexAttributeFieldsValidator::toString) + .map(ComplexFieldsWithStructFieldAttributesValidator::toString) .collect(Collectors.joining(", ")); if (!unsupportedFields.isEmpty()) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldIndexesValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldIndexesValidator.java new file mode 100644 index 00000000000..a18ce7e245d --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldIndexesValidator.java @@ -0,0 +1,72 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.schema.Schema; +import com.yahoo.schema.document.ImmutableSDField; +import com.yahoo.vespa.model.VespaModel; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Validates that complex fields (of type struct or map) do not have any struct fields with 'indexing: index'. + * This is not supported and will confuse the user if not validated. + * + * Only applies for indexed search clusters. + * + * @author geirst + */ +public class ComplexFieldsWithStructFieldIndexesValidator extends Validator { + + @Override + public void validate(VespaModel model, DeployState deployState) { + for (var cluster : model.getSearchClusters()) { + if (cluster.isStreaming()) { + continue; + } + for (var spec : cluster.schemas().values()) { + validateComplexFields(cluster.getClusterName(), spec.fullSchema()); + } + } + } + + private static void validateComplexFields(String clusterName, Schema schema) { + String unsupportedFields = schema.allFields() + .filter(field -> hasStructFieldsWithIndex(field)) + .map(ComplexFieldsWithStructFieldIndexesValidator::toString) + .collect(Collectors.joining(", ")); + + if (!unsupportedFields.isEmpty()) { + throw new IllegalArgumentException( + String.format("For cluster '%s', schema '%s': The following complex fields have struct fields with 'indexing: index' which is not supported: %s. " + + "Change to 'indexing: attribute' instead", + clusterName, schema.getName(), unsupportedFields)); + } + } + + private static boolean hasStructFieldsWithIndex(ImmutableSDField field) { + return (!field.isImportedField() && field.usesStructOrMap() && hasStructFieldsWithIndex(field.getStructFields())); + } + + private static String toString(ImmutableSDField field) { + return field.getName() + " (" + String.join(", ", getStructFieldsWithIndex(field.getStructFields())) + ")"; + } + + private static boolean hasStructFieldsWithIndex(Collection<? extends ImmutableSDField> structFields) { + return !getStructFieldsWithIndex(structFields).isEmpty(); + } + + private static List<String> getStructFieldsWithIndex(Collection<? extends ImmutableSDField> structFields) { + var result = new ArrayList<String>(); + for (var structField : structFields) { + if (structField.wasConfiguredToDoIndexing()) { + result.add(structField.getName()); + } + result.addAll(getStructFieldsWithIndex(structField.getStructFields())); + } + return result; + } +} 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 e4a64e8d476..7ea582b99e6 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 @@ -84,22 +84,21 @@ public class QuotaValidator extends Validator { } } - private void throwIfBudgetNegative(double spend, BigDecimal budget, SystemName systemName) { + private static 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); + throw new IllegalArgumentException(quotaMessage("Please free up some capacity", systemName, spend, budget)); } } - private void throwIfBudgetExceeded(double spend, BigDecimal budget, SystemName systemName) { + private static void throwIfBudgetExceeded(double spend, BigDecimal budget, SystemName systemName) { if (budget.doubleValue() < spend) { - throw new IllegalArgumentException((systemName.equals(SystemName.Public) ? "" : systemName.value() + ": ") + - "Deployment would make your tenant exceed its quota and has been blocked! Please contact support to update your plan."); + throw new IllegalArgumentException(quotaMessage("Deployment exceeds its quota and has been blocked! Please contact support to update your plan", systemName, spend, budget)); } } - 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); + private static String quotaMessage(String message, SystemName system, double spend, BigDecimal budget) { + String quotaDescription = String.format(Locale.ENGLISH, "Quota is $%.2f, but at least $%.2f is required", budget, spend); + return (system == SystemName.Public ? "" : system.value() + ": ") + message + ": " + quotaDescription; } + } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java index 4f322578b1c..718f1646126 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/UriBindingsValidator.java @@ -24,7 +24,7 @@ class UriBindingsValidator extends Validator { @Override public void validate(VespaModel model, DeployState deployState) { for (ApplicationContainerCluster cluster : model.getContainerClusters().values()) { - for (Handler<?> handler : cluster.getHandlers()) { + for (Handler handler : cluster.getHandlers()) { for (BindingPattern binding : handler.getServerBindings()) { validateUserBinding(binding, model, deployState); } 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 ce61d3edc3b..dab1eeccc96 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 @@ -71,7 +71,8 @@ public class Validation { new SchemasDirValidator().validate(model, deployState); new BundleValidator().validate(model, deployState); new SearchDataTypeValidator().validate(model, deployState); - new ComplexAttributeFieldsValidator().validate(model, deployState); + new ComplexFieldsWithStructFieldAttributesValidator().validate(model, deployState); + new ComplexFieldsWithStructFieldIndexesValidator().validate(model, deployState); new StreamingValidator().validate(model, deployState); new RankSetupValidator(validationParameters.ignoreValidationErrors()).validate(model, deployState); new NoPrefixForIndexes().validate(model, deployState); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java index 69613944e74..170e8940787 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java @@ -5,7 +5,6 @@ import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.text.XML; import com.yahoo.vespa.model.container.ApplicationContainerCluster; -import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.component.UserBindingPattern; import org.w3c.dom.Element; @@ -22,7 +21,7 @@ public class DomClientProviderBuilder extends DomHandlerBuilder { @Override protected Handler doBuild(DeployState deployState, AbstractConfigProducer parent, Element clientElement) { - Handler<? super Component<?, ?>> client = createHandler(clientElement); + Handler client = createHandler(clientElement); for (Element binding : XML.getChildren(clientElement, "binding")) client.addClientBindings(UserBindingPattern.fromPattern(XML.getValue(binding))); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java index 025a6377b04..7bfe971981e 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java @@ -9,15 +9,12 @@ import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.text.XML; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.component.BindingPattern; -import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.component.UserBindingPattern; import com.yahoo.vespa.model.container.xml.BundleInstantiationSpecificationBuilder; import org.w3c.dom.Element; -import java.util.List; import java.util.Set; -import java.util.logging.Level; import static com.yahoo.vespa.model.container.ApplicationContainerCluster.METRICS_V2_HANDLER_BINDING_1; import static com.yahoo.vespa.model.container.ApplicationContainerCluster.METRICS_V2_HANDLER_BINDING_2; @@ -29,7 +26,7 @@ import static java.util.logging.Level.INFO; /** * @author gjoranv */ -public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Handler<?>> { +public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Handler> { private static final Set<BindingPattern> reservedBindings = Set.of(METRICS_V2_HANDLER_BINDING_1, @@ -45,8 +42,8 @@ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder< } @Override - protected Handler<?> doBuild(DeployState deployState, AbstractConfigProducer<?> parent, Element handlerElement) { - Handler<? super Component<?, ?>> handler = createHandler(handlerElement); + protected Handler doBuild(DeployState deployState, AbstractConfigProducer<?> parent, Element handlerElement) { + Handler handler = createHandler(handlerElement); for (Element binding : XML.getChildren(handlerElement, "binding")) addServerBinding(handler, UserBindingPattern.fromPattern(XML.getValue(binding)), deployState.getDeployLogger()); @@ -56,18 +53,18 @@ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder< return handler; } - Handler<? super Component<?, ?>> createHandler(Element handlerElement) { + Handler createHandler(Element handlerElement) { BundleInstantiationSpecification bundleSpec = BundleInstantiationSpecificationBuilder.build(handlerElement); - return new Handler<>(new ComponentModel(bundleSpec)); + return new Handler(new ComponentModel(bundleSpec)); } - private void addServerBinding(Handler<? super Component<?, ?>> handler, BindingPattern binding, DeployLogger log) { + private void addServerBinding(Handler handler, BindingPattern binding, DeployLogger log) { throwIfBindingIsReserved(binding, handler); handler.addServerBindings(binding); removeExistingServerBinding(binding, handler, log); } - private void throwIfBindingIsReserved(BindingPattern binding, Handler<?> newHandler) { + private void throwIfBindingIsReserved(BindingPattern binding, Handler newHandler) { for (var reserved : reservedBindings) { if (binding.hasSamePattern(reserved)) { throw new IllegalArgumentException("Binding '" + binding.patternString() + "' is a reserved Vespa binding and " + @@ -76,7 +73,7 @@ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder< } } - private void removeExistingServerBinding(BindingPattern binding, Handler<?> newHandler, DeployLogger log) { + private void removeExistingServerBinding(BindingPattern binding, Handler newHandler, DeployLogger log) { for (var handler : cluster.getHandlers()) { for (BindingPattern serverBinding : handler.getServerBindings()) { if (serverBinding.hasSamePattern(binding)) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilder.java index e2bfcb66f37..96e2e1d5ebd 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilder.java @@ -230,7 +230,9 @@ public class VespaDomBuilder extends VespaModelBuilder { deployState.getDocumentModel(), deployState.getVespaVersion(), deployState.getProperties().applicationId()); - root.setHostSystem(new HostSystem(root, "hosts", deployState.getProvisioner(), deployState.getDeployLogger())); + root.setHostSystem(new HostSystem(root, "hosts", deployState.getProvisioner(), + deployState.getDeployLogger(), + deployState.isHosted())); new Client(root); return root; } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java index 33c125dcecf..f23f27c0d8e 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java @@ -1,8 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.clients; -import com.yahoo.config.model.producer.AbstractConfigProducer; -import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.handler.threadpool.ContainerThreadpoolConfig; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.vespa.model.container.ContainerCluster; @@ -41,31 +39,31 @@ public class ContainerDocumentApi { private static void addFeedHandler(ContainerCluster<?> cluster, HandlerOptions handlerOptions) { String bindingSuffix = ContainerCluster.RESERVED_URI_PREFIX + "/feedapi"; - var handler = newVespaClientHandler("com.yahoo.vespa.http.server.FeedHandler", bindingSuffix, handlerOptions); - cluster.addComponent(handler); var executor = new Threadpool("feedapi-handler", handlerOptions.feedApiThreadpoolOptions); - handler.inject(executor); - handler.addComponent(executor); + var handler = newVespaClientHandler("com.yahoo.vespa.http.server.FeedHandler", + bindingSuffix, handlerOptions, executor); + cluster.addComponent(handler); } private static void addRestApiHandler(ContainerCluster<?> cluster, HandlerOptions handlerOptions) { - var handler = newVespaClientHandler("com.yahoo.document.restapi.resource.DocumentV1ApiHandler", DOCUMENT_V1_PREFIX + "/*", handlerOptions); + var handler = newVespaClientHandler("com.yahoo.document.restapi.resource.DocumentV1ApiHandler", + DOCUMENT_V1_PREFIX + "/*", handlerOptions, null); cluster.addComponent(handler); // We need to include a dummy implementation of the previous restapi handler (using the same class name). // The internal legacy test framework requires that the name of the old handler is listed in /ApplicationStatus. - var oldHandlerDummy = handlerComponentSpecification("com.yahoo.document.restapi.resource.RestApi"); + var oldHandlerDummy = createHandler("com.yahoo.document.restapi.resource.RestApi", null); cluster.addComponent(oldHandlerDummy); } public boolean ignoreUndefinedFields() { return ignoreUndefinedFields; } - private static Handler<AbstractConfigProducer<?>> newVespaClientHandler( - String componentId, - String bindingSuffix, - HandlerOptions handlerOptions) { - Handler<AbstractConfigProducer<?>> handler = handlerComponentSpecification(componentId); + private static Handler newVespaClientHandler(String componentId, + String bindingSuffix, + HandlerOptions handlerOptions, + Threadpool executor) { + Handler handler = createHandler(componentId, executor); if (handlerOptions.bindings.isEmpty()) { handler.addServerBindings( SystemBindingPattern.fromHttpPath(bindingSuffix), @@ -81,9 +79,9 @@ public class ContainerDocumentApi { return handler; } - private static Handler<AbstractConfigProducer<?>> handlerComponentSpecification(String className) { - return new Handler<>(new ComponentModel( - BundleInstantiationSpecification.getFromStrings(className, null, "vespaclient-container-plugin"), "")); + private static Handler createHandler(String className, Threadpool executor) { + return new Handler(new ComponentModel(className, null, "vespaclient-container-plugin"), + executor); } public static final class HandlerOptions { @@ -104,12 +102,10 @@ public class ContainerDocumentApi { } @Override - public void getConfig(ContainerThreadpoolConfig.Builder builder) { - super.getConfig(builder); - - // User options overrides below configuration - if (hasUserOptions()) return; - builder.maxThreads(-4).minThreads(-4).queueSize(500); + protected void setDefaultConfigValues(ContainerThreadpoolConfig.Builder builder) { + builder.maxThreads(-4) + .minThreads(-4) + .queueSize(500); } } 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 937d7cf58d3..e316f826ad6 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 @@ -147,7 +147,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat } private void addMetricsHandler(String handlerClass, BindingPattern rootBinding, BindingPattern innerBinding) { - Handler<AbstractConfigProducer<?>> handler = new Handler<>( + Handler handler = new Handler( new ComponentModel(handlerClass, null, null, null)); handler.addServerBindings(rootBinding, innerBinding); addComponent(handler); 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 63c323bfdaf..3d6332a9773 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 @@ -76,7 +76,7 @@ public abstract class Container extends AbstractService implements private final boolean dumpHeapOnShutdownTimeout; private final double shutdownTimeoutS; - private final ComponentGroup<Handler<?>> handlers = new ComponentGroup<>(this, "handler"); + private final ComponentGroup<Handler> handlers = new ComponentGroup<>(this, "handler"); private final ComponentGroup<Component<?, ?>> components = new ComponentGroup<>(this, "components"); private final JettyHttpServer defaultHttpServer; @@ -113,7 +113,7 @@ public abstract class Container extends AbstractService implements /** True if this container is retired (slated for removal) */ public boolean isRetired() { return retired; } - public ComponentGroup<Handler<?>> getHandlers() { + public ComponentGroup<Handler> getHandlers() { return handlers; } @@ -129,7 +129,7 @@ public abstract class Container extends AbstractService implements addComponent(new SimpleComponent(new ComponentModel(idSpec, classSpec, bundleSpec))); } - public final void addHandler(Handler<?> h) { + public final void addHandler(Handler h) { handlers.addComponent(h); } 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 2385b5b3812..e69872ca1e7 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,8 @@ import java.util.TreeSet; import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.yahoo.vespa.model.container.component.chain.ProcessingHandler.PROCESSING_HANDLER_CLASS; + /** * Parent class for all container cluster types. * @@ -129,7 +131,7 @@ public abstract class ContainerCluster<CONTAINER extends Container> public static final BindingPattern VIP_HANDLER_BINDING = SystemBindingPattern.fromHttpPath("/status.html"); public static final Set<Path> SEARCH_AND_DOCPROC_BUNDLES = Stream.of( - PlatformBundles.searchAndDocprocBundle, "container-search-gui", "docprocs", "linguistics-components") + PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE, "container-search-gui", "docprocs", "linguistics-components") .map(PlatformBundles::absoluteBundlePath).collect(Collectors.toSet()); private final String name; @@ -142,6 +144,7 @@ public abstract class ContainerCluster<CONTAINER extends Container> private ContainerDocproc containerDocproc; private ContainerDocumentApi containerDocumentApi; private SecretStore secretStore; + private final ContainerThreadpool defaultHandlerThreadpool = new Handler.DefaultHandlerThreadpool(); private boolean rpcServerEnabled = true; private boolean httpServerEnabled = true; @@ -181,6 +184,7 @@ public abstract class ContainerCluster<CONTAINER extends Container> addCommonVespaBundles(); addSimpleComponent(AccessLog.class); addComponent(new DefaultThreadpoolProvider(this, defaultPoolNumThreads)); + addComponent(defaultHandlerThreadpool); addSimpleComponent(com.yahoo.concurrent.classlock.ClassLocking.class); addSimpleComponent("com.yahoo.container.jdisc.metric.MetricConsumerProviderProvider"); addSimpleComponent("com.yahoo.container.jdisc.metric.MetricProvider"); @@ -217,36 +221,45 @@ public abstract class ContainerCluster<CONTAINER extends Container> } public void addMetricStateHandler() { - Handler<AbstractConfigProducer<?>> stateHandler = new Handler<>( + Handler stateHandler = new Handler( new ComponentModel(STATE_HANDLER_CLASS, null, null, null)); stateHandler.addServerBindings(STATE_HANDLER_BINDING_1, STATE_HANDLER_BINDING_2); addComponent(stateHandler); } public void addDefaultRootHandler() { - Handler<AbstractConfigProducer<?>> handler = new Handler<>( - new ComponentModel(BundleInstantiationSpecification.getFromStrings( + Handler handler = new Handler( + new ComponentModel(BundleInstantiationSpecification.fromStrings( BINDINGS_OVERVIEW_HANDLER_CLASS, null, null), null)); // null bundle, as the handler is in container-disc handler.addServerBindings(ROOT_HANDLER_BINDING); addComponent(handler); } public void addApplicationStatusHandler() { - Handler<AbstractConfigProducer<?>> statusHandler = new Handler<>( - new ComponentModel(BundleInstantiationSpecification.getFromStrings( + Handler statusHandler = new Handler( + new ComponentModel(BundleInstantiationSpecification.fromStrings( APPLICATION_STATUS_HANDLER_CLASS, null, null), null)); // null bundle, as the handler is in container-disc statusHandler.addServerBindings(SystemBindingPattern.fromHttpPath("/ApplicationStatus")); addComponent(statusHandler); } public void addVipHandler() { - Handler<?> vipHandler = Handler.fromClassName(FileStatusHandlerComponent.CLASS); + Handler vipHandler = Handler.fromClassName(FileStatusHandlerComponent.CLASS); vipHandler.addServerBindings(VIP_HANDLER_BINDING); addComponent(vipHandler); } public final void addComponent(Component<?, ?> component) { componentGroup.addComponent(component); + if (component instanceof Handler handler) { + ensureHandlerHasThreadpool(handler); + } + } + + private void ensureHandlerHasThreadpool(Handler handler) { + if (! handler.hasCustomThreadPool) { + handler.inject(defaultHandlerThreadpool); + } } public final void addSimpleComponent(String idSpec, String classSpec, String bundleSpec) { @@ -305,11 +318,9 @@ public abstract class ContainerCluster<CONTAINER extends Container> this.processingChains = processingChains; - // Cannot use the class object for ProcessingHandler, because its superclass is not accessible ProcessingHandler<?> processingHandler = new ProcessingHandler<>( processingChains, - "com.yahoo.processing.handler.ProcessingHandler", - null); + BundleInstantiationSpecification.fromStrings(PROCESSING_HANDLER_CLASS, null, null)); for (BindingPattern binding: serverBindings) processingHandler.addServerBindings(binding); @@ -368,9 +379,8 @@ public abstract class ContainerCluster<CONTAINER extends Container> return containerDocproc.getChains(); } - @SuppressWarnings("unchecked") - public Collection<Handler<?>> getHandlers() { - return (Collection<Handler<?>>)(Collection)componentGroup.getComponents(Handler.class); + public Collection<Handler> getHandlers() { + return componentGroup.getComponents(Handler.class); } public void setSecretStore(SecretStore secretStore) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java index cb8e6ba85ff..088465f56b1 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java @@ -63,8 +63,8 @@ public class ContainerModelEvaluation implements rankProfileList.getConfig(builder); } - public static Handler<?> getHandler() { - Handler<?> handler = new Handler<>(new ComponentModel(REST_HANDLER_NAME, null, EVALUATION_BUNDLE_NAME)); + public static Handler getHandler() { + Handler handler = new Handler(new ComponentModel(REST_HANDLER_NAME, null, EVALUATION_BUNDLE_NAME)); handler.addServerBindings( SystemBindingPattern.fromHttpPath(REST_BINDING_PATH), SystemBindingPattern.fromHttpPath(REST_BINDING_PATH + "/*")); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java index 489e4cc135a..fb4e62f5cd1 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java @@ -17,14 +17,14 @@ import java.util.Optional; * * @author bjorncs */ -public class ContainerThreadpool extends SimpleComponent implements ContainerThreadpoolConfig.Producer { +public abstract class ContainerThreadpool extends SimpleComponent implements ContainerThreadpoolConfig.Producer { private final String name; private final UserOptions userOptions; public ContainerThreadpool(String name, UserOptions userOptions) { super(new ComponentModel( - BundleInstantiationSpecification.getFromStrings( + BundleInstantiationSpecification.fromStrings( "threadpool@" + name, ContainerThreadpoolImpl.class.getName(), null))); @@ -32,8 +32,13 @@ public class ContainerThreadpool extends SimpleComponent implements ContainerThr this.userOptions = userOptions; } + // Must be implemented by subclasses to set values that may be overridden by user options. + protected abstract void setDefaultConfigValues(ContainerThreadpoolConfig.Builder builder); + @Override public void getConfig(ContainerThreadpoolConfig.Builder builder) { + setDefaultConfigValues(builder); + builder.name(this.name); if (userOptions != null) { builder.maxThreads(userOptions.maxThreads); @@ -42,9 +47,6 @@ public class ContainerThreadpool extends SimpleComponent implements ContainerThr } } - protected Optional<UserOptions> userOptions() { return Optional.ofNullable(userOptions); } - protected boolean hasUserOptions() { return userOptions().isPresent(); } - public static class UserOptions { private final int maxThreads; private final int minThreads; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java index 0b37abaded9..0fdd36b8811 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java @@ -19,7 +19,7 @@ class DefaultThreadpoolProvider extends SimpleComponent implements ThreadpoolCon DefaultThreadpoolProvider(ContainerCluster<?> cluster, int defaultWorkerThreads) { super(new ComponentModel( - BundleInstantiationSpecification.getFromStrings( + BundleInstantiationSpecification.fromStrings( "default-threadpool", ThreadPoolProvider.class.getName(), null))); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java index 7ce2425179b..5e8bb85c29d 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java @@ -33,7 +33,7 @@ public class IdentityProvider extends SimpleComponent implements IdentityConfig. URI ztsUrl, String athenzDnsSuffix, Zone zone) { - super(new ComponentModel(BundleInstantiationSpecification.getFromStrings(CLASS, CLASS, BUNDLE))); + super(new ComponentModel(BundleInstantiationSpecification.fromStrings(CLASS, CLASS, BUNDLE))); this.domain = domain; this.service = service; this.loadBalancerName = loadBalancerName; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java b/config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java index e5125fe7e1d..6a1e647e9be 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java @@ -1,6 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container; +import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.vespa.defaults.Defaults; import java.nio.file.Path; @@ -28,7 +29,7 @@ public class PlatformBundles { } public static final Path LIBRARY_PATH = Paths.get(Defaults.getDefaults().underVespaHome("lib/jars")); - public static final String searchAndDocprocBundle = "container-search-and-docproc"; + public static final String SEARCH_AND_DOCPROC_BUNDLE = BundleInstantiationSpecification.CONTAINER_SEARCH_AND_DOCPROC; public static Set<Path> commonVespaBundles() { var bundles = new LinkedHashSet<Path>(); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java index 76124b14209..e4a5c2cd440 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/DiscBindingsConfigGenerator.java @@ -14,16 +14,16 @@ import static java.util.stream.Collectors.toList; */ public class DiscBindingsConfigGenerator { - public static Map<String, Handlers.Builder> generate(Collection<? extends Handler<?>> handlers) { + public static Map<String, Handlers.Builder> generate(Collection<? extends Handler> handlers) { Map<String, Handlers.Builder> handlerBuilders = new LinkedHashMap<>(); - for (Handler<?> handler : handlers) { + for (Handler handler : handlers) { handlerBuilders.putAll(generate(handler)); } return handlerBuilders; } - public static <T extends Handler<?>> Map<String, Handlers.Builder> generate(T handler) { + public static <T extends Handler> Map<String, Handlers.Builder> generate(T handler) { if (handler.getServerBindings().isEmpty() && handler.getClientBindings().isEmpty()) return Collections.emptyMap(); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java index 8ffdccae896..9f2bfe9251b 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java @@ -1,8 +1,9 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container.component; -import com.yahoo.config.model.producer.AbstractConfigProducer; +import com.yahoo.container.handler.threadpool.ContainerThreadpoolConfig; import com.yahoo.osgi.provider.model.ComponentModel; +import com.yahoo.vespa.model.container.ContainerThreadpool; import java.util.ArrayList; import java.util.Arrays; @@ -15,22 +16,35 @@ import java.util.Set; * Models a jdisc RequestHandler (including ClientProvider). * RequestHandlers always have at least one server binding, * while ClientProviders have at least one client binding. - * <p> - * Note that this is also used to model vespa handlers (which do not have any bindings) * * @author gjoranv */ -public class Handler<CHILD extends AbstractConfigProducer<?>> extends Component<CHILD, ComponentModel> { +public class Handler extends Component<Component<?, ?>, ComponentModel> { private final Set<BindingPattern> serverBindings = new LinkedHashSet<>(); private final List<BindingPattern> clientBindings = new ArrayList<>(); + public final boolean hasCustomThreadPool; + public Handler(ComponentModel model) { + this(model, null); + } + + public Handler(ComponentModel model, ContainerThreadpool threadpool) { super(model); + + // The default threadpool is always added to the cluster, so cannot be added here. + if (threadpool != null) { + hasCustomThreadPool = true; + addComponent(threadpool); + inject(threadpool); + } else { + hasCustomThreadPool = false; + } } - public static Handler<AbstractConfigProducer<?>> fromClassName(String className) { - return new Handler<>(new ComponentModel(className, null, null, null)); + public static Handler fromClassName(String className) { + return new Handler(new ComponentModel(className, null, null, null)); } public void addServerBindings(BindingPattern... bindings) { @@ -53,4 +67,24 @@ public class Handler<CHILD extends AbstractConfigProducer<?>> extends Component< return Collections.unmodifiableList(clientBindings); } + + /** + * The default threadpool for all handlers, except those that declare their own, e.g. SearchHandler. + */ + public static class DefaultHandlerThreadpool extends ContainerThreadpool { + + public DefaultHandlerThreadpool() { + super("default-handler-common", null); + } + + @Override + public void setDefaultConfigValues(ContainerThreadpoolConfig.Builder builder) { + builder.maxThreadExecutionTimeSeconds(190) + .keepAliveTime(5.0) + .maxThreads(-2) + .minThreads(-2) + .queueSize(-40); + } + } + } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/SimpleComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/SimpleComponent.java index 2c4dd9a8dda..23915afacdd 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/SimpleComponent.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/SimpleComponent.java @@ -17,7 +17,7 @@ public class SimpleComponent extends Component<AbstractConfigProducer<?>, Compon } public SimpleComponent(String className) { - this(new ComponentModel(BundleInstantiationSpecification.getFromStrings(className, null, null))); + this(new ComponentModel(BundleInstantiationSpecification.fromStrings(className, null, null))); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ProcessingHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ProcessingHandler.java index 3f68a0a2709..897a1f22f30 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ProcessingHandler.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/chain/ProcessingHandler.java @@ -4,7 +4,7 @@ package com.yahoo.vespa.model.container.component.chain; import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.core.ChainsConfig; import com.yahoo.osgi.provider.model.ComponentModel; -import com.yahoo.config.model.producer.AbstractConfigProducer; +import com.yahoo.vespa.model.container.ContainerThreadpool; import com.yahoo.vespa.model.container.component.Handler; @@ -14,21 +14,21 @@ import com.yahoo.vespa.model.container.component.Handler; * @author gjoranv */ public class ProcessingHandler<CHAINS extends Chains<?>> - extends Handler<AbstractConfigProducer<?>> + extends Handler implements ChainsConfig.Producer { - protected final CHAINS chains; + // Cannot use the class object for ProcessingHandler, because its superclass is not accessible + public static final String PROCESSING_HANDLER_CLASS = "com.yahoo.processing.handler.ProcessingHandler"; - public ProcessingHandler(CHAINS chains, String handlerClass) { - this(chains, BundleInstantiationSpecification.getInternalProcessingSpecificationFromStrings(handlerClass, null)); - } + protected final CHAINS chains; - public ProcessingHandler(CHAINS chains, String handlerClass, String bundle) { - this(chains, BundleInstantiationSpecification.getFromStrings(handlerClass, null, bundle)); + // Create a handler that uses the default threadpool for handlers + public ProcessingHandler(CHAINS chains, BundleInstantiationSpecification spec) { + this(chains, spec, null); } - private ProcessingHandler(CHAINS chains, BundleInstantiationSpecification spec) { - super(new ComponentModel(spec, null)); + public ProcessingHandler(CHAINS chains, BundleInstantiationSpecification spec, ContainerThreadpool threadpool) { + super(new ComponentModel(spec), threadpool); this.chains = chains; } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java index 31ba195775b..109ab3e806e 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java @@ -9,6 +9,7 @@ import com.yahoo.docproc.jdisc.observability.DocprocsStatusExtension; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; +import com.yahoo.vespa.model.container.PlatformBundles; import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.component.SimpleComponent; import com.yahoo.vespa.model.container.component.SystemBindingPattern; @@ -26,13 +27,12 @@ public class DocprocChains extends Chains<DocprocChain> { public DocprocChains(AbstractConfigProducer<?> parent, String subId) { super(parent, subId); - docprocHandler = new ProcessingHandler<>(this, "com.yahoo.docproc.jdisc.DocumentProcessingHandler"); + docprocHandler = new ProcessingHandler<>( + this, + BundleInstantiationSpecification.fromSearchAndDocproc("com.yahoo.docproc.jdisc.DocumentProcessingHandler")); addComponent(docprocHandler); - addComponent( - new SimpleComponent( - new ComponentModel( - BundleInstantiationSpecification.getInternalProcessingSpecificationFromStrings( - DocprocsStatusExtension.class.getName(), null), null))); + addComponent(new SimpleComponent( + new ComponentModel(DocprocsStatusExtension.class.getName(), null, PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE))); if (! (getParent() instanceof ApplicationContainerCluster)) { // All application containers already have a DocumentTypeManager, diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/MbusClient.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/MbusClient.java index 28a0748be26..0efcd8df37f 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/MbusClient.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/MbusClient.java @@ -6,13 +6,12 @@ import com.yahoo.component.ComponentSpecification; import com.yahoo.container.jdisc.config.SessionConfig; import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.osgi.provider.model.ComponentModel; -import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.vespa.model.container.component.Handler; /** * @author Einar M R Rosenvinge */ -public class MbusClient extends Handler<AbstractConfigProducer<?>> implements SessionConfig.Producer { +public class MbusClient extends Handler implements SessionConfig.Producer { private static final ComponentSpecification CLASSNAME = ComponentSpecification.fromString("com.yahoo.container.jdisc.messagebus.MbusClientProvider"); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java index d85f00a5bb2..3ee0414bf32 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java @@ -51,7 +51,7 @@ public class AccessControl { private final String domain; private ClientAuthentication clientAuthentication = ClientAuthentication.need; private final Set<BindingPattern> excludeBindings = new LinkedHashSet<>(); - private Collection<Handler<?>> handlers = Collections.emptyList(); + private Collection<Handler> handlers = Collections.emptyList(); public Builder(String domain) { this.domain = domain; } @@ -79,12 +79,12 @@ public class AccessControl { public final String domain; public final ClientAuthentication clientAuthentication; private final Set<BindingPattern> excludedBindings; - private final Collection<Handler<?>> handlers; + private final Collection<Handler> handlers; private AccessControl(String domain, ClientAuthentication clientAuthentication, Set<BindingPattern> excludedBindings, - Collection<Handler<?>> handlers) { + Collection<Handler> handlers) { this.domain = domain; this.clientAuthentication = clientAuthentication; this.excludedBindings = Collections.unmodifiableSet(excludedBindings); @@ -119,7 +119,7 @@ public class AccessControl { public Set<BindingPattern> excludedBindings() { return excludedBindings; } /** all handlers (that are known by the access control components) **/ - public Collection<Handler<?>> handlers() { return handlers; } + public Collection<Handler> handlers() { return handlers; } public static boolean hasHandlerThatNeedsProtection(ApplicationContainerCluster cluster) { return cluster.getHandlers().stream() @@ -135,7 +135,7 @@ public class AccessControl { for (BindingPattern excludedBinding : excludedBindings) { http.getBindings().add(createAccessControlExcludedBinding(excludedBinding)); } - for (Handler<?> handler : handlers) { + for (Handler handler : handlers) { if (isExcludedHandler(handler)) { for (BindingPattern binding : handler.getServerBindings()) { http.getBindings().add(createAccessControlExcludedBinding(binding)); @@ -188,9 +188,9 @@ public class AccessControl { private static Chain<Filter> createChain(ComponentId id) { return new Chain<>(FilterChains.emptyChainSpec(id)); } - private static boolean isExcludedHandler(Handler<?> handler) { return EXCLUDED_HANDLERS.contains(handler.getClassId().getName()); } + private static boolean isExcludedHandler(Handler handler) { return EXCLUDED_HANDLERS.contains(handler.getClassId().getName()); } - private static boolean hasNonMbusBinding(Handler<?> handler) { + private static boolean hasNonMbusBinding(Handler handler) { return handler.getServerBindings().stream().anyMatch(binding -> ! binding.scheme().equals("mbus")); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java index 3ac12381a1f..7a0b6c8e023 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java @@ -25,7 +25,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import static com.yahoo.vespa.model.container.PlatformBundles.searchAndDocprocBundle; +import static com.yahoo.vespa.model.container.PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE; /** * @author gjoranv @@ -56,9 +56,9 @@ public class ContainerSearch extends ContainerSubsystem<SearchChains> this.owningCluster = cluster; this.options = options; - owningCluster.addComponent(Component.fromClassAndBundle(QUERY_PROFILE_REGISTRY_CLASS, searchAndDocprocBundle)); - owningCluster.addComponent(Component.fromClassAndBundle(com.yahoo.search.schema.SchemaInfo.class.getName(), searchAndDocprocBundle)); - owningCluster.addComponent(Component.fromClassAndBundle(SearchStatusExtension.class.getName(), searchAndDocprocBundle)); + owningCluster.addComponent(Component.fromClassAndBundle(QUERY_PROFILE_REGISTRY_CLASS, SEARCH_AND_DOCPROC_BUNDLE)); + owningCluster.addComponent(Component.fromClassAndBundle(com.yahoo.search.schema.SchemaInfo.class.getName(), SEARCH_AND_DOCPROC_BUNDLE)); + owningCluster.addComponent(Component.fromClassAndBundle(SearchStatusExtension.class.getName(), SEARCH_AND_DOCPROC_BUNDLE)); cluster.addSearchAndDocprocBundles(); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java index 6edcd8901de..44c60cf0619 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java @@ -32,7 +32,7 @@ public class DispatcherComponent extends Component<AbstractConfigProducer<?>, Co String dispatcherComponentId = "dispatcher." + indexedSearchCluster.getClusterName(); // used by ClusterSearcher return new ComponentModel(dispatcherComponentId, com.yahoo.search.dispatch.Dispatcher.class.getName(), - PlatformBundles.searchAndDocprocBundle); + PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE); } @Override diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java index 7087cabafc1..b00faa80715 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/GUIHandler.java @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container.search; -import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.vespa.model.container.component.Handler; @@ -10,7 +9,7 @@ import com.yahoo.vespa.model.container.component.Handler; /** * @author Henrik Høiness */ -public class GUIHandler extends Handler<AbstractConfigProducer<?>> { +public class GUIHandler extends Handler { public static final String BUNDLE = "container-search-gui"; public static final String CLASS = "com.yahoo.search.query.gui.GUIHandler"; @@ -21,7 +20,7 @@ public class GUIHandler extends Handler<AbstractConfigProducer<?>> { } public static BundleInstantiationSpecification bundleSpec(String className, String bundle) { - return BundleInstantiationSpecification.getFromStrings(className, className, bundle); + return BundleInstantiationSpecification.fromStrings(className, className, bundle); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java index 956d551f6b3..b4b2af3c808 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java @@ -13,6 +13,6 @@ public class RpcResourcePoolComponent extends Component<RpcResourcePoolComponent private static ComponentModel toComponentModel(String clusterName) { String componentId = "rpcresourcepool." + clusterName; - return new ComponentModel(componentId, com.yahoo.search.dispatch.rpc.RpcResourcePool.class.getName(), PlatformBundles.searchAndDocprocBundle); + return new ComponentModel(componentId, com.yahoo.search.dispatch.rpc.RpcResourcePool.class.getName(), PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java index fbdefd0afb0..cdea63cfb70 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java @@ -10,6 +10,8 @@ import org.w3c.dom.Element; import java.util.Arrays; import java.util.List; +import static com.yahoo.vespa.model.container.component.chain.ProcessingHandler.PROCESSING_HANDLER_CLASS; + /** * This object builds a bundle instantiation spec from an XML element. * @@ -30,7 +32,7 @@ public class BundleInstantiationSpecificationBuilder { private static BundleInstantiationSpecification setBundleForSearchAndDocprocComponents(BundleInstantiationSpecification spec) { if (PlatformBundles.isSearchAndDocprocClass(spec.getClassName())) - return spec.inBundle(PlatformBundles.searchAndDocprocBundle); + return spec.inBundle(PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE); else return spec; } @@ -39,7 +41,7 @@ public class BundleInstantiationSpecificationBuilder { private static void validate(BundleInstantiationSpecification instSpec) { List<String> forbiddenClasses = Arrays.asList( SearchHandler.HANDLER_CLASS, - "com.yahoo.processing.handler.ProcessingHandler"); + PROCESSING_HANDLER_CLASS); for (String forbiddenClass: forbiddenClasses) { if (forbiddenClass.equals(instSpec.getClassName())) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/CloudSecretStore.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/CloudSecretStore.java index 18916f1e09b..373eb9714d3 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/CloudSecretStore.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/CloudSecretStore.java @@ -21,7 +21,7 @@ public class CloudSecretStore extends SimpleComponent implements SecretStoreConf private final List<StoreConfig> configList; public CloudSecretStore() { - super(new ComponentModel(BundleInstantiationSpecification.getFromStrings(CLASS, CLASS, BUNDLE))); + super(new ComponentModel(BundleInstantiationSpecification.fromStrings(CLASS, CLASS, BUNDLE))); configList = new ArrayList<>(); } 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 6d0b5085ce6..8f02476b2cc 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 @@ -30,6 +30,7 @@ import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.Zone; +import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.logging.FileConnectionLog; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.schema.OnnxModel; @@ -222,7 +223,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } if (deployState.zone().system().isPublic()) { BindingPattern bindingPattern = SystemBindingPattern.fromHttpPath("/validate-secret-store"); - Handler<AbstractConfigProducer<?>> handler = new Handler<>( + Handler handler = new Handler( new ComponentModel("com.yahoo.jdisc.cloud.aws.AwsParameterStoreValidationHandler", null, "jdisc-cloud-aws", null)); handler.addServerBindings(bindingPattern); cluster.addComponent(handler); @@ -886,8 +887,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private void addSearchHandler(ApplicationContainerCluster cluster, Element searchElement) { // Magic spell is needed to receive the chains config :-| - cluster.addComponent(new ProcessingHandler<>(cluster.getSearch().getChains(), - "com.yahoo.search.searchchain.ExecutionFactory")); + cluster.addComponent(new ProcessingHandler<>( + cluster.getSearch().getChains(), + BundleInstantiationSpecification.fromSearchAndDocproc("com.yahoo.search.searchchain.ExecutionFactory"))); cluster.addComponent( new SearchHandler( @@ -897,7 +899,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } private void addGUIHandler(ApplicationContainerCluster cluster) { - Handler<?> guiHandler = new GUIHandler(); + Handler guiHandler = new GUIHandler(); guiHandler.addServerBindings(SystemBindingPattern.fromHttpPath(GUIHandler.BINDING_PATH)); cluster.addComponent(guiHandler); } @@ -928,8 +930,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { if (documentApiElement == null) return null; ContainerDocumentApi.HandlerOptions documentApiOptions = DocumentApiOptionsBuilder.build(documentApiElement); + Element ignoreUndefinedFields = XML.getChild(documentApiElement, "ignore-undefined-fields"); return new ContainerDocumentApi(cluster, documentApiOptions, - documentApiElement.getAttribute("ignore-undefined-fields").equals("true")); + "true".equals(XML.getValue(ignoreUndefinedFields))); } private ContainerDocproc buildDocproc(DeployState deployState, ApplicationContainerCluster cluster, Element spec) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java index 54d943f498a..7e1b3be9240 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java @@ -1,9 +1,11 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container.xml; +import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.handler.threadpool.ContainerThreadpoolConfig; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerThreadpool; +import com.yahoo.vespa.model.container.PlatformBundles; import com.yahoo.vespa.model.container.component.BindingPattern; import com.yahoo.vespa.model.container.component.SystemBindingPattern; import com.yahoo.vespa.model.container.component.chain.ProcessingHandler; @@ -11,6 +13,8 @@ import com.yahoo.vespa.model.container.search.searchchain.SearchChains; import java.util.List; +import static com.yahoo.container.bundle.BundleInstantiationSpecification.fromSearchAndDocproc; + /** * Component definition for {@link com.yahoo.search.handler.SearchHandler} * @@ -19,38 +23,32 @@ import java.util.List; class SearchHandler extends ProcessingHandler<SearchChains> { static final String HANDLER_CLASS = com.yahoo.search.handler.SearchHandler.class.getName(); + static final BundleInstantiationSpecification HANDLER_SPEC = fromSearchAndDocproc(HANDLER_CLASS); static final BindingPattern DEFAULT_BINDING = SystemBindingPattern.fromHttpPath("/search/*"); SearchHandler(ApplicationContainerCluster cluster, List<BindingPattern> bindings, ContainerThreadpool.UserOptions threadpoolOptions) { - super(cluster.getSearchChains(), HANDLER_CLASS); + super(cluster.getSearchChains(), HANDLER_SPEC, new Threadpool(threadpoolOptions)); bindings.forEach(this::addServerBindings); - Threadpool threadpool = new Threadpool(cluster, threadpoolOptions); - inject(threadpool); - addComponent(threadpool); } + private static class Threadpool extends ContainerThreadpool { - private final ApplicationContainerCluster cluster; - Threadpool(ApplicationContainerCluster cluster, UserOptions options) { + Threadpool(UserOptions options) { super("search-handler", options); - this.cluster = cluster; } @Override - public void getConfig(ContainerThreadpoolConfig.Builder builder) { - super.getConfig(builder); - - builder.maxThreadExecutionTimeSeconds(190); - builder.keepAliveTime(5.0); - - // User options overrides below configuration - if (hasUserOptions()) return; - builder.maxThreads(-2).minThreads(-2).queueSize(-40); + public void setDefaultConfigValues(ContainerThreadpoolConfig.Builder builder) { + builder.maxThreadExecutionTimeSeconds(190) + .keepAliveTime(5.0) + .maxThreads(-2) + .minThreads(-2) + .queueSize(-40); } - } + } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/document/DocumentFactoryBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/document/DocumentFactoryBuilder.java index 5df862647a0..7f3d7981bb6 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/document/DocumentFactoryBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/document/DocumentFactoryBuilder.java @@ -31,7 +31,7 @@ public class DocumentFactoryBuilder { String concDocFactory=pkg+"."+CONCRETE_DOC_FACTORY_CLASS; String bundle = e.getAttribute("bundle"); Component<AbstractConfigProducer<?>, ComponentModel> component = new Component<>( - new ComponentModel(BundleInstantiationSpecification.getFromStrings(concDocFactory, concDocFactory, bundle))); + new ComponentModel(BundleInstantiationSpecification.fromStrings(concDocFactory, concDocFactory, bundle))); if (!cluster.getComponentsMap().containsKey(component.getComponentId())) cluster.addComponent(component); types.put(type, concDocFactory); } diff --git a/config-model/src/test/derived/imported_struct_fields/summarymap.cfg b/config-model/src/test/derived/imported_struct_fields/summarymap.cfg index cd19a75e85f..653b0a26b75 100644 --- a/config-model/src/test/derived/imported_struct_fields/summarymap.cfg +++ b/config-model/src/test/derived/imported_struct_fields/summarymap.cfg @@ -1,4 +1,7 @@ defaultoutputclass -1 +override[].field "documentid" +override[].command "documentid" +override[].arguments "" override[].field "my_elem_array" override[].command "attributecombiner" override[].arguments "" diff --git a/config-model/src/test/java/com/yahoo/schema/derived/SummaryMapTestCase.java b/config-model/src/test/java/com/yahoo/schema/derived/SummaryMapTestCase.java index f0fc58b97e5..2f1b837554d 100644 --- a/config-model/src/test/java/com/yahoo/schema/derived/SummaryMapTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/derived/SummaryMapTestCase.java @@ -162,23 +162,42 @@ public class SummaryMapTestCase extends AbstractSchemaTestCase { assertFalse(SummaryMap.isDynamicCommand(SummaryTransform.ATTRIBUTE.getName())); } + @Test + public void documentid_summary_field_has_corresponding_summary_transform() throws ParseException { + var schema = buildSchema("field foo type string { indexing: summary }", + joinLines("document-summary bar {", + " summary documentid type string {}", + "}")); + assertOverride(schema, "documentid", SummaryTransform.DOCUMENT_ID.getName(), ""); + } + + @Test + public void documentid_summary_transform_requires_disk_access() { + assertFalse(SummaryTransform.DOCUMENT_ID.isInMemory()); + } + private void assertOverride(String fieldContent, String expFieldName, String expCommand) throws ParseException { - var summaryMap = new SummaryMap(buildSearch(fieldContent)); + assertOverride(buildSchema(fieldContent, ""), expFieldName, expCommand, expFieldName); + } + + private void assertOverride(Schema schema, String expFieldName, String expCommand, String expArguments) throws ParseException { + var summaryMap = new SummaryMap(schema); var cfgBuilder = new SummarymapConfig.Builder(); summaryMap.getConfig(cfgBuilder); var cfg = new SummarymapConfig(cfgBuilder); var override = cfg.override(0); assertEquals(expFieldName, override.field()); assertEquals(expCommand, override.command()); - assertEquals(expFieldName, override.arguments()); + assertEquals(expArguments, override.arguments()); } - private Schema buildSearch(String field) throws ParseException { + private Schema buildSchema(String field, String documentSummary) throws ParseException { var builder = new ApplicationBuilder(new RankProfileRegistry()); builder.addSchema(joinLines("search test {", " document test {", field, " }", + documentSummary, "}")); builder.build(true); return builder.getSchema(); diff --git a/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionTypeResolverTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionTypeResolverTestCase.java index 4b6a22fc81a..f2f2a82b97c 100644 --- a/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionTypeResolverTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionTypeResolverTestCase.java @@ -456,7 +456,7 @@ public class RankingExpressionTypeResolverTestCase { builder.build(true); String message = logger.findMessage("The following query features"); assertNotNull(message); - assertEquals("WARNING: The following query features used in rank profile 'my_rank_profile' are not declared in query profile types and " + + assertEquals("WARNING: The following query features used in rank profile 'my_rank_profile' are not declared and " + "will be interpreted as scalars, not tensors: [query(bar), query(baz), query(foo)]", message); } diff --git a/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionWithOnnxTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionWithOnnxTestCase.java index 94a51d25717..bfd0520c62a 100644 --- a/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionWithOnnxTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/processing/RankingExpressionWithOnnxTestCase.java @@ -391,7 +391,7 @@ public class RankingExpressionWithOnnxTestCase { @Override public ApplicationFile getFile(Path file) { - return new MockApplicationFile(file, Path.fromString(root().toString())); + return new MockApplicationFile(file, root()); } @Override diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java index 7bf08461df7..d50aedeafa0 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java @@ -70,7 +70,7 @@ public class MetricsProxyContainerClusterTest { @Test public void http_handlers_are_set_up() { VespaModel model = getModel(servicesWithAdminOnly(), self_hosted); - Collection<Handler<?>> handlers = model.getAdmin().getMetricsProxyCluster().getHandlers(); + Collection<Handler> handlers = model.getAdmin().getMetricsProxyCluster().getHandlers(); Collection<ComponentSpecification> handlerClasses = handlers.stream().map(Component::getClassId).collect(toList()); assertTrue(handlerClasses.contains(ComponentSpecification.fromString(MetricsV1Handler.class.getName()))); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexFieldsValidatorTestCase.java index 85050aa0cf9..11e4ae855a5 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexFieldsValidatorTestCase.java @@ -22,7 +22,7 @@ import static com.yahoo.config.model.test.TestUtil.joinLines; /** * @author geirst */ -public class ComplexAttributeFieldsValidatorTestCase { +public class ComplexFieldsValidatorTestCase { @SuppressWarnings("deprecation") @Rule @@ -78,6 +78,30 @@ public class ComplexAttributeFieldsValidatorTestCase { } @Test + public void throws_when_complex_fields_have_struct_fields_with_index() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("For cluster 'mycluster', schema 'test': " + + "The following complex fields have struct fields with 'indexing: index' which is not supported: " + + "topics (topics.id, topics.label). Change to 'indexing: attribute' instead"); + createModelAndValidate(joinLines( + "schema test {", + "document test {", + "struct topic {", + " field id type string {}", + " field label type string {}", + " field desc type string {}", + "}", + "field topics type array<topic> {", + " indexing: summary", + " struct-field id { indexing: index }", + " struct-field label { indexing: index | attribute }", + " struct-field desc { indexing: attribute }", + "}", + "}", + "}")); + } + + @Test public void validation_passes_when_only_supported_struct_field_attributes_are_used() throws IOException, SAXException { createModelAndValidate(joinLines("search test {", " document test {", 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 8750d3caa47..5e746d462b5 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("Deployment would make your tenant exceed its quota and has been blocked! Please contact support to update your plan.", e.getMessage()); + assertEquals("Deployment exceeds its quota and has been blocked! Please contact support to update your plan: Quota is $1.25, but at least $1.63 is required", e.getMessage()); } } @@ -58,7 +58,7 @@ public class QuotaValidatorTest { tester.deploy(null, getServices("testCluster", 10), Environment.prod, null); fail(); } catch (RuntimeException e) { - assertEquals("publiccd: Deployment would make your tenant exceed its quota and has been blocked! Please contact support to update your plan.", e.getMessage()); + assertEquals("publiccd: Deployment exceeds its quota and has been blocked! Please contact support to update your plan: Quota is $1.00, but at least $1.63 is required", e.getMessage()); } } @@ -69,7 +69,7 @@ public class QuotaValidatorTest { tester.deploy(null, getServices("testCluster", 10), Environment.prod, null); fail(); } catch (RuntimeException e) { - assertEquals("publiccd: Deployment would make your tenant exceed its quota and has been blocked! Please contact support to update your plan.", e.getMessage()); + assertEquals("publiccd: Deployment exceeds its quota and has been blocked! Please contact support to update your plan: Quota is $1.25, but at least $1.63 is required", e.getMessage()); } } @@ -82,7 +82,7 @@ public class QuotaValidatorTest { 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! ($--.--)", + assertEquals("Please free up some capacity: Quota is $--.--, but at least $-.-- is required", ValidationTester.censorNumbers(e.getMessage())); } } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2BuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2BuilderTest.java index 1a41d2689a2..072f88602a6 100755 --- a/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2BuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV2BuilderTest.java @@ -36,16 +36,6 @@ public class DomAdminV2BuilderTest extends DomBuilderTest { root = new MockRoot("root"); } - // Supported for backwards compatibility - private Element servicesConfigserver() { - return XML.getDocument( - "<admin version=\"2.0\">" + - " <configserver hostalias=\"mockhost\"/>" + - " <adminserver hostalias=\"mockhost\"/>" + - "</admin>").getDocumentElement(); - - } - private Element servicesOverride() { return XML.getDocument( "<admin version=\"2.0\">" + @@ -131,15 +121,6 @@ public class DomAdminV2BuilderTest extends DomBuilderTest { } /** - * Tests that configserver works (deprecated, but allowed in admin 2.0) - */ - @Test - public void adminWithConfigserverElement() { - Admin admin = buildAdmin(servicesConfigserver()); - assertEquals(1, admin.getConfigservers().size()); - } - - /** * Tests that configservers/configserver works */ @Test diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessLogTest.java index 81b6de8f2ee..d9659cf92d4 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 @@ -89,7 +89,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase { assertNotNull(getVespaAccessLog("default")); { // vespa - Component<?, ?> accessLogComponent = getContainerComponent("default", VespaAccessLog.class.getName()); + Component<?, ?> accessLogComponent = getComponent("default", VespaAccessLog.class.getName()); assertNotNull(accessLogComponent); assertEquals(VespaAccessLog.class.getName(), accessLogComponent.getClassId().getName(), VespaAccessLog.class.getName()); AccessLogConfig config = root.getConfig(AccessLogConfig.class, "default/component/com.yahoo.container.logging.VespaAccessLog"); @@ -101,7 +101,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase { } { // json - Component<?, ?> accessLogComponent = getContainerComponent("default", JSONAccessLog.class.getName()); + Component<?, ?> accessLogComponent = getComponent("default", JSONAccessLog.class.getName()); assertNotNull(accessLogComponent); assertEquals(JSONAccessLog.class.getName(), accessLogComponent.getClassId().getName(), JSONAccessLog.class.getName()); AccessLogConfig config = root.getConfig(AccessLogConfig.class, "default/component/com.yahoo.container.logging.JSONAccessLog"); @@ -124,7 +124,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase { nodesXml, "</container>" ); createModel(root, clusterElem); - Component<?, ?> connectionLogComponent = getContainerComponent("default", FileConnectionLog.class.getName()); + Component<?, ?> connectionLogComponent = getComponent("default", FileConnectionLog.class.getName()); assertNotNull(connectionLogComponent); ConnectionLogConfig config = root.getConfig(ConnectionLogConfig.class, "default/component/com.yahoo.container.logging.FileConnectionLog"); assertEquals("default", config.cluster()); @@ -140,7 +140,7 @@ public class AccessLogTest extends ContainerModelBuilderTestBase { nodesXml, "</container>" ); createModel(root, clusterElem); - Component<?, ?> fileConnectionLogComponent = getContainerComponent("default", FileConnectionLog.class.getName()); + Component<?, ?> fileConnectionLogComponent = getComponent("default", FileConnectionLog.class.getName()); assertNull(fileConnectionLogComponent); } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java index 686f7bbd1f1..125f1823fec 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java @@ -29,7 +29,7 @@ public class BundleInstantiationSpecificationBuilderTest { @Test public void bundle_is_replaced_for_internal_class() { String internalClass = GroupingValidator.class.getName(); - verifyExpectedBundle(internalClass, null, PlatformBundles.searchAndDocprocBundle); + verifyExpectedBundle(internalClass, null, PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE); } @Test diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java index ca0b4681e51..4a9d44ea2f8 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerDocumentApiBuilderTest.java @@ -30,11 +30,11 @@ import static org.junit.Assert.assertTrue; */ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBase { - private Map<String, Handler<?>> getHandlers(String clusterName) { + private Map<String, Handler> getHandlers(String clusterName) { ContainerCluster<?> cluster = (ContainerCluster<?>) root.getChildren().get(clusterName); - Map<String, Handler<?>> handlerMap = new HashMap<>(); - Collection<Handler<?>> handlers = cluster.getHandlers(); - for (Handler<?> handler : handlers) { + Map<String, Handler> handlerMap = new HashMap<>(); + Collection<Handler> handlers = cluster.getHandlers(); + for (Handler handler : handlers) { assertFalse(handlerMap.containsKey(handler.getComponentId().toString())); //die on overwrites handlerMap.put(handler.getComponentId().toString(), handler); } @@ -56,7 +56,7 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa } private void verifyCustomBindings(String id) { - Handler<?> handler = getHandlers("cluster1").get(id); + Handler handler = getHandlers("cluster1").get(id); assertTrue(handler.getServerBindings().contains(UserBindingPattern.fromHttpPath("/document-api/reserved-for-internal-use/feedapi"))); assertTrue(handler.getServerBindings().contains(UserBindingPattern.fromHttpPath("/document-api/reserved-for-internal-use/feedapi/"))); @@ -73,7 +73,7 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa "</container>"); createModel(root, elem); - Map<String, Handler<?>> handlerMap = getHandlers("cluster1"); + Map<String, Handler> handlerMap = getHandlers("cluster1"); assertNotNull(handlerMap.get("com.yahoo.container.handler.VipStatusHandler")); assertNotNull(handlerMap.get("com.yahoo.container.handler.observability.ApplicationStatusHandler")); @@ -91,8 +91,10 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa public void nonexisting_fields_can_be_ignored() { Element elem = DomBuilderTest.parse( "<container id='cluster1' version='1.0'>", - " <document-api ignore-undefined-fields='true' />", + " <document-api>" + + " <ignore-undefined-fields>true</ignore-undefined-fields>" + nodesXml, + " </document-api>" + "</container>"); ContainerModel model = createModel(root, elem).get(0); @@ -110,8 +112,8 @@ public class ContainerDocumentApiBuilderTest extends ContainerModelBuilderTestBa "</container>"); root = new MockRoot("root", new MockApplicationPackage.Builder().build()); createModel(root, elem); - Map<String, Handler<?>> handlers = getHandlers("cluster1"); - Handler<?> feedApiHandler = handlers.get("com.yahoo.vespa.http.server.FeedHandler"); + Map<String, Handler> handlers = getHandlers("cluster1"); + Handler feedApiHandler = handlers.get("com.yahoo.vespa.http.server.FeedHandler"); Set<String> injectedComponentIds = feedApiHandler.getInjectedComponentIds(); assertTrue(injectedComponentIds.contains("threadpool@feedapi-handler")); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java index 3a241d9607f..1d43c031a1b 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java @@ -42,6 +42,7 @@ import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.ContainerModelEvaluation; import com.yahoo.vespa.model.container.component.Component; +import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.content.utils.ContentClusterUtils; import com.yahoo.vespa.model.test.VespaModelTester; import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithFilePkg; @@ -60,19 +61,15 @@ import java.util.stream.Collectors; import static com.yahoo.config.model.test.TestUtil.joinLines; import static com.yahoo.test.LinePatternMatcher.containsLineWithPattern; import static com.yahoo.vespa.defaults.Defaults.getDefaults; -import static com.yahoo.vespa.model.container.ContainerCluster.ROOT_HANDLER_BINDING; -import static com.yahoo.vespa.model.container.ContainerCluster.STATE_HANDLER_BINDING_1; -import static org.hamcrest.CoreMatchers.is; +import static com.yahoo.vespa.model.container.component.chain.ProcessingHandler.PROCESSING_HANDLER_CLASS; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -198,6 +195,17 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { } @Test + public void builtin_handlers_get_default_threadpool() { + createBasicContainerModel(); + + Handler h1 = getHandler("default", ApplicationStatusHandler.class.getName()); + assertTrue(h1.getInjectedComponentIds().contains("threadpool@default-handler-common")); + + Handler h2 = getHandler("default", BindingsOverviewHandler.class.getName()); + assertTrue(h2.getInjectedComponentIds().contains("threadpool@default-handler-common")); + } + + @Test public void verify_bindings_for_builtin_handlers() { createBasicContainerModel(); JdiscBindingsConfig config = root.getConfig(JdiscBindingsConfig.class, "default/container.0"); @@ -216,68 +224,6 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { } @Test - public void default_root_handler_binding_can_be_stolen_by_user_configured_handler() { - Element clusterElem = DomBuilderTest.parse( - "<container id='default' version='1.0'>" + - " <handler id='userRootHandler'>" + - " <binding>" + ROOT_HANDLER_BINDING.patternString() + "</binding>" + - " </handler>" + - "</container>"); - createModel(root, clusterElem); - - // The handler is still set up. - ComponentsConfig.Components userRootHandler = getComponent(componentsConfig(), BindingsOverviewHandler.class.getName()); - assertNotNull(userRootHandler); - - // .. but it has no bindings - var discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default"); - assertNull(discBindingsConfig.handlers(BindingsOverviewHandler.class.getName())); - } - - @Test - public void reserved_binding_cannot_be_stolen_by_user_configured_handler() { - Element clusterElem = DomBuilderTest.parse( - "<container id='default' version='1.0'>" + - " <handler id='userHandler'>" + - " <binding>" + STATE_HANDLER_BINDING_1.patternString() + "</binding>" + - " </handler>" + - "</container>"); - try { - createModel(root, clusterElem); - fail("Expected exception when stealing a reserved binding."); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), is("Binding 'http://*/state/v1' is a reserved Vespa binding " + - "and cannot be used by handler: userHandler")); - } - } - - @Test - public void handler_bindings_are_included_in_discBindings_config() { - createClusterWithJDiscHandler(); - String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString(); - assertThat(discBindingsConfig, containsString(".serverBindings[0] \"http://*/binding0\"")); - assertThat(discBindingsConfig, containsString(".serverBindings[1] \"http://*/binding1\"")); - } - - @Test - public void handlers_are_included_in_components_config() { - createClusterWithJDiscHandler(); - assertThat(componentsConfig().toString(), containsString(".id \"discHandler\"")); - } - - private void createClusterWithJDiscHandler() { - Element clusterElem = DomBuilderTest.parse( - "<container id='default' version='1.0'>", - " <handler id='discHandler'>", - " <binding>http://*/binding0</binding>", - " <binding>http://*/binding1</binding>", - " </handler>", - "</container>"); - - createModel(root, clusterElem); - } - - @Test public void processing_handler_bindings_can_be_overridden() { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>", @@ -324,12 +270,19 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { @Test public void processingHandler_gets_only_processing_chains_in_chains_config() { createClusterWithProcessingAndSearchChains(); - String processingHandlerConfigId = "default/component/com.yahoo.processing.handler.ProcessingHandler"; + String processingHandlerConfigId = "default/component/" + PROCESSING_HANDLER_CLASS; String chainsConfig = getChainsConfig(processingHandlerConfigId); assertThat(chainsConfig, containsLineWithPattern(".*\\.id \"testProcessor@default\"$")); assertThat(chainsConfig, not(containsLineWithPattern(".*\\.id \"testSearcher@default\"$"))); } + @Test + public void processingHandler_is_instantiated_from_the_default_bundle() { + createClusterWithProcessingAndSearchChains(); + ComponentsConfig.Components config = getComponentInConfig(componentsConfig(), PROCESSING_HANDLER_CLASS); + assertEquals(PROCESSING_HANDLER_CLASS, config.bundle()); + } + private void createClusterWithProcessingAndSearchChains() { Element clusterElem = DomBuilderTest.parse( "<container id='default' version='1.0'>" + @@ -375,20 +328,6 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { } @Test - public void nested_components_are_injected_to_handlers() { - Element clusterElem = DomBuilderTest.parse( - "<container id='default' version='1.0'>", - " <handler id='myHandler'>", - " <component id='injected' />", - " </handler>", - "</container>"); - - createModel(root, clusterElem); - Component<?,?> handler = getContainerComponent("default", "myHandler"); - assertThat(handler.getInjectedComponentIds(), hasItem("injected@myHandler")); - } - - @Test public void component_includes_are_added() { VespaModelCreatorWithFilePkg creator = new VespaModelCreatorWithFilePkg("src/test/cfg/application/include_dirs"); VespaModel model = creator.create(true); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java index 3d7b17d37e0..dc831ef863d 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTestBase.java @@ -13,6 +13,7 @@ import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.ContainerModel; import com.yahoo.vespa.model.container.component.Component; +import com.yahoo.vespa.model.container.component.Handler; import com.yahoo.vespa.model.container.search.ContainerSearch; import org.junit.Before; import org.w3c.dom.Element; @@ -94,7 +95,7 @@ public abstract class ContainerModelBuilderTestBase { return root.getConfig(ComponentsConfig.class, "default"); } - protected ComponentsConfig.Components getComponent(ComponentsConfig componentsConfig, String id) { + protected ComponentsConfig.Components getComponentInConfig(ComponentsConfig componentsConfig, String id) { for (ComponentsConfig.Components component : componentsConfig.components()) { if (component.id().equals(id)) return component; @@ -106,11 +107,18 @@ public abstract class ContainerModelBuilderTestBase { return (ApplicationContainerCluster) root.getChildren().get(clusterId); } - public Component<?, ?> getContainerComponent(String clusterId, String componentId) { + public Component<?, ?> getComponent(String clusterId, String componentId) { return getContainerCluster(clusterId).getComponentsMap().get( ComponentId.fromString(componentId)); } + public Handler getHandler(String clusterId, String componentId) { + Component<?,?> component = getComponent(clusterId, componentId); + if (! (component instanceof Handler)) + throw new RuntimeException("Component is not a handler: " + componentId); + return (Handler) component; + } + void assertComponentConfigured(ApplicationContainerCluster cluster, String componentId) { Component<?, ?> component = cluster.getComponentsMap().get(ComponentId.fromString(componentId)); assertNotNull(component); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java new file mode 100644 index 00000000000..42cda0d8034 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java @@ -0,0 +1,118 @@ +package com.yahoo.vespa.model.container.xml; + +import com.yahoo.config.model.builder.xml.test.DomBuilderTest; +import com.yahoo.container.ComponentsConfig; +import com.yahoo.container.jdisc.JdiscBindingsConfig; +import com.yahoo.container.usability.BindingsOverviewHandler; +import com.yahoo.vespa.model.container.ApplicationContainerCluster; +import com.yahoo.vespa.model.container.component.Component; +import com.yahoo.vespa.model.container.component.Handler; +import org.junit.Test; +import org.w3c.dom.Element; + +import static com.yahoo.vespa.model.container.ContainerCluster.ROOT_HANDLER_BINDING; +import static com.yahoo.vespa.model.container.ContainerCluster.STATE_HANDLER_BINDING_1; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Tests for container model building with custom handlers. + * + * @author gjoranv + */ +public class HandlerBuilderTest extends ContainerModelBuilderTestBase { + + @Test + public void handlers_are_included_in_components_config() { + createClusterWithJDiscHandler(); + assertThat(componentsConfig().toString(), containsString(".id \"discHandler\"")); + } + + @Test + public void handler_bindings_are_included_in_discBindings_config() { + createClusterWithJDiscHandler(); + String discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default").toString(); + assertThat(discBindingsConfig, containsString(".serverBindings[0] \"http://*/binding0\"")); + assertThat(discBindingsConfig, containsString(".serverBindings[1] \"http://*/binding1\"")); + } + + @Test + public void nested_components_are_injected_to_handlers() { + Element clusterElem = DomBuilderTest.parse( + "<container id='default' version='1.0'>", + " <handler id='myHandler'>", + " <component id='injected' />", + " </handler>", + "</container>"); + + createModel(root, clusterElem); + Component<?,?> handler = getComponent("default", "myHandler"); + assertThat(handler.getInjectedComponentIds(), hasItem("injected@myHandler")); + } + + @Test + public void default_root_handler_binding_can_be_stolen_by_user_configured_handler() { + Element clusterElem = DomBuilderTest.parse( + "<container id='default' version='1.0'>" + + " <handler id='userRootHandler'>" + + " <binding>" + ROOT_HANDLER_BINDING.patternString() + "</binding>" + + " </handler>" + + "</container>"); + createModel(root, clusterElem); + + // The handler is still set up. + ComponentsConfig.Components userRootHandler = getComponentInConfig(componentsConfig(), BindingsOverviewHandler.class.getName()); + assertNotNull(userRootHandler); + + // .. but it has no bindings + var discBindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default"); + assertNull(discBindingsConfig.handlers(BindingsOverviewHandler.class.getName())); + } + + @Test + public void reserved_binding_cannot_be_stolen_by_user_configured_handler() { + Element clusterElem = DomBuilderTest.parse( + "<container id='default' version='1.0'>" + + " <handler id='userHandler'>" + + " <binding>" + STATE_HANDLER_BINDING_1.patternString() + "</binding>" + + " </handler>" + + "</container>"); + try { + createModel(root, clusterElem); + fail("Expected exception when stealing a reserved binding."); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("Binding 'http://*/state/v1' is a reserved Vespa binding " + + "and cannot be used by handler: userHandler")); + } + } + + @Test + public void custom_handler_gets_default_threadpool() { + createClusterWithJDiscHandler(); + ApplicationContainerCluster cluster = (ApplicationContainerCluster)root.getChildren().get("default"); + Handler handler = cluster.getHandlers().stream() + .filter(h -> h.getComponentId().toString().equals("discHandler")) + .findAny().orElseThrow(); + + assertTrue(handler.getInjectedComponentIds().contains("threadpool@default-handler-common")); + } + + private void createClusterWithJDiscHandler() { + Element clusterElem = DomBuilderTest.parse( + "<container id='default' version='1.0'>", + " <handler id='discHandler'>", + " <binding>http://*/binding0</binding>", + " <binding>http://*/binding1</binding>", + " </handler>", + "</container>"); + + createModel(root, clusterElem); + } + +} diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ApplicationBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java index ca59e053a89..c41373b9f85 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ApplicationBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/SearchBuilderTest.java @@ -31,7 +31,7 @@ import static org.junit.Assert.fail; /** * @author gjoranv */ -public class ApplicationBuilderTest extends ContainerModelBuilderTestBase { +public class SearchBuilderTest extends ContainerModelBuilderTestBase { private ChainsConfig chainsConfig() { return root.getConfig(ChainsConfig.class, "default/component/com.yahoo.search.handler.SearchHandler"); @@ -53,7 +53,7 @@ public class ApplicationBuilderTest extends ContainerModelBuilderTestBase { ApplicationContainerCluster cluster = (ApplicationContainerCluster)root.getChildren().get("default"); GUIHandler guiHandler = null; - for (Handler<?> handler : cluster.getHandlers()) { + for (Handler handler : cluster.getHandlers()) { if (handler instanceof GUIHandler) { guiHandler = (GUIHandler) handler; } @@ -230,12 +230,7 @@ public class ApplicationBuilderTest extends ContainerModelBuilderTestBase { createModel(root, clusterElem); - ApplicationContainerCluster cluster = (ApplicationContainerCluster)root.getChildren().get("default"); - Handler<?> searchHandler = cluster.getHandlers().stream() - .filter(h -> h.getComponentId().toString().equals(SearchHandler.HANDLER_CLASS)) - .findAny() - .get(); - + Handler searchHandler = getHandler("default", SearchHandler.HANDLER_CLASS); assertTrue(searchHandler.getInjectedComponentIds().contains("threadpool@search-handler")); ContainerThreadpoolConfig config = root.getConfig( diff --git a/config-model/src/test/schema-test-files/services.xml b/config-model/src/test/schema-test-files/services.xml index 4fb81963152..df87d2b9f3d 100644 --- a/config-model/src/test/schema-test-files/services.xml +++ b/config-model/src/test/schema-test-files/services.xml @@ -193,6 +193,9 @@ <handler id="jdisc-handler"> <binding>http://*:*/HelloWorld</binding> <binding>http://*:*/Status</binding> + <component id="injected-to-handler"> + <config name="foo"/> + </component> </handler> <server id="server-provider"> |