summaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java
diff options
context:
space:
mode:
authorHÃ¥kon Hallingstad <hakon@verizonmedia.com>2020-04-20 10:36:33 +0200
committerGitHub <noreply@github.com>2020-04-20 10:36:33 +0200
commitb105eead1fbbcefbb85bc962749f2a12fa660bbe (patch)
tree7a3c996c00b854066d32608a002335715fb98c96 /config-model/src/main/java
parentf61f6c701dc91e839b865f158a6da56ff166def7 (diff)
parent9ab0ef70e9ed4f422df67603f26bcb0c7918fdc4 (diff)
Merge branch 'master' into hakonhall/remove-use-bucket-space-metric-feature-flag
Diffstat (limited to 'config-model/src/main/java')
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java5
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/ConfigDefinitionStore.java1
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java86
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/SearchDocumentModel.java21
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java34
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java61
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/provision/HostsXmlProvisioner.java6
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java89
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java10
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java40
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/MockRoot.java3
-rw-r--r--config-model/src/main/java/com/yahoo/documentmodel/NewDocumentType.java23
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/DocumentModelBuilder.java12
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java4
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/FieldOperationApplierForStructs.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/ImportedFieldsEnumerator.java31
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/Index.java56
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java3
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/Search.java4
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java8
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java11
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java17
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java43
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexSchema.java17
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java27
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/FieldSet.java5
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/HnswIndexParams.java64
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/SDDocumentType.java9
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java77
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/TemporarySDField.java6
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/ExpressionTransforms.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/LightGBMFeatureConverter.java59
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/FieldOperationContainer.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexOperation.java19
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingRewriteOperation.java7
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/AddExtraFieldsToDocument.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java4
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolver.java16
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/TensorFieldProcessor.java58
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/UriHack.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentManager.java10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentTypes.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/ConfigProducer.java21
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/Host.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java42
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java44
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/LogserverContainerCluster.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java24
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java51
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java59
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicMetrics.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/MetricsConsumer.java19
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/SystemMetrics.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java110
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/Metrics.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java40
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/MetricsBuilder.java15
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/AwsAccessControlValidator.java48
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java37
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/DeploymentSpecValidator.java19
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java48
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankingConstantsValidator.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java8
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidator.java39
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java57
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/first/AccessControlOnFirstDeploymentValidator.java33
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomClientProviderBuilder.java11
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java60
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java239
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/VespaDomBuilder.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java35
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/Container.java10
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java30
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/Handler.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java54
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java48
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredFilebasedSslProvider.java28
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java52
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java69
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java21
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java22
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java18
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java137
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java37
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java14
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java25
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilder.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributionConfigProducer.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java78
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java17
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java20
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/NamedSchema.java (renamed from config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java)11
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/SchemaDefinitionXMLHandler.java (renamed from config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinitionXMLHandler.java)10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/SearchCluster.java14
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/StreamingSearchCluster.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/Tuning.java16
114 files changed, 1954 insertions, 952 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java b/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
index a9677d4b34c..4cd0c1815dd 100644
--- a/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
+++ b/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
@@ -133,6 +133,11 @@ public final class XmlHelper {
return Optional.ofNullable(element.getAttribute(name)).filter(s -> !s.isEmpty());
}
+ public static Optional<Element> getOptionalChild(Element parent, String childName) {
+ return Optional.ofNullable(XML.getChild(parent, childName));
+
+ }
+
public static Optional<String> getOptionalChildValue(Element parent, String childName) {
Element child = XML.getChild(parent, childName);
if (child == null) return Optional.empty();
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/ConfigDefinitionStore.java b/config-model/src/main/java/com/yahoo/config/model/deploy/ConfigDefinitionStore.java
index 140cb3001a0..b9ad830090c 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/ConfigDefinitionStore.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/ConfigDefinitionStore.java
@@ -8,7 +8,6 @@ import java.util.Optional;
/**
* @author Ulf Lilleengen
- * @since 5.1
*/
public interface ConfigDefinitionStore {
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
index 7c9e930bb4f..3fb7ba6bc3a 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
@@ -12,10 +12,11 @@ import com.yahoo.config.application.api.UnparsedConfigDefinition;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.model.api.ContainerEndpoint;
+import com.yahoo.config.model.api.EndpointCertificateSecrets;
import com.yahoo.config.model.api.HostProvisioner;
import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
-import com.yahoo.config.model.api.EndpointCertificateSecrets;
+import com.yahoo.config.model.api.Provisioned;
import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.application.provider.MockFileRegistry;
@@ -36,9 +37,8 @@ import com.yahoo.vespa.model.container.search.QueryProfiles;
import com.yahoo.vespa.model.container.search.QueryProfilesBuilder;
import com.yahoo.vespa.model.container.search.SemanticRuleBuilder;
import com.yahoo.vespa.model.container.search.SemanticRules;
-import com.yahoo.vespa.model.search.SearchDefinition;
+import com.yahoo.vespa.model.search.NamedSchema;
-import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
@@ -62,11 +62,12 @@ public class DeployState implements ConfigDefinitionStore {
private final DeployLogger logger;
private final FileRegistry fileRegistry;
private final DocumentModel documentModel;
- private final List<SearchDefinition> searchDefinitions;
+ private final List<NamedSchema> schemas;
private final ApplicationPackage applicationPackage;
private final Optional<ConfigDefinitionRepo> configDefinitionRepo;
private final Optional<ApplicationPackage> permanentApplicationPackage;
private final Optional<Model> previousModel;
+ private final boolean accessLoggingEnabledByDefault;
private final ModelContext.Properties properties;
private final Version vespaVersion;
private final Set<ContainerEndpoint> endpoints;
@@ -76,8 +77,10 @@ public class DeployState implements ConfigDefinitionStore {
private final ImportedMlModels importedModels;
private final ValidationOverrides validationOverrides;
private final Version wantedNodeVespaVersion;
+ private final Optional<String> wantedDockerImageRepo;
private final Instant now;
private final HostProvisioner provisioner;
+ private final Provisioned provisioned;
public static DeployState createTestState() {
return new Builder().build();
@@ -97,18 +100,21 @@ public class DeployState implements ConfigDefinitionStore {
FileRegistry fileRegistry,
DeployLogger deployLogger,
Optional<HostProvisioner> hostProvisioner,
+ Provisioned provisioned,
ModelContext.Properties properties,
Version vespaVersion,
Optional<ApplicationPackage> permanentApplicationPackage,
Optional<ConfigDefinitionRepo> configDefinitionRepo,
- java.util.Optional<Model> previousModel,
+ Optional<Model> previousModel,
Set<ContainerEndpoint> endpoints,
Collection<MlModelImporter> modelImporters,
Zone zone,
QueryProfiles queryProfiles,
SemanticRules semanticRules,
Instant now,
- Version wantedNodeVespaVersion) {
+ Version wantedNodeVespaVersion,
+ boolean accessLoggingEnabledByDefault,
+ Optional<String> wantedDockerImageRepo) {
this.logger = deployLogger;
this.fileRegistry = fileRegistry;
this.rankProfileRegistry = rankProfileRegistry;
@@ -116,8 +122,10 @@ public class DeployState implements ConfigDefinitionStore {
this.properties = properties;
this.vespaVersion = vespaVersion;
this.previousModel = previousModel;
+ this.accessLoggingEnabledByDefault = accessLoggingEnabledByDefault;
this.provisioner = hostProvisioner.orElse(getDefaultModelHostProvisioner(applicationPackage));
- this.searchDefinitions = searchDocumentModel.getSearchDefinitions();
+ this.provisioned = provisioned;
+ this.schemas = searchDocumentModel.getSchemas();
this.documentModel = searchDocumentModel.getDocumentModel();
this.permanentApplicationPackage = permanentApplicationPackage;
this.configDefinitionRepo = configDefinitionRepo;
@@ -137,6 +145,7 @@ public class DeployState implements ConfigDefinitionStore {
this.wantedNodeVespaVersion = wantedNodeVespaVersion;
this.now = now;
+ this.wantedDockerImageRepo = wantedDockerImageRepo;
}
public static HostProvisioner getDefaultModelHostProvisioner(ApplicationPackage applicationPackage) {
@@ -147,6 +156,8 @@ public class DeployState implements ConfigDefinitionStore {
}
}
+ public Provisioned provisioned() { return provisioned; }
+
/** Get the global rank profile registry for this application. */
public final RankProfileRegistry rankProfileRegistry() { return rankProfileRegistry; }
@@ -157,9 +168,7 @@ public class DeployState implements ConfigDefinitionStore {
public final Optional<ConfigDefinition> getConfigDefinition(ConfigDefinitionKey defKey) {
if (existingConfigDefs == null) {
existingConfigDefs = new LinkedHashMap<>();
- if (configDefinitionRepo.isPresent()) {
- existingConfigDefs.putAll(createLazyMapping(configDefinitionRepo.get()));
- }
+ configDefinitionRepo.ifPresent(definitionRepo -> existingConfigDefs.putAll(createLazyMapping(definitionRepo)));
existingConfigDefs.putAll(applicationPackage.getAllExistingConfigDefs());
}
if ( ! existingConfigDefs.containsKey(defKey)) return Optional.empty();
@@ -205,8 +214,8 @@ public class DeployState implements ConfigDefinitionStore {
return applicationPackage;
}
- public List<SearchDefinition> getSearchDefinitions() {
- return searchDefinitions;
+ public List<NamedSchema> getSchemas() {
+ return schemas;
}
public DocumentModel getDocumentModel() {
@@ -217,6 +226,10 @@ public class DeployState implements ConfigDefinitionStore {
return logger;
}
+ public boolean getAccessLoggingEnabledByDefault() {
+ return accessLoggingEnabledByDefault;
+ }
+
public FileRegistry getFileRegistry() {
return fileRegistry;
}
@@ -253,6 +266,8 @@ public class DeployState implements ConfigDefinitionStore {
public Version getWantedNodeVespaVersion() { return wantedNodeVespaVersion; }
+ public Optional<String> getWantedDockerImageRepo() { return wantedDockerImageRepo; }
+
public Instant now() { return now; }
public Optional<EndpointCertificateSecrets> endpointCertificateSecrets() { return properties.endpointCertificateSecrets(); }
@@ -279,6 +294,7 @@ public class DeployState implements ConfigDefinitionStore {
private FileRegistry fileRegistry = new MockFileRegistry();
private DeployLogger logger = new BaseDeployLogger();
private Optional<HostProvisioner> hostProvisioner = Optional.empty();
+ private Provisioned provisioned = new Provisioned();
private Optional<ApplicationPackage> permanentApplicationPackage = Optional.empty();
private ModelContext.Properties properties = new TestProperties();
private Version version = new Version(1, 0, 0);
@@ -289,6 +305,8 @@ public class DeployState implements ConfigDefinitionStore {
private Zone zone = Zone.defaultZone();
private Instant now = Instant.now();
private Version wantedNodeVespaVersion = Vtag.currentVersion;
+ private boolean accessLoggingEnabledByDefault = true;
+ private Optional<String> wantedDockerImageRepo = Optional.empty();
public Builder applicationPackage(ApplicationPackage applicationPackage) {
this.applicationPackage = applicationPackage;
@@ -310,6 +328,11 @@ public class DeployState implements ConfigDefinitionStore {
return this;
}
+ public Builder provisioned(Provisioned provisioned) {
+ this.provisioned = provisioned;
+ return this;
+ }
+
public Builder permanentApplicationPackage(Optional<ApplicationPackage> permanentApplicationPackage) {
this.permanentApplicationPackage = permanentApplicationPackage;
return this;
@@ -360,6 +383,20 @@ public class DeployState implements ConfigDefinitionStore {
return this;
}
+ public Builder wantedDockerImageRepo(Optional<String> dockerImageRepo) {
+ this.wantedDockerImageRepo = dockerImageRepo;
+ return this;
+ }
+
+ /**
+ * Whether access logging is enabled for an application without an accesslog element in services.xml.
+ * True by default.
+ */
+ public Builder accessLoggingEnabledByDefault(boolean accessLoggingEnabledByDefault) {
+ this.accessLoggingEnabledByDefault = accessLoggingEnabledByDefault;
+ return this;
+ }
+
public DeployState build() {
return build(new ValidationParameters());
}
@@ -375,6 +412,7 @@ public class DeployState implements ConfigDefinitionStore {
fileRegistry,
logger,
hostProvisioner,
+ provisioned,
properties,
version,
permanentApplicationPackage,
@@ -386,7 +424,9 @@ public class DeployState implements ConfigDefinitionStore {
queryProfiles,
semanticRules,
now,
- wantedNodeVespaVersion);
+ wantedNodeVespaVersion,
+ accessLoggingEnabledByDefault,
+ wantedDockerImageRepo);
}
private SearchDocumentModel createSearchDocumentModel(RankProfileRegistry rankProfileRegistry,
@@ -399,20 +439,18 @@ public class DeployState implements ConfigDefinitionStore {
for (NamedReader reader : readers) {
try {
String readerName = reader.getName();
- String searchName = builder.importReader(reader, readerName, logger);
+ String topLevelName = builder.importReader(reader, readerName, logger);
String sdName = stripSuffix(readerName, ApplicationPackage.SD_NAME_SUFFIX);
- names.put(searchName, sdName);
- if ( ! sdName.equals(searchName)) {
- throw new IllegalArgumentException("Search definition file name ('" + sdName + "') and name of " +
- "search element ('" + searchName +
+ names.put(topLevelName, sdName);
+ if ( ! sdName.equals(topLevelName)) {
+ throw new IllegalArgumentException("Schema definition file name ('" + sdName + "') and name of " +
+ "top level element ('" + topLevelName +
"') are not equal for file '" + readerName + "'");
}
} catch (ParseException e) {
- throw new IllegalArgumentException("Could not parse search definition file '" +
- getSearchDefinitionRelativePath(reader.getName()) + "': " + e.getMessage(), e);
+ throw new IllegalArgumentException("Could not parse sd file '" + reader.getName() + "'", e);
} catch (IOException e) {
- throw new IllegalArgumentException("Could not read search definition file '" +
- getSearchDefinitionRelativePath(reader.getName()) + "': " + e.getMessage(), e);
+ throw new IllegalArgumentException("Could not read sd file '" + reader.getName() + "'", e);
} finally {
closeIgnoreException(reader.getReader());
}
@@ -421,10 +459,6 @@ public class DeployState implements ConfigDefinitionStore {
return SearchDocumentModel.fromBuilderAndNames(builder, names);
}
- private String getSearchDefinitionRelativePath(String name) {
- return ApplicationPackage.SEARCH_DEFINITIONS_DIR + File.separator + name;
- }
-
private static String stripSuffix(String nodeName, String postfix) {
assert (nodeName.endsWith(postfix));
return nodeName.substring(0, nodeName.length() - postfix.length());
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/SearchDocumentModel.java b/config-model/src/main/java/com/yahoo/config/model/deploy/SearchDocumentModel.java
index cdd4f6f8e8a..9b9729dddb3 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/SearchDocumentModel.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/SearchDocumentModel.java
@@ -3,7 +3,7 @@ package com.yahoo.config.model.deploy;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.vespa.documentmodel.DocumentModel;
-import com.yahoo.vespa.model.search.SearchDefinition;
+import com.yahoo.vespa.model.search.NamedSchema;
import java.util.ArrayList;
import java.util.List;
@@ -13,16 +13,15 @@ import java.util.Map;
* Internal helper class to retrieve document model and search definitions.
*
* @author Ulf Lilleengen
- * @since 5.1
*/
public class SearchDocumentModel {
private final DocumentModel documentModel;
- private final List<SearchDefinition> searchDefinitions;
+ private final List<NamedSchema> schemas;
- public SearchDocumentModel(DocumentModel documentModel, List<SearchDefinition> searchDefinitions) {
+ public SearchDocumentModel(DocumentModel documentModel, List<NamedSchema> schemas) {
this.documentModel = documentModel;
- this.searchDefinitions = searchDefinitions;
+ this.schemas = schemas;
}
@@ -30,22 +29,22 @@ public class SearchDocumentModel {
return documentModel;
}
- public List<SearchDefinition> getSearchDefinitions() {
- return searchDefinitions;
+ public List<NamedSchema> getSchemas() {
+ return schemas;
}
public static SearchDocumentModel fromBuilderAndNames(SearchBuilder builder, Map<String, String> names) {
- List<SearchDefinition> ret = new ArrayList<>();
+ List<NamedSchema> ret = new ArrayList<>();
for (com.yahoo.searchdefinition.Search search : builder.getSearchList()) {
- ret.add(new SearchDefinition(names.get(search.getName()), search));
+ ret.add(new NamedSchema(names.get(search.getName()), search));
}
return new SearchDocumentModel(builder.getModel(), ret);
}
public static SearchDocumentModel fromBuilder(SearchBuilder builder) {
- List<SearchDefinition> ret = new ArrayList<>();
+ List<NamedSchema> ret = new ArrayList<>();
for (com.yahoo.searchdefinition.Search search : builder.getSearchList()) {
- ret.add(new SearchDefinition(search.getName(), search));
+ ret.add(new NamedSchema(search.getName(), search));
}
return new SearchDocumentModel(builder.getModel(), ret);
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
index 9f4d1b09f91..99225beba4f 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
@@ -4,10 +4,11 @@ package com.yahoo.config.model.deploy;
import com.google.common.collect.ImmutableList;
import com.yahoo.config.model.api.ConfigServerSpec;
import com.yahoo.config.model.api.ContainerEndpoint;
-import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.EndpointCertificateSecrets;
+import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.TlsSecrets;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.Zone;
@@ -39,9 +40,11 @@ public class TestProperties implements ModelContext.Properties {
private boolean isFirstTimeDeployment = false;
private boolean useDedicatedNodeForLogserver = false;
private boolean useAdaptiveDispatch = false;
+ private double topKProbability = 1.0;
private double defaultTermwiseLimit = 1.0;
+ private double softStartSeconds = 0.0;
private Optional<EndpointCertificateSecrets> endpointCertificateSecrets = Optional.empty();
-
+ private AthenzDomain athenzDomain;
@Override public boolean multitenant() { return multitenant; }
@Override public ApplicationId applicationId() { return applicationId; }
@@ -60,13 +63,30 @@ public class TestProperties implements ModelContext.Properties {
@Override public Optional<EndpointCertificateSecrets> endpointCertificateSecrets() { return endpointCertificateSecrets; }
@Override public Optional<TlsSecrets> tlsSecrets() { return endpointCertificateSecrets.map(TlsSecrets::new); }
@Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; }
+
+ @Override
+ public double defaultSoftStartSeconds() {
+ return softStartSeconds;
+ }
+
+ @Override public double defaultTopKProbability() { return topKProbability; }
@Override public boolean useBucketSpaceMetric() { return true; }
+ @Override public Optional<AthenzDomain> athenzDomain() { return Optional.ofNullable(athenzDomain); }
public TestProperties setDefaultTermwiseLimit(double limit) {
defaultTermwiseLimit = limit;
return this;
}
+ public TestProperties setTopKProbability(double probability) {
+ topKProbability = probability;
+ return this;
+ }
+ public TestProperties setSoftStartSeconds(double softStartSeconds) {
+ this.softStartSeconds = softStartSeconds;
+ return this;
+ }
+
public TestProperties setApplicationId(ApplicationId applicationId) {
this.applicationId = applicationId;
return this;
@@ -102,6 +122,16 @@ public class TestProperties implements ModelContext.Properties {
return this;
}
+ public TestProperties setZone(Zone zone) {
+ this.zone = zone;
+ return this;
+ }
+
+ public TestProperties setAthenzDomain(AthenzDomain domain) {
+ this.athenzDomain = domain;
+ return this;
+ }
+
public static class Spec implements ConfigServerSpec {
private final String hostName;
diff --git a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
index 48c21f370f4..cce5a7850a0 100644
--- a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
+++ b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
@@ -7,7 +7,6 @@ import com.yahoo.config.model.ApplicationConfigProducerRoot;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.subscription.ConfigInstanceUtil;
import com.yahoo.log.LogLevel;
-import com.yahoo.text.Utf8;
import com.yahoo.vespa.config.ConfigDefinitionKey;
import com.yahoo.vespa.config.ConfigPayload;
import com.yahoo.vespa.config.ConfigPayloadBuilder;
@@ -21,14 +20,8 @@ import com.yahoo.vespa.model.admin.Admin;
import com.yahoo.vespa.model.admin.monitoring.Monitoring;
import com.yahoo.vespa.model.utils.FreezableMap;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -291,60 +284,6 @@ public abstract class AbstractConfigProducer<CHILD extends AbstractConfigProduce
public AbstractConfigProducer getParent() { return parent; }
- /**
- * Writes files that need to be written. The files will usually only be
- * written when the Vespa model is generated through the deploy-application
- * script.
- *
- * TODO: Make sure all implemented ConfigProducers call createConfig()
- * instead of getConfig() when implementing this method.
- */
- public void writeFiles(File directory) throws java.io.IOException {
- if (!directory.isDirectory() && !directory.mkdirs()) {
- throw new java.io.IOException("Cannot create directory: "+ directory);
- }
- for (Method m : getClass().getMethods()) {
- try {
- ConfigInstance.Builder builder = getBuilderIfIsGetConfig(m);
- if (builder!=null) {
- writeBuilder(directory, m, builder);
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- private void writeBuilder(File directory, Method m,
- ConfigInstance.Builder builder) throws IllegalAccessException,
- InvocationTargetException, InstantiationException,
- NoSuchMethodException, IOException {
- m.invoke(this, builder);
- Class<?> configInstClass = builder.getClass().getEnclosingClass();
- ConfigInstance inst = (ConfigInstance) configInstClass.getConstructor(builder.getClass()).newInstance(builder);
- List<String> payloadList = ConfigInstance.serialize(inst);
- File outfn = new File(directory, ConfigInstance.getDefName(inst.getClass()) + ".MODEL.cfg");
- FileOutputStream out = new FileOutputStream(outfn);
- for (String s : payloadList) {
- out.write(Utf8.toBytes(s));
- out.write('\n');
- }
- }
-
- /**
- * New Builder instance if m is getConfig(SomeConfig.Builder), or null
- */
- private ConfigInstance.Builder getBuilderIfIsGetConfig(Method m) throws ReflectiveOperationException {
- if (!"getConfig".equals(m.getName())) return null;
- Type[] params = m.getParameterTypes();
- if (params.length!=1) return null;
- Type param = params[0];
- if (!(param instanceof Class)) return null;
- Class<?> paramClass = (Class<?>) param;
- if (!(ConfigInstance.Builder.class.isAssignableFrom(paramClass))) return null;
- return (ConfigInstance.Builder) paramClass.getDeclaredConstructor().newInstance();
- }
-
public void dump(PrintStream out) {
for (ConfigProducer c : getChildren().values()) {
out.println("id: " + c.getConfigId());
diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/HostsXmlProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/HostsXmlProvisioner.java
index f909f3864da..201b69c1aae 100644
--- a/config-model/src/main/java/com/yahoo/config/model/provision/HostsXmlProvisioner.java
+++ b/config-model/src/main/java/com/yahoo/config/model/provision/HostsXmlProvisioner.java
@@ -45,10 +45,16 @@ public class HostsXmlProvisioner implements HostProvisioner {
}
@Override
+ @Deprecated // TODO: Remove after April 2020
public List<HostSpec> prepare(ClusterSpec cluster, Capacity quantity, int groups, ProvisionLogger logger) {
throw new UnsupportedOperationException("Prepare on an XML host provisioner is not supported");
}
+ @Override
+ public List<HostSpec> prepare(ClusterSpec cluster, Capacity quantity, ProvisionLogger logger) {
+ throw new UnsupportedOperationException("Prepare on an XML host provisioner is not supported");
+ }
+
private HostSpec host2HostSpec(Host host) {
return new HostSpec(host.hostname(), host.aliases());
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
index bfbe2eaddb3..8706bb44ded 100644
--- a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
+++ b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
@@ -4,8 +4,10 @@ package com.yahoo.config.model.provision;
import com.yahoo.collections.ListMap;
import com.yahoo.collections.Pair;
import com.yahoo.config.model.api.HostProvisioner;
+import com.yahoo.config.model.api.Provisioned;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
+import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostSpec;
@@ -49,7 +51,6 @@ public class InMemoryProvisioner implements HostProvisioner {
/** Free hosts of each resource size */
private final ListMap<NodeResources, Host> freeNodes = new ListMap<>();
- private final Map<String, HostSpec> legacyMapping = new LinkedHashMap<>();
private final Map<ClusterSpec, List<HostSpec>> allocations = new LinkedHashMap<>();
/** Indexes must be unique across all groups in a cluster */
@@ -58,6 +59,10 @@ public class InMemoryProvisioner implements HostProvisioner {
/** Use this index as start index for all clusters */
private final int startIndexForClusters;
+ private final boolean useMaxResources;
+
+ private Provisioned provisioned = new Provisioned();
+
/** Creates this with a number of nodes with resources 1, 3, 9, 1 */
public InMemoryProvisioner(int nodeCount) {
this(nodeCount, defaultResources);
@@ -65,27 +70,31 @@ public class InMemoryProvisioner implements HostProvisioner {
/** Creates this with a number of nodes with given resources */
public InMemoryProvisioner(int nodeCount, NodeResources resources) {
- this(Map.of(resources, createHostInstances(nodeCount)), true, 0);
+ this(Map.of(resources, createHostInstances(nodeCount)), true, false, 0);
}
/** Creates this with a set of host names of the flavor 'default' */
public InMemoryProvisioner(boolean failOnOutOfCapacity, String... hosts) {
- this(Map.of(defaultResources, toHostInstances(hosts)), failOnOutOfCapacity, 0);
+ this(Map.of(defaultResources, toHostInstances(hosts)), failOnOutOfCapacity, false, 0);
}
/** Creates this with a set of hosts of the flavor 'default' */
public InMemoryProvisioner(Hosts hosts, boolean failOnOutOfCapacity, String ... retiredHostNames) {
- this(Map.of(defaultResources, hosts.asCollection()), failOnOutOfCapacity, 0, retiredHostNames);
+ this(Map.of(defaultResources, hosts.asCollection()), failOnOutOfCapacity, false, 0, retiredHostNames);
}
/** Creates this with a set of hosts of the flavor 'default' */
public InMemoryProvisioner(Hosts hosts, boolean failOnOutOfCapacity, int startIndexForClusters, String ... retiredHostNames) {
- this(Map.of(defaultResources, hosts.asCollection()), failOnOutOfCapacity, startIndexForClusters, retiredHostNames);
+ this(Map.of(defaultResources, hosts.asCollection()), failOnOutOfCapacity, false, startIndexForClusters, retiredHostNames);
}
- public InMemoryProvisioner(Map<NodeResources, Collection<Host>> hosts, boolean failOnOutOfCapacity,
- int startIndexForClusters, String ... retiredHostNames) {
+ public InMemoryProvisioner(Map<NodeResources, Collection<Host>> hosts,
+ boolean failOnOutOfCapacity,
+ boolean useMaxResources,
+ int startIndexForClusters,
+ String ... retiredHostNames) {
this.failOnOutOfCapacity = failOnOutOfCapacity;
+ this.useMaxResources = useMaxResources;
for (Map.Entry<NodeResources, Collection<Host>> hostsWithResources : hosts.entrySet())
for (Host host : hostsWithResources.getValue())
freeNodes.put(hostsWithResources.getKey(), host);
@@ -106,44 +115,52 @@ public class InMemoryProvisioner implements HostProvisioner {
@Override
public HostSpec allocateHost(String alias) {
- if (legacyMapping.containsKey(alias)) return legacyMapping.get(alias);
List<Host> defaultHosts = freeNodes.get(defaultResources);
if (defaultHosts.isEmpty()) throw new IllegalArgumentException("No more hosts with default resources available");
Host newHost = freeNodes.removeValue(defaultResources, 0);
- HostSpec hostSpec = new HostSpec(newHost.hostname(), newHost.aliases(), newHost.flavor(), Optional.empty(), newHost.version());
- legacyMapping.put(alias, hostSpec);
- return hostSpec;
+ // Note: Always returns HostSpec with empty dockerImageRepo, which is OK since this method is never used when docker image repo is set
+ return new HostSpec(newHost.hostname(), newHost.aliases(), newHost.flavor(), Optional.empty(), newHost.version(), Optional.empty());
}
@Override
+ @Deprecated // TODO: Remove after April 2020
public List<HostSpec> prepare(ClusterSpec cluster, Capacity requestedCapacity, int groups, ProvisionLogger logger) {
- if (cluster.group().isPresent() && groups > 1)
+ return prepare(cluster, requestedCapacity.withGroups(groups), logger);
+ }
+
+ @Override
+ public List<HostSpec> prepare(ClusterSpec cluster, Capacity requested, ProvisionLogger logger) {
+ provisioned.add(cluster.id(), requested);
+ if (useMaxResources)
+ return prepare(cluster, requested.maxResources(), requested.isRequired(), requested.canFail());
+ else
+ return prepare(cluster, requested.minResources(), requested.isRequired(), requested.canFail());
+ }
+
+ public List<HostSpec> prepare(ClusterSpec cluster, ClusterResources requested, boolean required, boolean canFail) {
+ if (cluster.group().isPresent() && requested.groups() > 1)
throw new IllegalArgumentException("Cannot both be specifying a group and ask for groups to be created");
- if (requestedCapacity.nodeCount() % groups != 0)
- throw new IllegalArgumentException("Requested " + requestedCapacity.nodeCount() + " nodes in " +
- groups + " groups, but the node count is not divisible into this number of groups");
- int capacity = failOnOutOfCapacity || requestedCapacity.isRequired()
- ? requestedCapacity.nodeCount()
- : Math.min(requestedCapacity.nodeCount(), freeNodes.get(defaultResources).size() + totalAllocatedTo(cluster));
- if (groups > capacity)
- groups = capacity;
+ int capacity = failOnOutOfCapacity || required
+ ? requested.nodes()
+ : Math.min(requested.nodes(), freeNodes.get(defaultResources).size() + totalAllocatedTo(cluster));
+ int groups = requested.groups() > capacity ? capacity : requested.groups();
List<HostSpec> allocation = new ArrayList<>();
if (groups == 1) {
allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(0))),
- requestedCapacity.nodeResources(),
+ requested.nodeResources(),
capacity,
startIndexForClusters,
- requestedCapacity.canFail()));
+ canFail));
}
else {
for (int i = 0; i < groups; i++) {
allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(i))),
- requestedCapacity.nodeResources(),
+ requested.nodeResources(),
capacity / groups,
allocation.size(),
- requestedCapacity.canFail()));
+ canFail));
}
}
for (ListIterator<HostSpec> i = allocation.listIterator(); i.hasNext(); ) {
@@ -154,15 +171,24 @@ public class InMemoryProvisioner implements HostProvisioner {
return allocation;
}
+ /** Create a new provisioned instance to record provision requests to this and returns it */
+ public Provisioned startProvisionedRecording() {
+ provisioned = new Provisioned();
+ return provisioned;
+ }
+
private HostSpec retire(HostSpec host) {
return new HostSpec(host.hostname(),
host.aliases(),
host.flavor(),
Optional.of(host.membership().get().retire()),
- host.version());
+ host.version(),
+ Optional.empty(),
+ Optional.empty(),
+ host.dockerImageRepo());
}
- private List<HostSpec> allocateHostGroup(ClusterSpec clusterGroup, Optional<NodeResources> requestedResources,
+ private List<HostSpec> allocateHostGroup(ClusterSpec clusterGroup, NodeResources requestedResources,
int nodesInGroup, int startIndex, boolean canFail) {
List<HostSpec> allocation = allocations.getOrDefault(clusterGroup, new ArrayList<>());
allocations.put(clusterGroup, allocation);
@@ -170,8 +196,8 @@ public class InMemoryProvisioner implements HostProvisioner {
// Check if the current allocations are compatible with the new request
for (int i = allocation.size() - 1; i >= 0; i--) {
Optional<NodeResources> currentResources = allocation.get(0).flavor().map(Flavor::resources);
- if (currentResources.isEmpty() || requestedResources.isEmpty()) continue;
- if (!currentResources.get().compatibleWith(requestedResources.get())) {
+ if (currentResources.isEmpty() || requestedResources == NodeResources.unspecified) continue;
+ if (!currentResources.get().compatibleWith(requestedResources)) {
HostSpec removed = allocation.remove(i);
freeNodes.put(currentResources.get(), new Host(removed.hostname())); // Return the node back to free pool
}
@@ -182,7 +208,7 @@ public class InMemoryProvisioner implements HostProvisioner {
// Find the smallest host that can fit the requested requested
Optional<NodeResources> hostResources = freeNodes.keySet().stream()
.sorted(new MemoryDiskCpu())
- .filter(resources -> requestedResources.isEmpty() || resources.satisfies(requestedResources.get()))
+ .filter(resources -> requestedResources == NodeResources.unspecified || resources.satisfies(requestedResources))
.findFirst();
if (hostResources.isEmpty()) {
if (canFail)
@@ -195,8 +221,9 @@ public class InMemoryProvisioner implements HostProvisioner {
if (freeNodes.get(hostResources.get()).isEmpty()) freeNodes.removeAll(hostResources.get());
ClusterMembership membership = ClusterMembership.from(clusterGroup, nextIndex++);
allocation.add(new HostSpec(newHost.hostname(), newHost.aliases(),
- hostResources.map(Flavor::new), Optional.of(membership),
- newHost.version(), Optional.empty(), requestedResources));
+ hostResources.map(Flavor::new), Optional.of(membership),
+ newHost.version(), Optional.empty(),
+ requestedResources == NodeResources.unspecified ? Optional.empty() : Optional.of(requestedResources)));
}
nextIndexInCluster.put(new Pair<>(clusterGroup.type(), clusterGroup.id()), nextIndex);
diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java
index 180a16f3c8f..8945223447f 100644
--- a/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java
+++ b/config-model/src/main/java/com/yahoo/config/model/provision/SingleNodeProvisioner.java
@@ -30,6 +30,7 @@ public class SingleNodeProvisioner implements HostProvisioner {
host = new Host(HostName.getLocalhost());
this.hostSpec = new HostSpec(host.hostname(), host.aliases());
}
+
public SingleNodeProvisioner(Flavor flavor) {
host = new Host(HostName.getLocalhost());
this.hostSpec = new HostSpec(host.hostname(), host.aliases(), flavor);
@@ -41,7 +42,14 @@ public class SingleNodeProvisioner implements HostProvisioner {
}
@Override
- public List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, int groups, ProvisionLogger logger) { // TODO: This should fail if capacity requested is more than 1
+ @Deprecated // TODO: Remove after April 2020
+ public List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, int groups, ProvisionLogger logger) {
+ return prepare(cluster, capacity.withGroups(groups), logger);
+ }
+
+ @Override
+ public List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, ProvisionLogger logger) {
+ // TODO: This should fail if capacity requested is more than 1
List<HostSpec> hosts = new ArrayList<>();
hosts.add(new HostSpec(host.hostname(), host.aliases(), ClusterMembership.from(cluster, counter++)));
return hosts;
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 eb61bda83a6..10649df88e1 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
@@ -55,23 +55,23 @@ public class MockApplicationPackage implements ApplicationPackage {
private final File root;
private final String hostsS;
private final String servicesS;
- private final List<String> searchDefinitions;
- private final String searchDefinitionDir;
+ private final List<String> schemas;
+ private final String schemaDir;
private final Optional<String> deploymentSpec;
private final Optional<String> validationOverrides;
private final boolean failOnValidateXml;
private final QueryProfileRegistry queryProfileRegistry;
private final ApplicationMetaData applicationMetaData;
- protected MockApplicationPackage(File root, String hosts, String services, List<String> searchDefinitions,
- String searchDefinitionDir,
+ protected MockApplicationPackage(File root, String hosts, String services, List<String> schemas,
+ String schemaDir,
String deploymentSpec, String validationOverrides, boolean failOnValidateXml,
String queryProfile, String queryProfileType) {
this.root = root;
this.hostsS = hosts;
this.servicesS = services;
- this.searchDefinitions = searchDefinitions;
- this.searchDefinitionDir = searchDefinitionDir;
+ this.schemas = schemas;
+ this.schemaDir = schemaDir;
this.deploymentSpec = Optional.ofNullable(deploymentSpec);
this.validationOverrides = Optional.ofNullable(validationOverrides);
this.failOnValidateXml = failOnValidateXml;
@@ -108,7 +108,7 @@ public class MockApplicationPackage implements ApplicationPackage {
@Override
public Reader getHosts() {
- if (hostsS==null) return null;
+ if (hostsS == null) return null;
return new StringReader(hostsS);
}
@@ -118,7 +118,7 @@ public class MockApplicationPackage implements ApplicationPackage {
SearchBuilder searchBuilder = new SearchBuilder(this,
new RankProfileRegistry(),
queryProfileRegistry);
- for (String sd : searchDefinitions) {
+ for (String sd : schemas) {
try {
String name = searchBuilder.importString(sd);
readers.add(new NamedReader(name + ApplicationPackage.SD_NAME_SUFFIX, new StringReader(sd)));
@@ -184,7 +184,7 @@ public class MockApplicationPackage implements ApplicationPackage {
@Override
public Reader getRankingExpression(String name) {
- File expressionFile = new File(searchDefinitionDir, name);
+ File expressionFile = new File(schemaDir, name);
try {
return IOUtils.createReader(expressionFile, "utf-8");
}
@@ -200,9 +200,9 @@ public class MockApplicationPackage implements ApplicationPackage {
public static ApplicationPackage fromSearchDefinitionDirectory(String dir) {
return new MockApplicationPackage.Builder()
- .withEmptyHosts()
- .withEmptyServices()
- .withSearchDefinitionDir(dir).build();
+ .withEmptyHosts()
+ .withEmptyServices()
+ .withSchemaDir(dir).build();
}
public static class Builder {
@@ -210,8 +210,8 @@ public class MockApplicationPackage implements ApplicationPackage {
private File root = new File("nonexisting");
private String hosts = null;
private String services = null;
- private List<String> searchDefinitions = Collections.emptyList();
- private String searchDefinitionDir = null;
+ private List<String> schemas = Collections.emptyList();
+ private String schemaDir = null;
private String deploymentSpec = null;
private String validationOverrides = null;
private boolean failOnValidateXml = false;
@@ -245,17 +245,17 @@ public class MockApplicationPackage implements ApplicationPackage {
}
public Builder withSearchDefinition(String searchDefinition) {
- this.searchDefinitions = Collections.singletonList(searchDefinition);
+ this.schemas = Collections.singletonList(searchDefinition);
return this;
}
- public Builder withSearchDefinitions(List<String> searchDefinition) {
- this.searchDefinitions = Collections.unmodifiableList(searchDefinition);
+ public Builder withSchemas(List<String> searchDefinition) {
+ this.schemas = Collections.unmodifiableList(searchDefinition);
return this;
}
- public Builder withSearchDefinitionDir(String searchDefinitionDir) {
- this.searchDefinitionDir = searchDefinitionDir;
+ public Builder withSchemaDir(String schemaDir) {
+ this.schemaDir = schemaDir;
return this;
}
@@ -285,7 +285,7 @@ public class MockApplicationPackage implements ApplicationPackage {
}
public ApplicationPackage build() {
- return new MockApplicationPackage(root, hosts, services, searchDefinitions, searchDefinitionDir,
+ return new MockApplicationPackage(root, hosts, services, schemas, schemaDir,
deploymentSpec, validationOverrides, failOnValidateXml,
queryProfile, queryProfileType);
}
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 13f271ebe9d..87d6554f691 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
@@ -26,6 +26,7 @@ import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.Set;
@@ -66,7 +67,7 @@ public class MockRoot extends AbstractConfigProducerRoot {
super(rootConfigId);
hostSystem = new HostSystem(this, "hostsystem", deployState.getProvisioner(), deployState.getDeployLogger());
this.deployState = deployState;
- fileDistributor = new FileDistributor(deployState.getFileRegistry(), null);
+ fileDistributor = new FileDistributor(deployState.getFileRegistry(), List.of(), deployState.isHosted());
}
public FileDistributionConfigProducer getFileDistributionConfigProducer() {
diff --git a/config-model/src/main/java/com/yahoo/documentmodel/NewDocumentType.java b/config-model/src/main/java/com/yahoo/documentmodel/NewDocumentType.java
index fc42864f1d0..41a30c4553d 100644
--- a/config-model/src/main/java/com/yahoo/documentmodel/NewDocumentType.java
+++ b/config-model/src/main/java/com/yahoo/documentmodel/NewDocumentType.java
@@ -69,25 +69,37 @@ public final class NewDocumentType extends StructuredDataType implements DataTyp
private final StructDataType body;
private final Set<FieldSet> fieldSets = new LinkedHashSet<>();
private final Set<Name> documentReferences;
+ // Imported fields are virtual and therefore exist outside of the SD's document field definition
+ // block itself. But for features like imported fields in a non-search context (e.g. GC selections)
+ // it is necessary to know that certain identifiers refer to imported fields instead of being unknown
+ // document fields. To achieve this, we track the names of imported fields as part of the document
+ // config itself.
+ private final Set<String> importedFieldNames;
public NewDocumentType(Name name) {
this(name, emptySet());
}
- public NewDocumentType(Name name, Set<Name> documentReferences) {
+ public NewDocumentType(Name name, Set<Name> documentReferences, Set<String> importedFieldNames) {
this(
name,
new StructDataType(name.getName() + ".header"),
new StructDataType(name.getName() + ".body"),
new FieldSets(),
- documentReferences);
+ documentReferences,
+ importedFieldNames);
+ }
+
+ public NewDocumentType(Name name, Set<Name> documentReferences) {
+ this(name, documentReferences, emptySet());
}
public NewDocumentType(Name name,
StructDataType header,
StructDataType body,
FieldSets fs,
- Set<Name> documentReferences) {
+ Set<Name> documentReferences,
+ Set<String> importedFieldNames) {
super(name.getName());
this.name = name;
this.header = header;
@@ -102,6 +114,7 @@ public final class NewDocumentType extends StructuredDataType implements DataTyp
}
}
this.documentReferences = documentReferences;
+ this.importedFieldNames = importedFieldNames;
}
public Name getFullName() {
@@ -389,4 +402,8 @@ public final class NewDocumentType extends StructuredDataType implements DataTyp
return documentReferences;
}
+ public Set<String> getImportedFieldNames() {
+ return importedFieldNames;
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentModelBuilder.java b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentModelBuilder.java
index f0b1b427531..d3a78321106 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentModelBuilder.java
@@ -18,6 +18,7 @@ import com.yahoo.documentmodel.VespaDocumentType;
import com.yahoo.searchdefinition.document.Attribute;
import com.yahoo.searchdefinition.document.SDDocumentType;
import com.yahoo.searchdefinition.document.SDField;
+import com.yahoo.searchdefinition.document.TemporaryImportedFields;
import com.yahoo.searchdefinition.document.annotation.SDAnnotationType;
import com.yahoo.searchdefinition.document.annotation.TemporaryAnnotationReferenceDataType;
import com.yahoo.vespa.documentmodel.DocumentModel;
@@ -27,6 +28,7 @@ import com.yahoo.vespa.documentmodel.SearchField;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -338,7 +340,8 @@ public class DocumentModelBuilder {
sdoc.getDocumentType().contentStruct(),
sdoc.getDocumentType().getBodyType(),
sdoc.getFieldSets(),
- convertDocumentReferencesToNames(sdoc.getDocumentReferences()));
+ convertDocumentReferencesToNames(sdoc.getDocumentReferences()),
+ convertTemporaryImportedFieldsToNames(sdoc.getTemporaryImportedFields()));
for (SDDocumentType n : sdoc.getInheritedTypes()) {
NewDocumentType.Name name = new NewDocumentType.Name(n.getName());
NewDocumentType inherited = model.getDocumentManager().getDocumentType(name);
@@ -404,6 +407,13 @@ public class DocumentModelBuilder {
.collect(toSet());
}
+ private static Set<String> convertTemporaryImportedFieldsToNames(TemporaryImportedFields importedFields) {
+ if (importedFields == null) {
+ return emptySet();
+ }
+ return Collections.unmodifiableSet(importedFields.fields().keySet());
+ }
+
private static void extractDataTypesFromFields(NewDocumentType dt, Collection<Field> fields) {
for (Field f : fields) {
DataType type = f.getDataType();
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java
index 14f8a0a9d37..0d0da71bd0f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java
@@ -25,8 +25,8 @@ public class DocumentReferenceResolver {
private final Map<String, Search> searchMapping;
- public DocumentReferenceResolver(List<Search> searchDefinitions) {
- this.searchMapping = createDocumentNameToSearchMapping(searchDefinitions);
+ public DocumentReferenceResolver(List<Search> schemas) {
+ this.searchMapping = createDocumentNameToSearchMapping(schemas);
}
public void resolveReferences(SDDocumentType documentType) {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/FieldOperationApplierForStructs.java b/config-model/src/main/java/com/yahoo/searchdefinition/FieldOperationApplierForStructs.java
index 9ff749a994c..ade8ae21870 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/FieldOperationApplierForStructs.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/FieldOperationApplierForStructs.java
@@ -41,7 +41,7 @@ public class FieldOperationApplierForStructs extends FieldOperationApplier {
}
if (structUsedByField.getName().equals(structType.getName())) {
//this field is using this type!!
- field.populateWithStructFields(sdoc, field.getName(), field.getDataType(), field.isHeader(), 0);
+ field.populateWithStructFields(sdoc, field.getName(), field.getDataType(), 0);
field.populateWithStructMatching(sdoc, field.getName(), field.getDataType(), field.getMatching());
}
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/ImportedFieldsEnumerator.java b/config-model/src/main/java/com/yahoo/searchdefinition/ImportedFieldsEnumerator.java
new file mode 100644
index 00000000000..25cdd1e08cd
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/ImportedFieldsEnumerator.java
@@ -0,0 +1,31 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.searchdefinition;
+
+import com.yahoo.searchdefinition.document.SDDocumentType;
+
+import java.util.List;
+
+/**
+ * Enumerates and emplaces a set of all imported fields into a SDDocumentType from
+ * its corresponding Search instance.
+ */
+public class ImportedFieldsEnumerator {
+
+ private final List<Search> schemas;
+
+ public ImportedFieldsEnumerator(List<Search> schemas) {
+ this.schemas = schemas;
+ }
+
+ public void enumerateImportedFields(SDDocumentType documentType) {
+ var search = this.schemas.stream()
+ .filter(s -> s.getDocument() != null)
+ .filter(s -> s.getDocument().getName().equals(documentType.getName()))
+ .findFirst();
+ if (search.isEmpty()) {
+ return; // No imported fields present.
+ }
+ search.get().temporaryImportedFields().ifPresent(documentType::setTemporaryImportedFields);
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/Index.java b/config-model/src/main/java/com/yahoo/searchdefinition/Index.java
index e46db1d1b5f..aba6cf9a233 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/Index.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/Index.java
@@ -2,6 +2,7 @@
package com.yahoo.searchdefinition;
import com.yahoo.searchdefinition.document.BooleanIndexDefinition;
+import com.yahoo.searchdefinition.document.HnswIndexParams;
import com.yahoo.searchdefinition.document.RankType;
import com.yahoo.searchdefinition.document.Stemming;
@@ -9,6 +10,9 @@ import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
/**
@@ -20,6 +24,8 @@ import java.util.Set;
*/
public class Index implements Cloneable, Serializable {
+ public static enum DistanceMetric { EUCLIDEAN, ANGULAR, GEODEGREES }
+
public enum Type {
VESPA("vespa");
@@ -57,6 +63,10 @@ public class Index implements Cloneable, Serializable {
/** The boolean index definition, if set */
private BooleanIndexDefinition boolIndex;
+ private Optional<HnswIndexParams> hnswIndexParams = Optional.empty();
+
+ private Optional<DistanceMetric> distanceMetric = Optional.empty();
+
/** Whether the posting lists of this index field should have interleaved features (num occs, field length) in document id stream. */
private boolean interleavedFeatures = false;
@@ -115,20 +125,26 @@ public class Index implements Cloneable, Serializable {
}
@Override
- public int hashCode() {
- return name.hashCode() + ( prefix ? 17 : 0 );
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Index index = (Index) o;
+ return prefix == index.prefix &&
+ normalized == index.normalized &&
+ interleavedFeatures == index.interleavedFeatures &&
+ Objects.equals(name, index.name) &&
+ rankType == index.rankType &&
+ Objects.equals(aliases, index.aliases) &&
+ stemming == index.stemming &&
+ type == index.type &&
+ Objects.equals(boolIndex, index.boolIndex) &&
+ Objects.equals(distanceMetric, index.distanceMetric) &&
+ Objects.equals(hnswIndexParams, index.hnswIndexParams);
}
@Override
- public boolean equals(Object object) {
- if ( ! (object instanceof Index)) return false;
-
- Index other=(Index)object;
- return
- this.name.equals(other.name) &&
- this.prefix==other.prefix &&
- this.stemming==other.stemming &&
- this.normalized==other.normalized;
+ public int hashCode() {
+ return Objects.hash(name, rankType, prefix, aliases, stemming, normalized, type, boolIndex, distanceMetric, hnswIndexParams, interleavedFeatures);
}
public String toString() {
@@ -176,6 +192,24 @@ public class Index implements Cloneable, Serializable {
boolIndex = def;
}
+ public Optional<DistanceMetric> getDistanceMetric() {
+ return distanceMetric;
+ }
+
+ public void setDistanceMetric(String value) {
+ String upper = value.toUpperCase(Locale.ENGLISH);
+ DistanceMetric dm = DistanceMetric.valueOf(upper);
+ distanceMetric = Optional.of(dm);
+ }
+
+ public Optional<HnswIndexParams> getHnswIndexParams() {
+ return hnswIndexParams;
+ }
+
+ public void setHnswIndexParams(HnswIndexParams params) {
+ hnswIndexParams = Optional.of(params);
+ }
+
public void setInterleavedFeatures(boolean value) {
interleavedFeatures = value;
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
index fa196cd3bbf..23eb814de81 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
@@ -788,8 +788,7 @@ public class RankProfile implements Cloneable {
type = existingType.dimensionwiseGeneralizationWith(type).orElseThrow( () ->
new IllegalArgumentException(queryProfileType + " contains query feature " + feature.get() +
" with type " + field.getType().asTensorType() +
- ", but this is already defined " +
- "in another query profile with type " +
+ ", but this is already defined in another query profile with type " +
context.getType(feature.get())));
context.setType(feature.get(), type);
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/Search.java b/config-model/src/main/java/com/yahoo/searchdefinition/Search.java
index f90a7e4f6cd..0ab8a2308a4 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/Search.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/Search.java
@@ -288,7 +288,7 @@ public class Search implements ImmutableSearch {
/**
* Adds an extra field of this search definition not contained in a document
*
- * @param field to add to the searchdefinitions list of external fields.
+ * @param field to add to the schemas list of external fields
*/
public void addExtraField(SDField field) {
if (fields.containsKey(field.getName())) {
@@ -383,7 +383,7 @@ public class Search implements ImmutableSearch {
* Consolidates a set of index settings for the same index into one
*
* @param indices The list of indexes to consolidate.
- * @return The consolidated index
+ * @return the consolidated index
*/
private Index consolidateIndices(List<Index> indices) {
Index first = indices.get(0);
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
index 949539ff99f..eb68e6af203 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
@@ -230,20 +230,22 @@ public class SearchBuilder {
sdocs.add(search.getDocument());
}
}
- SDDocumentTypeOrderer orderer = new SDDocumentTypeOrderer(sdocs, deployLogger);
+ var orderer = new SDDocumentTypeOrderer(sdocs, deployLogger);
orderer.process();
for (SDDocumentType sdoc : orderer.getOrdered()) {
new FieldOperationApplierForStructs().process(sdoc);
new FieldOperationApplier().process(sdoc);
}
- DocumentReferenceResolver resolver = new DocumentReferenceResolver(searchList);
+ var resolver = new DocumentReferenceResolver(searchList);
sdocs.forEach(resolver::resolveReferences);
+ var importedFieldsEnumerator = new ImportedFieldsEnumerator(searchList);
+ sdocs.forEach(importedFieldsEnumerator::enumerateImportedFields);
if (validate)
new DocumentGraphValidator().validateDocumentGraph(sdocs);
- DocumentModelBuilder builder = new DocumentModelBuilder(model);
+ var builder = new DocumentModelBuilder(model);
for (Search search : new SearchOrderer().order(searchList)) {
new FieldOperationApplierForSearch().process(search); // TODO: Why is this not in the regular list?
process(search, deployLogger, new QueryProfiles(queryProfileRegistry, deployLogger), validate);
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java
index 76dff404568..8b5f7658475 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java
@@ -240,6 +240,17 @@ public class AttributeFields extends Derived implements AttributesConfig.Produce
aaB.tensortype(attribute.tensorType().get().toString());
}
aaB.imported(imported);
+ var dma = attribute.distanceMetric();
+ if (attribute.hnswIndexParams().isPresent()) {
+ var ib = new AttributesConfig.Attribute.Index.Builder();
+ var params = attribute.hnswIndexParams().get();
+ ib.hnsw.enabled(true);
+ ib.hnsw.maxlinkspernode(params.maxLinksPerNode());
+ ib.hnsw.neighborstoexploreatinsert(params.neighborsToExploreAtInsert());
+ var dm = AttributesConfig.Attribute.Index.Hnsw.Distancemetric.Enum.valueOf(dma.toString());
+ ib.hnsw.distancemetric(dm);
+ aaB.index(ib);
+ }
return aaB;
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
index 245913d7822..fc8710fa1a1 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
@@ -15,9 +15,11 @@ import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.derived.validation.Validation;
+import com.yahoo.vespa.model.container.search.QueryProfiles;
import java.io.IOException;
import java.io.Writer;
+import java.util.logging.Level;
/**
* A set of all derived configuration of a search definition. Use this as a facade to individual configurations when
@@ -39,12 +41,13 @@ public class DerivedConfiguration {
private VsmSummary streamingSummary;
private IndexSchema indexSchema;
private ImportedFields importedFields;
+ private QueryProfileRegistry queryProfiles;
/**
* Creates a complete derived configuration from a search definition.
* Only used in tests.
*
- * @param search The search to derive a configuration from. Derived objects will be snapshots, but this argument is
+ * @param search the search to derive a configuration from. Derived objects will be snapshots, but this argument is
* live. Which means that this object will be inconsistent when the given search definition is later
* modified.
* @param rankProfileRegistry a {@link com.yahoo.searchdefinition.RankProfileRegistry}
@@ -56,11 +59,11 @@ public class DerivedConfiguration {
/**
* Creates a complete derived configuration snapshot from a search definition.
*
- * @param search The search to derive a configuration from. Derived objects will be snapshots, but this
+ * @param search the search to derive a configuration from. Derived objects will be snapshots, but this
* argument is live. Which means that this object will be inconsistent when the given
* search definition is later modified.
* @param deployLogger a {@link DeployLogger} for logging when doing operations on this
- * @param deployProperties Properties set on deploy.
+ * @param deployProperties properties set on deploy
* @param rankProfileRegistry a {@link com.yahoo.searchdefinition.RankProfileRegistry}
* @param queryProfiles the query profiles of this application
*/
@@ -72,6 +75,7 @@ public class DerivedConfiguration {
ImportedMlModels importedModels) {
Validator.ensureNotNull("Search definition", search);
this.search = search;
+ this.queryProfiles = queryProfiles;
if ( ! search.isDocumentsOnly()) {
streamingFields = new VsmFields(search);
streamingSummary = new VsmSummary(search);
@@ -120,6 +124,10 @@ public class DerivedConfiguration {
exportCfg(new DocumenttypesConfig(documentTypesCfg), toDirectory + "/" + "documenttypes.cfg");
}
+ public static void exportQueryProfiles(QueryProfileRegistry queryProfileRegistry, String toDirectory) throws IOException {
+ exportCfg(new QueryProfiles(queryProfileRegistry, (level, message) -> {}).getConfig(), toDirectory + "/" + "query-profiles.cfg");
+ }
+
private static void exportCfg(ConfigInstance instance, String fileName) throws IOException {
Writer writer = null;
try {
@@ -186,4 +194,7 @@ public class DerivedConfiguration {
public ImportedFields getImportedFields() {
return importedFields;
}
+
+ public QueryProfileRegistry getQueryProfiles() { return queryProfiles; }
+
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
index 02b2a383318..da25680ca47 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
@@ -1,16 +1,25 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.derived;
-import com.yahoo.document.*;
+import com.yahoo.document.CollectionDataType;
+import com.yahoo.document.DataType;
+import com.yahoo.document.NumericDataType;
+import com.yahoo.document.PositionDataType;
import com.yahoo.searchdefinition.Index;
import com.yahoo.searchdefinition.Search;
-import com.yahoo.searchdefinition.document.*;
+import com.yahoo.searchdefinition.document.Attribute;
+import com.yahoo.searchdefinition.document.BooleanIndexDefinition;
+import com.yahoo.searchdefinition.document.FieldSet;
+import com.yahoo.searchdefinition.document.ImmutableSDField;
+import com.yahoo.searchdefinition.document.Matching;
+import com.yahoo.searchdefinition.document.Stemming;
import com.yahoo.searchdefinition.processing.ExactMatch;
import com.yahoo.searchdefinition.processing.NGramMatch;
import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.search.config.IndexInfoConfig;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -34,8 +43,10 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
private static final String CMD_PLAIN_TOKENS = "plain-tokens";
private static final String CMD_MULTIVALUE = "multivalue";
private static final String CMD_FAST_SEARCH = "fast-search";
+ private static final String CMD_PREDICATE = "predicate";
private static final String CMD_PREDICATE_BOUNDS = "predicate-bounds";
private static final String CMD_NUMERICAL = "numerical";
+ private static final String CMD_PHRASE_SEGMENTING = "phrase-segmenting";
private Set<IndexCommand> commands = new java.util.LinkedHashSet<>();
private Map<String, String> aliases = new java.util.LinkedHashMap<>();
private Map<String, FieldSet> fieldSets;
@@ -90,6 +101,7 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
protected void derive(ImmutableSDField field, Search search, boolean inPosition) {
if (field.getDataType().equals(DataType.PREDICATE)) {
+ addIndexCommand(field, CMD_PREDICATE);
Index index = field.getIndex(field.getName());
if (index != null) {
BooleanIndexDefinition options = index.getBooleanIndexDefiniton();
@@ -286,21 +298,14 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
// TODO: Move this to the FieldSetSettings processor (and rename it) as that already has to look at this.
private void addFieldSetCommands(IndexInfoConfig.Indexinfo.Builder iiB, FieldSet fieldSet) {
- // Explicit query commands on the field set, overrides everything.
- if (!fieldSet.queryCommands().isEmpty()) {
- for (String qc : fieldSet.queryCommands()) {
- iiB.command(
- new IndexInfoConfig.Indexinfo.Command.Builder()
- .indexname(fieldSet.getName())
- .command(qc));
- }
- return;
- }
+ for (String qc : fieldSet.queryCommands())
+ iiB.command(new IndexInfoConfig.Indexinfo.Command.Builder().indexname(fieldSet.getName()).command(qc));
boolean anyIndexing = false;
boolean anyAttributing = false;
boolean anyLowerCasing = false;
boolean anyStemming = false;
boolean anyNormalizing = false;
+ String phraseSegmentingCommand = null;
String stemmingCommand = null;
Matching fieldSetMatching = fieldSet.getMatching(); // null if no explicit matching
// First a pass over the fields to read some params to decide field settings implicitly:
@@ -321,8 +326,13 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
if (field.getNormalizing().doRemoveAccents()) {
anyNormalizing = true;
}
- if (fieldSetMatching == null && field.getMatching().getType() != Matching.defaultType)
+ if (fieldSetMatching == null && field.getMatching().getType() != Matching.defaultType) {
fieldSetMatching = field.getMatching();
+ }
+ Optional<String> explicitPhraseSegmentingCommand = field.getQueryCommands().stream().filter(c -> c.startsWith(CMD_PHRASE_SEGMENTING)).findFirst();
+ if (explicitPhraseSegmentingCommand.isPresent()) {
+ phraseSegmentingCommand = explicitPhraseSegmentingCommand.get();
+ }
}
if (anyIndexing && anyAttributing && fieldSet.getMatching() == null) {
// We have both attributes and indexes and no explicit match setting ->
@@ -365,6 +375,11 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
new IndexInfoConfig.Indexinfo.Command.Builder()
.indexname(fieldSet.getName())
.command(CMD_NORMALIZE));
+ if (phraseSegmentingCommand != null)
+ iiB.command(
+ new IndexInfoConfig.Indexinfo.Command.Builder()
+ .indexname(fieldSet.getName())
+ .command(phraseSegmentingCommand));
}
} else {
// Assume only attribute fields
@@ -400,9 +415,7 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
} else if (fieldSetMatching.getType().equals(Matching.Type.TEXT)) {
}
-
}
-
}
private boolean hasMultiValueField(FieldSet fieldSet) {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexSchema.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexSchema.java
index 60b8ee78c7b..39a67a69575 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexSchema.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexSchema.java
@@ -5,6 +5,7 @@ import com.yahoo.document.ArrayDataType;
import com.yahoo.document.DataType;
import com.yahoo.document.Field;
import com.yahoo.document.StructuredDataType;
+import com.yahoo.document.TensorDataType;
import com.yahoo.document.WeightedSetDataType;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.document.BooleanIndexDefinition;
@@ -20,7 +21,7 @@ import java.util.List;
import java.util.Map;
/**
- * Deriver of indexschema config containing information of all index fields with name and data type.
+ * Deriver of indexschema config containing information of all text index fields with name and data type.
*
* @author geirst
*/
@@ -44,9 +45,14 @@ public class IndexSchema extends Derived implements IndexschemaConfig.Producer {
super.derive(search);
}
+ private boolean isTensorField(ImmutableSDField field) {
+ return field.getDataType() instanceof TensorDataType;
+ }
+
private void deriveIndexFields(ImmutableSDField field, Search search) {
- if (!field.doesIndexing() &&
- !field.isIndexStructureField())
+ // Note: Indexes for tensor fields are NOT part of the index schema for text fields.
+ if ((!field.doesIndexing() && !field.isIndexStructureField()) ||
+ isTensorField(field))
{
return;
}
@@ -138,11 +144,10 @@ public class IndexSchema extends Derived implements IndexschemaConfig.Producer {
return Collections.singletonList(field);
}
if (fieldType instanceof ArrayDataType) {
- boolean header = field.isHeader();
List<Field> ret = new LinkedList<>();
- Field innerField = new Field(field.getName(), ((ArrayDataType)fieldType).getNestedType(), header);
+ Field innerField = new Field(field.getName(), ((ArrayDataType)fieldType).getNestedType());
for (Field flatField : flattenField(innerField)) {
- ret.add(new Field(flatField.getName(), DataType.getArray(flatField.getDataType()), header));
+ ret.add(new Field(flatField.getName(), DataType.getArray(flatField.getDataType())));
}
return ret;
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java
index fbcaf2a3a80..1661a80f238 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java
@@ -24,6 +24,7 @@ import com.yahoo.document.datatypes.Float16FieldValue;
import com.yahoo.document.datatypes.StringFieldValue;
import com.yahoo.document.datatypes.TensorFieldValue;
import com.yahoo.tensor.TensorType;
+import static com.yahoo.searchdefinition.Index.DistanceMetric;
import java.io.Serializable;
import java.util.LinkedHashSet;
@@ -66,6 +67,10 @@ public final class Attribute implements Cloneable, Serializable {
/** This is set if the type of this is REFERENCE */
private final Optional<StructuredDataType> referenceDocumentType;
+ private Optional<DistanceMetric> distanceMetric = Optional.empty();
+
+ private Optional<HnswIndexParams> hnswIndexParams = Optional.empty();
+
private boolean isPosition = false;
private final Sorting sorting = new Sorting();
@@ -195,6 +200,12 @@ public final class Attribute implements Cloneable, Serializable {
public Optional<TensorType> tensorType() { return tensorType; }
public Optional<StructuredDataType> referenceDocumentType() { return referenceDocumentType; }
+ public static final DistanceMetric DEFAULT_DISTANCE_METRIC = DistanceMetric.EUCLIDEAN;
+ public DistanceMetric distanceMetric() {
+ return distanceMetric.orElse(DEFAULT_DISTANCE_METRIC);
+ }
+ public Optional<HnswIndexParams> hnswIndexParams() { return hnswIndexParams; }
+
public Sorting getSorting() { return sorting; }
public void setRemoveIfZero(boolean remove) { this.removeIfZero = remove; }
@@ -217,6 +228,8 @@ public final class Attribute implements Cloneable, Serializable {
public void setUpperBound(long upperBound) { this.upperBound = upperBound; }
public void setDensePostingListThreshold(double threshold) { this.densePostingListThreshold = threshold; }
public void setTensorType(TensorType tensorType) { this.tensorType = Optional.of(tensorType); }
+ public void setDistanceMetric(Optional<DistanceMetric> dm) { this.distanceMetric = dm; }
+ public void setHnswIndexParams(HnswIndexParams params) { this.hnswIndexParams = Optional.of(params); }
public String getName() { return name; }
public Type getType() { return type; }
@@ -335,7 +348,7 @@ public final class Attribute implements Cloneable, Serializable {
public int hashCode() {
return Objects.hash(
name, type, collectionType, sorting, isPrefetch(), fastAccess, removeIfZero, createIfNonExistent,
- isPosition, huge, enableBitVectors, enableOnlyBitVector, tensorType, referenceDocumentType);
+ isPosition, huge, enableBitVectors, enableOnlyBitVector, tensorType, referenceDocumentType, hnswIndexParams);
}
@Override
@@ -349,8 +362,8 @@ public final class Attribute implements Cloneable, Serializable {
/** Returns whether these attributes describes the same entity, even if they have different names */
public boolean isCompatible(Attribute other) {
- if ( ! this.type.equals(other.type)) return false;
- if ( ! this.collectionType.equals(other.collectionType)) return false;
+ if (! this.type.equals(other.type)) return false;
+ if (! this.collectionType.equals(other.collectionType)) return false;
if (this.isPrefetch() != other.isPrefetch()) return false;
if (this.removeIfZero != other.removeIfZero) return false;
if (this.createIfNonExistent != other.createIfNonExistent) return false;
@@ -359,9 +372,11 @@ public final class Attribute implements Cloneable, Serializable {
// if (this.noSearch != other.noSearch) return false; No backend consequences so compatible for now
if (this.fastSearch != other.fastSearch) return false;
if (this.huge != other.huge) return false;
- if ( ! this.sorting.equals(other.sorting)) return false;
- if (!this.tensorType.equals(other.tensorType)) return false;
- if (!this.referenceDocumentType.equals(other.referenceDocumentType)) return false;
+ if (! this.sorting.equals(other.sorting)) return false;
+ if (! Objects.equals(tensorType, other.tensorType)) return false;
+ if (! Objects.equals(referenceDocumentType, other.referenceDocumentType)) return false;
+ if (! Objects.equals(distanceMetric, other.distanceMetric)) return false;
+ if (! Objects.equals(hnswIndexParams, other.hnswIndexParams)) return false;
return true;
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/FieldSet.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/FieldSet.java
index 55dedc4a1d7..22424286ef9 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/FieldSet.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/FieldSet.java
@@ -25,10 +25,7 @@ public class FieldSet {
public FieldSet addFieldName(String field) { fieldNames.add(field); return this; }
public Set<String> getFieldNames() { return fieldNames; }
public Set<ImmutableSDField> fields() { return fields; }
-
- public Set<String> queryCommands() {
- return queryCommands;
- }
+ public Set<String> queryCommands() { return queryCommands; }
public void setMatching(Matching matching) {
this.matching = matching;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/HnswIndexParams.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/HnswIndexParams.java
new file mode 100644
index 00000000000..2f084d3e513
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/HnswIndexParams.java
@@ -0,0 +1,64 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.searchdefinition.document;
+
+import java.util.Locale;
+import java.util.Optional;
+
+/**
+ * Configuration parameters for a hnsw index used together with a 1-dimensional indexed tensor for approximate nearest neighbor search.
+ *
+ * @author geirst
+ */
+public class HnswIndexParams {
+
+ public static final int DEFAULT_MAX_LINKS_PER_NODE = 16;
+ public static final int DEFAULT_NEIGHBORS_TO_EXPLORE_AT_INSERT = 200;
+
+ private final Optional<Integer> maxLinksPerNode;
+ private final Optional<Integer> neighborsToExploreAtInsert;
+
+ public static class Builder {
+ private Optional<Integer> maxLinksPerNode = Optional.empty();
+ private Optional<Integer> neighborsToExploreAtInsert = Optional.empty();
+
+ public void setMaxLinksPerNode(int value) {
+ maxLinksPerNode = Optional.of(value);
+ }
+ public void setNeighborsToExploreAtInsert(int value) {
+ neighborsToExploreAtInsert = Optional.of(value);
+ }
+ public HnswIndexParams build() {
+ return new HnswIndexParams(maxLinksPerNode, neighborsToExploreAtInsert);
+ }
+ }
+
+ public HnswIndexParams() {
+ this.maxLinksPerNode = Optional.empty();
+ this.neighborsToExploreAtInsert = Optional.empty();
+ }
+
+ public HnswIndexParams(Optional<Integer> maxLinksPerNode,
+ Optional<Integer> neighborsToExploreAtInsert) {
+ this.maxLinksPerNode = maxLinksPerNode;
+ this.neighborsToExploreAtInsert = neighborsToExploreAtInsert;
+ }
+
+ /**
+ * Creates a new instance where values from the given parameter instance are used where they are present,
+ * otherwise we use values from this.
+ */
+ public HnswIndexParams overrideFrom(Optional<HnswIndexParams> other) {
+ if (! other.isPresent()) return this;
+ HnswIndexParams rhs = other.get();
+ return new HnswIndexParams(rhs.maxLinksPerNode.or(() -> maxLinksPerNode),
+ rhs.neighborsToExploreAtInsert.or(() -> neighborsToExploreAtInsert));
+ }
+
+ public int maxLinksPerNode() {
+ return maxLinksPerNode.orElse(DEFAULT_MAX_LINKS_PER_NODE);
+ }
+
+ public int neighborsToExploreAtInsert() {
+ return neighborsToExploreAtInsert.orElse(DEFAULT_NEIGHBORS_TO_EXPLORE_AT_INSERT);
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/SDDocumentType.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/SDDocumentType.java
index 414a605c621..b3f98cc6f26 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/SDDocumentType.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/SDDocumentType.java
@@ -47,6 +47,7 @@ public class SDDocumentType implements Cloneable, Serializable {
private FieldSets fieldSets;
// Document references
private Optional<DocumentReferences> documentReferences = Optional.empty();
+ private TemporaryImportedFields temporaryImportedFields;
static {
VESPA_DOCUMENT = new SDDocumentType(VespaDocumentType.INSTANCE.getFullName().getName());
@@ -58,6 +59,7 @@ public class SDDocumentType implements Cloneable, Serializable {
type.docType = docType.clone();
type.inheritedTypes.putAll(inheritedTypes);
type.structType = structType;
+ // TODO this isn't complete; should it be..?!
return type;
}
@@ -334,4 +336,11 @@ public class SDDocumentType implements Cloneable, Serializable {
this.documentReferences = Optional.of(documentReferences);
}
+ public TemporaryImportedFields getTemporaryImportedFields() {
+ return temporaryImportedFields;
+ }
+
+ public void setTemporaryImportedFields(TemporaryImportedFields temporaryImportedFields) {
+ this.temporaryImportedFields = temporaryImportedFields;
+ }
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java
index c657d29033a..30ce142d503 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/SDField.java
@@ -38,9 +38,8 @@ import java.util.TreeMap;
/**
* The field class represents a document field. It is used in
- * the Document class to get and set fields. Each SDField has
- * a name, a numeric ID, a data type, and a boolean that says whether it's
- * a header field. The numeric ID is used when the fields are stored
+ * the Document class to get and set fields. Each SDField has a name, a numeric ID,
+ * a data type. The numeric ID is used when the fields are stored
* in serialized form.
*
* @author bratseth
@@ -92,7 +91,7 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
private NormalizeLevel normalizing = new NormalizeLevel();
/** Extra query commands of this field */
- private List<String> queryCommands=new java.util.ArrayList<>(0);
+ private List<String> queryCommands = new java.util.ArrayList<>(0);
/** Summary fields defined in this field */
private Map<String, SummaryField> summaryFields = new java.util.LinkedHashMap<>(0);
@@ -120,15 +119,14 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
* Creates a new field. This method is only used to create reserved fields
* @param name The name of the field
* @param dataType The datatype of the field
- * @param isHeader Whether this is a "header" field or a "content" field (true = "header").
*/
- protected SDField(SDDocumentType repo, String name, int id, DataType dataType, boolean isHeader, boolean populate) {
- super(name, id, dataType, isHeader);
- populate(populate, repo, name, dataType, isHeader);
+ protected SDField(SDDocumentType repo, String name, int id, DataType dataType, boolean populate) {
+ super(name, id, dataType);
+ populate(populate, repo, name, dataType);
}
- public SDField(SDDocumentType repo, String name, int id, DataType dataType, boolean isHeader) {
- this(repo, name, id, dataType, isHeader, true);
+ public SDField(SDDocumentType repo, String name, int id, DataType dataType) {
+ this(repo, name, id, dataType, true);
}
/**
@@ -136,41 +134,35 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
@param name The name of the field
@param dataType The datatype of the field
- @param isHeader Whether this is a "header" field or a "content" field
- (true = "header").
*/
- public SDField(SDDocumentType repo, String name, DataType dataType, boolean isHeader, boolean populate) {
- super(name,dataType,isHeader);
- populate(populate, repo, name, dataType, isHeader);
+ public SDField(SDDocumentType repo, String name, DataType dataType, boolean populate) {
+ super(name,dataType);
+ populate(populate, repo, name, dataType);
}
- private void populate(boolean populate, SDDocumentType repo, String name, DataType dataType, boolean isHeader) {
- populate(populate,repo, name, dataType, isHeader, null, 0);
+ private void populate(boolean populate, SDDocumentType repo, String name, DataType dataType) {
+ populate(populate,repo, name, dataType, null, 0);
}
- private void populate(boolean populate, SDDocumentType repo, String name, DataType dataType, boolean isHeader, Matching fieldMatching, int recursion) {
+ private void populate(boolean populate, SDDocumentType repo, String name, DataType dataType, Matching fieldMatching, int recursion) {
if (populate || (dataType instanceof MapDataType)) {
- populateWithStructFields(repo, name, dataType, isHeader, recursion);
+ populateWithStructFields(repo, name, dataType, recursion);
populateWithStructMatching(repo, name, dataType, fieldMatching);
}
}
- public SDField(String name, DataType dataType, boolean isHeader) {
- this(null, name, dataType, isHeader, true);
- }
/**
* Creates a new field.
*
* @param name The name of the field
* @param dataType The datatype of the field
- * @param isHeader Whether this is a "header" field or a "content" field (true = "header").
* @param owner the owning document (used to check for id collisions)
*/
- protected SDField(SDDocumentType repo, String name, DataType dataType, boolean isHeader, SDDocumentType owner, boolean populate) {
- super(name, dataType, isHeader, owner == null ? null : owner.getDocumentType());
+ protected SDField(SDDocumentType repo, String name, DataType dataType, SDDocumentType owner, boolean populate) {
+ super(name, dataType, owner == null ? null : owner.getDocumentType());
this.ownerDocType=owner;
- populate(populate, repo, name, dataType, isHeader);
+ populate(populate, repo, name, dataType);
}
/**
@@ -178,27 +170,25 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
*
* @param name The name of the field
* @param dataType The datatype of the field
- * @param isHeader Whether this is a "header" field or a "content" field (true = "header").
* @param owner The owning document (used to check for id collisions)
* @param fieldMatching The matching object to set for the field
*/
- protected SDField(SDDocumentType repo, String name, DataType dataType, boolean isHeader, SDDocumentType owner,
+ protected SDField(SDDocumentType repo, String name, DataType dataType, SDDocumentType owner,
Matching fieldMatching, boolean populate, int recursion) {
- super(name, dataType, isHeader, owner == null ? null : owner.getDocumentType());
+ super(name, dataType, owner == null ? null : owner.getDocumentType());
this.ownerDocType=owner;
if (fieldMatching != null)
this.setMatching(fieldMatching);
- populate(populate, repo, name, dataType, isHeader, fieldMatching, recursion);
+ populate(populate, repo, name, dataType, fieldMatching, recursion);
}
/**
- * Constructor for <b>header</b> fields
*
* @param name The name of the field
* @param dataType The datatype of the field
*/
public SDField(SDDocumentType repo, String name, DataType dataType) {
- this(repo, name,dataType,true, true);
+ this(repo, name,dataType, true);
}
public SDField(String name, DataType dataType) {
this(null, name,dataType);
@@ -277,7 +267,7 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
}
}
- public void populateWithStructFields(SDDocumentType sdoc, String name, DataType dataType, boolean isHeader, int recursion) {
+ public void populateWithStructFields(SDDocumentType sdoc, String name, DataType dataType, int recursion) {
DataType dt = getFirstStructOrMapRecursive();
if (dt == null) {
return;
@@ -286,11 +276,11 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
MapDataType mdt = (MapDataType) dataType;
SDField keyField = new SDField(sdoc, name.concat(".key"), mdt.getKeyType(),
- isHeader, getOwnerDocType(), new Matching(), true, recursion + 1);
+ getOwnerDocType(), new Matching(), true, recursion + 1);
structFields.put("key", keyField);
SDField valueField = new SDField(sdoc, name.concat(".value"), mdt.getValueType(),
- isHeader, getOwnerDocType(), new Matching(), true, recursion + 1);
+ getOwnerDocType(), new Matching(), true, recursion + 1);
structFields.put("value", valueField);
} else {
if (recursion >= 10) {
@@ -306,7 +296,7 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
}
for (Field field : subType.fieldSet()) {
SDField subField = new SDField(sdoc, name.concat(".").concat(field.getName()), field.getDataType(),
- isHeader, subType, new Matching(), true, recursion + 1);
+ subType, new Matching(), true, recursion + 1);
structFields.put(field.getName(), subField);
}
}
@@ -759,20 +749,11 @@ public class SDField extends Field implements TypedKey, FieldOperationContainer,
return queryCommands.contains(name);
}
- /**
- * A list of query commands
- *
- * @return a list of strings with query commands.
- */
+ /** Returns a list of query commands */
@Override
- public List<String> getQueryCommands() {
- return queryCommands;
- }
+ public List<String> getQueryCommands() { return queryCommands; }
- /**
- * The document that this field was declared in, or null
- *
- */
+ /** Returns the document that this field was declared in, or null */
private SDDocumentType getOwnerDocType() {
return ownerDocType;
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/TemporarySDField.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/TemporarySDField.java
index 886bf777d3a..04d11792379 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/TemporarySDField.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/TemporarySDField.java
@@ -8,12 +8,12 @@ import com.yahoo.document.DataType;
*/
public class TemporarySDField extends SDField {
- public TemporarySDField(String name, DataType dataType, boolean isHeader, SDDocumentType owner) {
- super(owner, name, dataType, isHeader, owner, false);
+ public TemporarySDField(String name, DataType dataType, SDDocumentType owner) {
+ super(owner, name, dataType, owner, false);
}
public TemporarySDField(String name, DataType dataType) {
- super(null, name, dataType, true, false);
+ super(null, name, dataType, false);
}
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/ExpressionTransforms.java b/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/ExpressionTransforms.java
index 6fdf448a39b..a6707ec7ac0 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/ExpressionTransforms.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/ExpressionTransforms.java
@@ -27,6 +27,7 @@ public class ExpressionTransforms {
ImmutableList.of(new TensorFlowFeatureConverter(),
new OnnxFeatureConverter(),
new XgboostFeatureConverter(),
+ new LightGBMFeatureConverter(),
new ConstantDereferencer(),
new ConstantTensorTransformer(),
new FunctionInliner(),
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/LightGBMFeatureConverter.java b/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/LightGBMFeatureConverter.java
new file mode 100644
index 00000000000..5bde627dc0a
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/expressiontransforms/LightGBMFeatureConverter.java
@@ -0,0 +1,59 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.searchdefinition.expressiontransforms;
+
+import com.yahoo.path.Path;
+import com.yahoo.searchlib.rankingexpression.rule.Arguments;
+import com.yahoo.searchlib.rankingexpression.rule.CompositeNode;
+import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
+import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
+import com.yahoo.searchlib.rankingexpression.transform.ExpressionTransformer;
+import com.yahoo.vespa.model.ml.ConvertedModel;
+import com.yahoo.vespa.model.ml.FeatureArguments;
+
+import java.io.UncheckedIOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Replaces instances of the lightgbm(model-path) pseudofeature with the
+ * native Vespa ranking expression implementing the same computation.
+ *
+ * @author lesters
+ */
+public class LightGBMFeatureConverter extends ExpressionTransformer<RankProfileTransformContext> {
+
+ /** A cache of imported models indexed by model path. This avoids importing the same model multiple times. */
+ private final Map<Path, ConvertedModel> convertedLightGBMModels = new HashMap<>();
+
+ @Override
+ public ExpressionNode transform(ExpressionNode node, RankProfileTransformContext context) {
+ if (node instanceof ReferenceNode)
+ return transformFeature((ReferenceNode) node, context);
+ else if (node instanceof CompositeNode)
+ return super.transformChildren((CompositeNode) node, context);
+ else
+ return node;
+ }
+
+ private ExpressionNode transformFeature(ReferenceNode feature, RankProfileTransformContext context) {
+ if ( ! feature.getName().equals("lightgbm")) return feature;
+
+ try {
+ FeatureArguments arguments = asFeatureArguments(feature.getArguments());
+ ConvertedModel convertedModel =
+ convertedLightGBMModels.computeIfAbsent(arguments.path(),
+ path -> ConvertedModel.fromSourceOrStore(path, true, context));
+ return convertedModel.expression(arguments, context);
+ } catch (IllegalArgumentException | UncheckedIOException e) {
+ throw new IllegalArgumentException("Could not use LightGBM model from " + feature, e);
+ }
+ }
+
+ private FeatureArguments asFeatureArguments(Arguments arguments) {
+ if (arguments.size() != 1)
+ throw new IllegalArgumentException("A lightgbm node must take a single argument pointing to " +
+ "the LightGBM model file under [application]/models");
+ return new FeatureArguments(arguments);
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/FieldOperationContainer.java b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/FieldOperationContainer.java
index 09e8b3ccdfa..ab9148992e0 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/FieldOperationContainer.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/FieldOperationContainer.java
@@ -11,7 +11,7 @@ public interface FieldOperationContainer {
/** Adds an operation */
void addOperation(FieldOperation op);
- /** Apply all operations. Operations must be sorted in thjeir natural order before applying each operation. */
+ /** Apply all operations. Operations must be sorted in their natural order before applying each operation. */
void applyOperations(SDField field);
String getName();
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexOperation.java b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexOperation.java
index 39f543c7db3..0c1f443dee3 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexOperation.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexOperation.java
@@ -4,6 +4,7 @@ package com.yahoo.searchdefinition.fieldoperation;
import com.yahoo.searchdefinition.Index;
import com.yahoo.searchdefinition.Index.Type;
import com.yahoo.searchdefinition.document.BooleanIndexDefinition;
+import com.yahoo.searchdefinition.document.HnswIndexParams;
import com.yahoo.searchdefinition.document.SDField;
import com.yahoo.searchdefinition.document.Stemming;
@@ -31,6 +32,9 @@ public class IndexOperation implements FieldOperation {
private OptionalDouble densePostingListThreshold = OptionalDouble.empty();
private Optional<Boolean> enableBm25 = Optional.empty();
+ private Optional<String> distanceMetric = Optional.empty();
+ private Optional<HnswIndexParams.Builder> hnswIndexParams = Optional.empty();
+
public String getIndexName() {
return indexName;
}
@@ -91,6 +95,12 @@ public class IndexOperation implements FieldOperation {
if (enableBm25.isPresent()) {
index.setInterleavedFeatures(enableBm25.get());
}
+ if (distanceMetric.isPresent()) {
+ index.setDistanceMetric(distanceMetric.get());
+ }
+ if (hnswIndexParams.isPresent()) {
+ index.setHnswIndexParams(hnswIndexParams.get().build());
+ }
}
public Type getType() {
@@ -116,8 +126,17 @@ public class IndexOperation implements FieldOperation {
public void setDensePostingListThreshold(double densePostingListThreshold) {
this.densePostingListThreshold = OptionalDouble.of(densePostingListThreshold);
}
+
public void setEnableBm25(boolean value) {
enableBm25 = Optional.of(value);
}
+ public void setDistanceMetric(String value) {
+ this.distanceMetric = Optional.of(value);
+ }
+
+ public void setHnswIndexParams(HnswIndexParams.Builder params) {
+ this.hnswIndexParams = Optional.of(params);
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingRewriteOperation.java b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingRewriteOperation.java
index a0d47d7fa81..0a29fae04bf 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingRewriteOperation.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingRewriteOperation.java
@@ -4,9 +4,10 @@ package com.yahoo.searchdefinition.fieldoperation;
import com.yahoo.searchdefinition.document.SDField;
/**
- * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @author Einar M R Rosenvinge
*/
public class IndexingRewriteOperation implements FieldOperation {
- public void apply(SDField field) {
- }
+
+ public void apply(SDField field) { }
+
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddExtraFieldsToDocument.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddExtraFieldsToDocument.java
index d1eb18c4916..0ffd13927b4 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddExtraFieldsToDocument.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddExtraFieldsToDocument.java
@@ -71,7 +71,7 @@ public class AddExtraFieldsToDocument extends Processor {
if (docField == null) {
ImmutableSDField existingField = search.getField(field.getName());
if (existingField == null) {
- SDField newField = new SDField(document, field.getName(), field.getDataType(), field.isHeader(), true);
+ SDField newField = new SDField(document, field.getName(), field.getDataType(), true);
newField.setIsExtraField(true);
document.addField(newField);
} else if (!existingField.isImportedField()) {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java
index d6c334ee80b..6d3de23238d 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java
@@ -64,7 +64,7 @@ public class ImportedFieldsResolver extends Processor {
}
private void resolveImportedPositionField(TemporaryImportedField importedField, DocumentReference reference,
- ImmutableSDField targetField, boolean validate) {
+ ImmutableSDField targetField, boolean validate) {
TemporaryImportedField importedZCurveField = new TemporaryImportedField(PositionDataType.getZCurveFieldName(importedField.fieldName()),
reference.referenceField().getName(), PositionDataType.getZCurveFieldName(targetField.getName()));
ImmutableSDField targetZCurveField = getTargetField(importedZCurveField, reference);
@@ -175,7 +175,7 @@ public class ImportedFieldsResolver extends Processor {
}
private void validateTargetField(TemporaryImportedField importedField,
- ImmutableSDField targetField, DocumentReference reference) {
+ ImmutableSDField targetField, DocumentReference reference) {
if (!targetField.doesAttributing()) {
fail(importedField, targetFieldAsString(targetField.getName(), reference) +
": Is not an attribute field. Only attribute fields supported");
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolver.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolver.java
index 9a9e9bbba5f..89b8889b4ae 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolver.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolver.java
@@ -15,7 +15,10 @@ import com.yahoo.tensor.TensorType;
import com.yahoo.tensor.evaluation.TypeContext;
import com.yahoo.vespa.model.container.search.QueryProfiles;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import java.util.logging.Level;
/**
@@ -45,9 +48,10 @@ public class RankingExpressionTypeResolver extends Processor {
public void process(boolean validate, boolean documentsOnly) {
if (documentsOnly) return;
+ Set<Reference> warnedAbout = new HashSet<>();
for (RankProfile profile : rankProfileRegistry.rankProfilesOf(search)) {
try {
- resolveTypesIn(profile, validate);
+ resolveTypesIn(profile, validate, warnedAbout);
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException("In " + (search != null ? search + ", " : "") + profile, e);
@@ -60,7 +64,7 @@ public class RankingExpressionTypeResolver extends Processor {
*
* @throws IllegalArgumentException if validate is true and the given rank profile does not produce valid types
*/
- private void resolveTypesIn(RankProfile profile, boolean validate) {
+ private void resolveTypesIn(RankProfile profile, boolean validate, Set<Reference> warnedAbout) {
MapEvaluationTypeContext context = profile.typeContext(queryProfiles);
for (Map.Entry<String, RankProfile.RankingExpressionFunction> function : profile.getFunctions().entrySet()) {
ExpressionFunction expressionFunction = function.getValue().function();
@@ -83,10 +87,14 @@ public class RankingExpressionTypeResolver extends Processor {
profile.getSummaryFeatures().forEach(f -> resolveType(f, "summary feature " + f, context));
ensureValidDouble(profile.getFirstPhaseRanking(), "first-phase expression", context);
ensureValidDouble(profile.getSecondPhaseRanking(), "second-phase expression", context);
- if ( context.tensorsAreUsed() && ! context.queryFeaturesNotDeclared().isEmpty()) {
- deployLogger.log(Level.WARNING, "The following query features are not declared in query profile " +
+ if ( context.tensorsAreUsed() &&
+ ! context.queryFeaturesNotDeclared().isEmpty() &&
+ ! warnedAbout.containsAll(context.queryFeaturesNotDeclared())) {
+ deployLogger.log(Level.WARNING, "The following query features used in '" + profile.getName() +
+ "' are not declared in query profile " +
"types and will be interpreted as scalars, not tensors: " +
context.queryFeaturesNotDeclared());
+ warnedAbout.addAll(context.queryFeaturesNotDeclared());
}
}
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/TensorFieldProcessor.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/TensorFieldProcessor.java
index 8e54d7c00d6..c97ee2bd935 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/TensorFieldProcessor.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/TensorFieldProcessor.java
@@ -6,7 +6,8 @@ import com.yahoo.document.CollectionDataType;
import com.yahoo.document.TensorDataType;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.Search;
-import com.yahoo.searchdefinition.document.Attribute;
+import com.yahoo.searchdefinition.document.HnswIndexParams;
+import com.yahoo.searchdefinition.document.ImmutableSDField;
import com.yahoo.searchdefinition.document.SDField;
import com.yahoo.vespa.model.container.search.QueryProfiles;
@@ -23,34 +24,71 @@ public class TensorFieldProcessor extends Processor {
@Override
public void process(boolean validate, boolean documentsOnly) {
- if ( ! validate) return;
-
- for (SDField field : search.allConcreteFields()) {
+ for (var field : search.allConcreteFields()) {
if ( field.getDataType() instanceof TensorDataType ) {
- validateIndexingScripsForTensorField(field);
- validateAttributeSettingForTensorField(field);
+ if (validate) {
+ validateIndexingScripsForTensorField(field);
+ validateAttributeSettingForTensorField(field);
+ }
+ processIndexSettingsForTensorField(field, validate);
}
else if (field.getDataType() instanceof CollectionDataType){
- validateDataTypeForCollectionField(field);
+ if (validate) {
+ validateDataTypeForCollectionField(field);
+ }
}
}
}
private void validateIndexingScripsForTensorField(SDField field) {
- if (field.doesIndexing()) {
- fail(search, field, "A field of type 'tensor' cannot be specified as an 'index' field.");
+ if (field.doesIndexing() && !isTensorTypeThatSupportsHnswIndex(field)) {
+ fail(search, field, "A tensor of type '" + tensorTypeToString(field) + "' does not support having an 'index'. " +
+ "Currently, only tensors with 1 indexed dimension supports that.");
+ }
+ }
+
+ private boolean isTensorTypeThatSupportsHnswIndex(ImmutableSDField field) {
+ var type = ((TensorDataType)field.getDataType()).getTensorType();
+ // Tensors with 1 indexed dimension supports a hnsw index (used for approximate nearest neighbor search).
+ if ((type.dimensions().size() == 1) &&
+ type.dimensions().get(0).isIndexed()) {
+ return true;
}
+ return false;
+ }
+
+ private String tensorTypeToString(ImmutableSDField field) {
+ return ((TensorDataType)field.getDataType()).getTensorType().toString();
}
private void validateAttributeSettingForTensorField(SDField field) {
if (field.doesAttributing()) {
- Attribute attribute = field.getAttributes().get(field.getName());
+ var attribute = field.getAttributes().get(field.getName());
if (attribute != null && attribute.isFastSearch()) {
fail(search, field, "An attribute of type 'tensor' cannot be 'fast-search'.");
}
}
}
+ private void processIndexSettingsForTensorField(SDField field, boolean validate) {
+ if (!field.doesIndexing()) {
+ return;
+ }
+ if (isTensorTypeThatSupportsHnswIndex(field)) {
+ if (validate && !field.doesAttributing()) {
+ fail(search, field, "A tensor that has an index must also be an attribute.");
+ }
+ var index = field.getIndex(field.getName());
+ // TODO: Calculate default params based on tensor dimension size
+ var params = new HnswIndexParams();
+ if (index != null) {
+ params = params.overrideFrom(index.getHnswIndexParams());
+ field.getAttribute().setDistanceMetric(index.getDistanceMetric());
+ }
+ field.getAttribute().setHnswIndexParams(params);
+ }
+ }
+
private void validateDataTypeForCollectionField(SDField field) {
if (((CollectionDataType)field.getDataType()).getNestedType() instanceof TensorDataType)
fail(search, field, "A field with collection type of tensor is not supported. Use simple type 'tensor' instead.");
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/UriHack.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/UriHack.java
index d0a0bbfb748..d6398bc348c 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/UriHack.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/UriHack.java
@@ -61,7 +61,7 @@ public class UriHack extends Processor {
String partName = uriName + "." + suffix;
// I wonder if this is explicit in qrs or implicit in backend?
// search.addFieldSetItem(uriName, partName);
- SDField partField = new SDField(partName, generatedType, true);
+ SDField partField = new SDField(partName, generatedType);
partField.setIndexStructureField(uriField.doesIndexing());
partField.setRankType(uriField.getRankType());
partField.setStemming(Stemming.NONE);
diff --git a/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentManager.java b/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentManager.java
index 02d500931d7..e0d015cc8b2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentManager.java
+++ b/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentManager.java
@@ -62,6 +62,7 @@ public class DocumentManager {
}
}
}
+
private void buildConfig(Collection<AnnotationType> types, DocumentmanagerConfig.Builder builder) {
for (AnnotationType type : types) {
DocumentmanagerConfig.Annotationtype.Builder atb = new DocumentmanagerConfig.Annotationtype.Builder();
@@ -110,6 +111,7 @@ public class DocumentManager {
doc.inherits(new Datatype.Documenttype.Inherits.Builder().name(inherited.getName()));
}
buildConfig(dt.getFieldSets(), doc);
+ buildImportedFieldsConfig(dt.getImportedFieldNames(), doc);
} else if (type instanceof TemporaryStructuredDataType) {
//Ignored
} else if (type instanceof StructDataType) {
@@ -164,4 +166,12 @@ public class DocumentManager {
doc.fieldsets(fs.getName(), new Datatype.Documenttype.Fieldsets.Builder().fields(fs.getFieldNames()));
}
+ private void buildImportedFieldsConfig(Collection<String> fieldNames, Datatype.Documenttype.Builder builder) {
+ for (String fieldName : fieldNames) {
+ var ib = new DocumentmanagerConfig.Datatype.Documenttype.Importedfield.Builder();
+ ib.name(fieldName);
+ builder.importedfield(ib);
+ }
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentTypes.java b/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentTypes.java
index d4228b52746..e6bf826dccc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentTypes.java
+++ b/config-model/src/main/java/com/yahoo/vespa/configmodel/producers/DocumentTypes.java
@@ -58,6 +58,7 @@ public class DocumentTypes {
buildConfig(annotation, atb);
}
buildConfig(documentType.getFieldSets(), db);
+ buildImportedFieldsConfig(documentType.getImportedFieldNames(), db);
builder.documenttype(db);
}
@@ -120,6 +121,14 @@ public class DocumentTypes {
}
}
+ private void buildImportedFieldsConfig(Collection<String> fieldNames, DocumenttypesConfig.Documenttype.Builder builder) {
+ for (String fieldName : fieldNames) {
+ var ib = new DocumenttypesConfig.Documenttype.Importedfield.Builder();
+ ib.name(fieldName);
+ builder.importedfield(ib);
+ }
+ }
+
private void buildConfig(StructDataType type,
DocumenttypesConfig.Documenttype.Datatype.Builder dataTypeBuilder,
DocumenttypesConfig.Documenttype.Builder documentBuilder,
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java b/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
index 72c475fb091..75b33e184d9 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
@@ -461,6 +461,7 @@ public abstract class AbstractService extends AbstractConfigProducer<AbstractCon
public FileReference sendFile(String relativePath) {
return getRoot().getFileDistributor().sendFileToHost(relativePath, getHost());
}
+
public FileReference sendUri(String uri) {
return getRoot().getFileDistributor().sendUriToHost(uri, getHost());
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducer.java b/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducer.java
index bf86bc4a453..48cd378a9a6 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/ConfigProducer.java
@@ -1,16 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.List;
-import java.util.Map;
-
import com.yahoo.config.ConfigInstance;
import com.yahoo.config.ConfigInstance.Builder;
import com.yahoo.config.model.producer.UserConfigRepo;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+
/**
* Interface that should be implemented by all config producing modules
* in the vespa model.
@@ -35,17 +33,6 @@ public interface ConfigProducer extends com.yahoo.config.ConfigInstance.Producer
List<Service> getDescendantServices();
/**
- * Writes files that need to be written. The files will usually
- * only be written when the Vespa model is generated through the
- * deploy-application script.
- * This is primarily intended for debugging.
- *
- * @param directory directory to write files to
- * @throws java.io.IOException if writing fails
- */
- void writeFiles(File directory) throws IOException;
-
- /**
* Dump the three of config producers to the specified stream.
*
* @param out The stream to print to, e.g. System.out
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/Host.java b/config-model/src/main/java/com/yahoo/vespa/model/Host.java
index a952d63526b..db469c2091e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/Host.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/Host.java
@@ -1,12 +1,11 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model;
-import java.io.File;
-import java.util.Objects;
-
import com.yahoo.cloud.config.SentinelConfig;
import com.yahoo.config.model.producer.AbstractConfigProducer;
+import java.util.Objects;
+
/**
* A physical host, running a set of services.
* The identity of a host is its hostname. Hosts are comparable on their host name.
@@ -67,10 +66,6 @@ public final class Host extends AbstractConfigProducer<AbstractConfigProducer<?>
}
@Override
- public void writeFiles(File directory) {
- }
-
- @Override
public void getConfig(SentinelConfig.Builder builder) {
// TODO (MAJOR_RELEASE): This shouldn't really be here, but we need to make sure users can upgrade if we change sentinel to use hosts/<hostname>/sentinel instead of hosts/<hostname>
// as config id. We should probably wait for a major release
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 eda562bea5a..557a61ec211 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
@@ -11,7 +11,6 @@ import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.ProvisionLogger;
import java.net.UnknownHostException;
-import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -34,13 +33,11 @@ public class HostSystem extends AbstractConfigProducer<Host> {
private static Logger log = Logger.getLogger(HostSystem.class.getName());
- private Map<String,String> hostnames = new LinkedHashMap<>();
-
private final Map<String, HostResource> hostname2host = new LinkedHashMap<>();
private final HostProvisioner provisioner;
private final DeployLogger deployLogger;
- public HostSystem(AbstractConfigProducer parent, String name, HostProvisioner provisioner, DeployLogger deployLogger) {
+ public HostSystem(AbstractConfigProducer<?> parent, String name, HostProvisioner provisioner, DeployLogger deployLogger) {
super(parent, name);
this.provisioner = provisioner;
this.deployLogger = deployLogger;
@@ -49,7 +46,8 @@ public class HostSystem extends AbstractConfigProducer<Host> {
void checkName(String hostname) {
// Give a warning if the host does not exist
try {
- Object address = java.net.InetAddress.getByName(hostname);
+ @SuppressWarnings("unused")
+ Object ignore = java.net.InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
deployLogger.log(Level.WARNING, "Unable to lookup IP address of host: " + hostname);
}
@@ -78,36 +76,10 @@ public class HostSystem extends AbstractConfigProducer<Host> {
return hostname2host.get(name);
}
- /**
- * Returns the canonical name of a given host. This will cache names for faster lookup.
- *
- * @param hostname the hostname to retrieve the canonical hostname for.
- * @return The canonical hostname, or null if unable to resolve.
- * @throws UnknownHostException if the hostname cannot be resolved
- */
- public String getCanonicalHostname(String hostname) throws UnknownHostException {
- if ( ! hostnames.containsKey(hostname)) {
- hostnames.put(hostname, lookupCanonicalHostname(hostname));
- }
- return hostnames.get(hostname);
- }
-
- /**
- * Static helper method that looks up the canonical name of a given host.
- *
- * @param hostname the hostname to retrieve the canonical hostname for.
- * @return The canonical hostname, or null if unable to resolve.
- * @throws UnknownHostException if the hostname cannot be resolved
- */
- // public - This is used by amenders outside this repo
- public static String lookupCanonicalHostname(String hostname) throws UnknownHostException {
- return java.net.InetAddress.getByName(hostname).getCanonicalHostName();
- }
-
@Override
public String toString() {
return "hosts [" + hostname2host.values().stream()
- .map(host -> host.getHostname())
+ .map(HostResource::getHostname)
.collect(Collectors.joining(", ")) +
"]";
}
@@ -139,8 +111,8 @@ public class HostSystem extends AbstractConfigProducer<Host> {
}
}
- public Map<HostResource, ClusterMembership> allocateHosts(ClusterSpec cluster, Capacity capacity, int groups, DeployLogger logger) {
- List<HostSpec> allocatedHosts = provisioner.prepare(cluster, capacity, groups, new ProvisionDeployLogger(logger));
+ public Map<HostResource, ClusterMembership> allocateHosts(ClusterSpec cluster, Capacity capacity, DeployLogger logger) {
+ List<HostSpec> allocatedHosts = provisioner.prepare(cluster, capacity, new ProvisionDeployLogger(logger));
// TODO: Even if HostResource owns a set of memberships, we need to return a map because the caller needs the current membership.
Map<HostResource, ClusterMembership> retAllocatedHosts = new LinkedHashMap<>();
for (HostSpec spec : allocatedHosts) {
@@ -169,7 +141,7 @@ public class HostSystem extends AbstractConfigProducer<Host> {
}
Set<HostSpec> getHostSpecs() {
- return getHosts().stream().map(host -> host.spec()).collect(Collectors.toCollection(LinkedHashSet::new));
+ return getHosts().stream().map(HostResource::spec).collect(Collectors.toCollection(LinkedHashSet::new));
}
/** A provision logger which forwards to a deploy logger */
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
index 2cfd2e23d08..6f2123f248c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
@@ -23,11 +23,13 @@ import com.yahoo.config.model.api.FileDistribution;
import com.yahoo.config.model.api.HostInfo;
import ai.vespa.rankingexpression.importer.configmodelview.ImportedMlModels;
import com.yahoo.config.model.api.Model;
+import com.yahoo.config.model.api.Provisioned;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
import com.yahoo.config.model.producer.UserConfigRepo;
import com.yahoo.config.provision.AllocatedHosts;
+import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.log.LogLevel;
import com.yahoo.searchdefinition.RankProfile;
import com.yahoo.searchdefinition.RankProfileRegistry;
@@ -61,7 +63,6 @@ import com.yahoo.vespa.model.utils.internal.ReflectionUtil;
import com.yahoo.yolean.Exceptions;
import org.xml.sax.SAXException;
-import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
@@ -123,6 +124,8 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
private final FileDistributor fileDistributor;
+ private final Provisioned provisioned;
+
/** Creates a Vespa Model from internal model types only */
public VespaModel(ApplicationPackage app) throws IOException, SAXException {
this(app, new NullConfigModelRegistry());
@@ -163,6 +166,7 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
configModelRegistry = new VespaConfigModelRegistry(configModelRegistry);
VespaModelBuilder builder = new VespaDomBuilder();
this.applicationPackage = deployState.getApplicationPackage();
+ this.provisioned = deployState.provisioned();
root = builder.getRoot(VespaModel.ROOT_CONFIGID, deployState, this);
createGlobalRankProfiles(deployState.getDeployLogger(), deployState.getImportedModels(),
@@ -205,7 +209,8 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
/** Creates a mutable model with no services instantiated */
public static VespaModel createIncomplete(DeployState deployState) throws IOException, SAXException {
- return new VespaModel(new NullConfigModelRegistry(), deployState, false, new FileDistributor(deployState.getFileRegistry(), null));
+ return new VespaModel(new NullConfigModelRegistry(), deployState, false,
+ new FileDistributor(deployState.getFileRegistry(), List.of(), deployState.isHosted()));
}
private void validateWrapExceptions() {
@@ -564,23 +569,6 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
id2producer.put(configId, descendant);
}
- /**
- * Writes MODEL.cfg files for all config producers.
- *
- * @param baseDirectory dir to write files to
- */
- public void writeFiles(File baseDirectory) throws IOException {
- super.writeFiles(baseDirectory);
- for (ConfigProducer cp : id2producer.values()) {
- try {
- File destination = new File(baseDirectory, cp.getConfigId().replace("/", File.separator));
- cp.writeFiles(destination);
- } catch (IOException e) {
- throw new IOException(cp.getConfigId() + ": " + e.getMessage());
- }
- }
- }
-
public Clients getClients() {
return configModelRepo.getClients();
}
@@ -629,11 +617,23 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
return Collections.unmodifiableMap(id2producer);
}
- /**
- * Returns this root's model repository
- */
+ /** Returns this root's model repository */
public ConfigModelRepo configModelRepo() {
return configModelRepo;
}
+ /** If provisioning through the node repo, returns the provision requests issued during build of this */
+ public Provisioned provisioned() { return provisioned; }
+
+ /** Returns the id of all clusters in this */
+ public Set<ClusterSpec.Id> allClusters() {
+ return hostSystem().getHosts().stream()
+ .map(HostResource::spec)
+ .filter(spec -> spec.membership().isPresent())
+ .map(spec -> spec.membership().get().cluster().id())
+ .collect(Collectors.toSet());
+ }
+
+
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java
index 54807697da4..631f4dab1a7 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java
@@ -146,11 +146,13 @@ public class VespaModelFactory implements ModelFactory {
.properties(modelContext.properties())
.vespaVersion(version())
.modelHostProvisioner(createHostProvisioner(modelContext))
+ .provisioned(modelContext.provisioned())
.endpoints(modelContext.properties().endpoints())
.modelImporters(modelImporters)
.zone(zone)
.now(clock.instant())
- .wantedNodeVespaVersion(modelContext.wantedNodeVespaVersion());
+ .wantedNodeVespaVersion(modelContext.wantedNodeVespaVersion())
+ .wantedDockerImageRepo(modelContext.wantedDockerImageRepository());
modelContext.previousModel().ifPresent(builder::previousModel);
return builder.build(validationParameters);
}
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 5714d41ef67..24e88d7ef7d 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
@@ -4,13 +4,14 @@ package com.yahoo.vespa.model.admin;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.container.handler.ThreadpoolConfig;
+import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.Handler;
/**
* @author hmusum
*/
-public class LogserverContainerCluster extends ContainerCluster<LogserverContainer> implements ThreadpoolConfig.Producer {
+public class LogserverContainerCluster extends ContainerCluster<LogserverContainer> {
public LogserverContainerCluster(AbstractConfigProducer<?> parent, String name, DeployState deployState) {
super(parent, name, name, deployState);
@@ -27,6 +28,12 @@ public class LogserverContainerCluster extends ContainerCluster<LogserverContain
builder.maxthreads(10);
}
+ @Override
+ public void getConfig(QrStartConfig.Builder builder) {
+ super.getConfig(builder);
+ builder.jvm.heapsize(384);
+ }
+
protected boolean messageBusEnabled() { return false; }
private void addLogHandler() {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
index 2de4e5f5950..41d9df414ea 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
@@ -11,8 +11,7 @@ import com.yahoo.vespa.model.container.ContainerCluster;
*
* @author gjoranv
*/
-public class ClusterControllerContainerCluster extends ContainerCluster<ClusterControllerContainer> implements
- ThreadpoolConfig.Producer
+public class ClusterControllerContainerCluster extends ContainerCluster<ClusterControllerContainer>
{
public ClusterControllerContainerCluster(AbstractConfigProducer<?> parent, String subId, String name, DeployState deployState) {
super(parent, subId, name, deployState);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
index fd924eb2a0f..fccacc3210d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainer.java
@@ -2,6 +2,8 @@
package com.yahoo.vespa.model.admin.metricsproxy;
+import ai.vespa.metricsproxy.http.metrics.MetricsV2Handler;
+import ai.vespa.metricsproxy.http.metrics.NodeInfoConfig;
import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
import ai.vespa.metricsproxy.metric.dimensions.PublicDimensions;
@@ -20,6 +22,7 @@ import java.util.Map;
import static com.yahoo.config.model.api.container.ContainerServiceType.METRICS_PROXY_CONTAINER;
import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.METRICS_PROXY_BUNDLE_NAME;
+import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.createMetricsHandler;
/**
* Container running a metrics proxy.
@@ -28,9 +31,11 @@ import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerClus
*/
public class MetricsProxyContainer extends Container implements
NodeDimensionsConfig.Producer,
+ NodeInfoConfig.Producer,
RpcConnectorConfig.Producer,
VespaServicesConfig.Producer
{
+ public static final int BASEPORT = 19092;
final boolean isHostedVespa;
@@ -46,6 +51,7 @@ public class MetricsProxyContainer extends Container implements
addMetricsProxyComponent(NodeDimensions.class);
addMetricsProxyComponent(RpcConnector.class);
addMetricsProxyComponent(VespaServices.class);
+ addHandler(createMetricsHandler(MetricsV2Handler.class, MetricsV2Handler.V2_PATH));
}
@Override
@@ -53,8 +59,6 @@ public class MetricsProxyContainer extends Container implements
return METRICS_PROXY_CONTAINER;
}
- static public int BASEPORT = 19092;
-
@Override
public int getWantedPort() {
return BASEPORT;
@@ -121,7 +125,21 @@ public class MetricsProxyContainer extends Container implements
}
}
- private void addMetricsProxyComponent(Class<?> componentClass) {
+ @Override
+ public void getConfig(NodeInfoConfig.Builder builder) {
+ builder.role(getNodeRole())
+ .hostname(getHostName());
+ }
+
+ private String getNodeRole() {
+ String hostConfigId = getHost().getConfigId();
+ if (! isHostedVespa) return hostConfigId;
+ return getHostResource().spec().membership()
+ .map(ClusterMembership::stringValue)
+ .orElse(hostConfigId);
+ }
+
+ private void addMetricsProxyComponent(Class<?> componentClass) {
addSimpleComponent(componentClass.getName(), null, METRICS_PROXY_BUNDLE_NAME);
}
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 fcc6c3279de..20f2bfe6636 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
@@ -20,6 +20,9 @@ import ai.vespa.metricsproxy.metric.dimensions.PublicDimensions;
import ai.vespa.metricsproxy.rpc.RpcServer;
import ai.vespa.metricsproxy.service.ConfigSentinelClient;
import ai.vespa.metricsproxy.service.SystemPollerProvider;
+import ai.vespa.metricsproxy.telegraf.Telegraf;
+import ai.vespa.metricsproxy.telegraf.TelegrafConfig;
+import ai.vespa.metricsproxy.telegraf.TelegrafRegistry;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
@@ -67,7 +70,7 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
ApplicationDimensionsConfig.Producer,
ConsumersConfig.Producer,
MonitoringConfig.Producer,
- ThreadpoolConfig.Producer,
+ TelegrafConfig.Producer,
MetricsNodesConfig.Producer
{
public static final Logger log = Logger.getLogger(MetricsProxyContainerCluster.class.getName());
@@ -116,14 +119,30 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
addHttpHandler(ApplicationMetricsHandler.class, ApplicationMetricsHandler.V1_PATH);
addMetricsProxyComponent(ApplicationMetricsRetriever.class);
+
+ addTelegrafComponents();
}
private void addHttpHandler(Class<? extends ThreadedHttpRequestHandler> clazz, String bindingPath) {
+ Handler<AbstractConfigProducer<?>> metricsHandler = createMetricsHandler(clazz, bindingPath);
+ addComponent(metricsHandler);
+ }
+
+ static Handler<AbstractConfigProducer<?>> createMetricsHandler(Class<? extends ThreadedHttpRequestHandler> clazz, String bindingPath) {
Handler<AbstractConfigProducer<?>> metricsHandler = new Handler<>(
new ComponentModel(clazz.getName(), null, METRICS_PROXY_BUNDLE_NAME, null));
metricsHandler.addServerBindings("http://*" + bindingPath,
"http://*" + bindingPath + "/*");
- addComponent(metricsHandler);
+ return metricsHandler;
+ }
+
+ private void addTelegrafComponents() {
+ getAdmin().ifPresent(admin -> {
+ if (admin.getUserMetrics().usesExternalMetricSystems()) {
+ addMetricsProxyComponent(Telegraf.class);
+ addMetricsProxyComponent(TelegrafRegistry.class);
+ }
+ });
}
@Override
@@ -156,6 +175,32 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
}
@Override
+ public void getConfig(TelegrafConfig.Builder builder) {
+ builder.isHostedVespa(isHostedVespa());
+
+ var userConsumers = getUserMetricsConsumers();
+ for (var consumer : userConsumers.values()) {
+ for (var cloudWatch : consumer.cloudWatches()) {
+ var cloudWatchBuilder = new TelegrafConfig.CloudWatch.Builder();
+ cloudWatchBuilder
+ .region(cloudWatch.region())
+ .namespace(cloudWatch.namespace())
+ .consumer(cloudWatch.consumer());
+
+ cloudWatch.hostedAuth().ifPresent(hostedAuth -> cloudWatchBuilder
+ .accessKeyName(hostedAuth.accessKeyName)
+ .secretKeyName(hostedAuth.secretKeyName));
+
+ cloudWatch.sharedCredentials().ifPresent(sharedCredentials -> {
+ cloudWatchBuilder.file(sharedCredentials.file);
+ sharedCredentials.profile.ifPresent(cloudWatchBuilder::profile);
+ });
+ builder.cloudWatch(cloudWatchBuilder);
+ }
+ }
+ }
+
+ @Override
public void getConfig(ThreadpoolConfig.Builder builder) {
builder.maxthreads(10);
}
@@ -198,7 +243,7 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
Optional.of(monitoring.getInterval()) : Optional.empty();
}
- private void addMetricsProxyComponent(Class<?> componentClass) {
+ private void addMetricsProxyComponent(Class<?> componentClass) {
addSimpleComponent(componentClass.getName(), null, METRICS_PROXY_BUNDLE_NAME);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java
new file mode 100644
index 00000000000..5351c3fb3a7
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java
@@ -0,0 +1,59 @@
+package com.yahoo.vespa.model.admin.monitoring;
+
+import java.util.Optional;
+
+/**
+ * Helper object for CloudWatch configuration.
+ *
+ * @author gjoranv
+ */
+public class CloudWatch {
+ private final String region;
+ private final String namespace;
+ private final MetricsConsumer consumer;
+
+ private HostedAuth hostedAuth;
+ private SharedCredentials sharedCredentials;
+
+ public CloudWatch(String region, String namespace, MetricsConsumer consumer) {
+ this.region = region;
+ this.namespace = namespace;
+ this.consumer = consumer;
+ }
+
+ public String region() { return region; }
+ public String namespace() { return namespace; }
+ public String consumer() { return consumer.getId(); }
+
+ public Optional<HostedAuth> hostedAuth() {return Optional.ofNullable(hostedAuth); }
+ public Optional<SharedCredentials> sharedCredentials() {return Optional.ofNullable(sharedCredentials); }
+
+ public void setHostedAuth(String accessKeyName, String secretKeyName) {
+ hostedAuth = new HostedAuth(accessKeyName, secretKeyName);
+ }
+
+ public void setSharedCredentials(String file, Optional<String> profile) {
+ sharedCredentials = new SharedCredentials(file, profile);
+ }
+
+ public static class HostedAuth {
+ public final String accessKeyName;
+ public final String secretKeyName;
+
+ HostedAuth(String accessKeyName, String secretKeyName) {
+ this.accessKeyName = accessKeyName;
+ this.secretKeyName = secretKeyName;
+ }
+ }
+
+ public static class SharedCredentials {
+ public final String file;
+ public final Optional<String> profile;
+
+ SharedCredentials(String file, Optional<String> profile) {
+ this.file = file;
+ this.profile = profile;
+ }
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicMetrics.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicMetrics.java
index 19a62f47e39..c80cebe3d5b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicMetrics.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicMetrics.java
@@ -20,10 +20,12 @@ import static java.util.Collections.singleton;
*/
public class DefaultPublicMetrics {
+ public static final String DEFAULT_METRIC_SET_ID = "default";
+
public static MetricSet defaultPublicMetricSet = createMetricSet();
private static MetricSet createMetricSet() {
- return new MetricSet("public",
+ return new MetricSet(DEFAULT_METRIC_SET_ID,
getAllMetrics(),
singleton(defaultVespaMetricSet));
}
@@ -84,6 +86,7 @@ public class DefaultPublicMetrics {
metrics.add(new Metric("content.proton.documentdb.matching.docs_matched.rate"));
metrics.add(new Metric("content.proton.documentdb.matching.docs_reranked.rate"));
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_setup_time.average"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_latency.average"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.rerank_time.average"));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/MetricsConsumer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/MetricsConsumer.java
index 529ed6ecf67..a8fbcf50b02 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/MetricsConsumer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/MetricsConsumer.java
@@ -2,9 +2,13 @@
package com.yahoo.vespa.model.admin.monitoring;
import javax.annotation.concurrent.Immutable;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
+import static java.util.Collections.unmodifiableList;
+
/**
* Represents an arbitrary metric consumer
*
@@ -13,12 +17,15 @@ import java.util.Objects;
*/
@Immutable
public class MetricsConsumer {
+
private final String id;
private final MetricSet metricSet;
+ private final List<CloudWatch> cloudWatches = new ArrayList<>();
+
/**
- * @param id The consumer
- * @param metricSet The metrics for this consumer
+ * @param id the consumer
+ * @param metricSet the metrics for this consumer
*/
public MetricsConsumer(String id, MetricSet metricSet) {
this.id = Objects.requireNonNull(id, "A consumer must have a non-null id.");;
@@ -38,4 +45,12 @@ public class MetricsConsumer {
return metricSet.getMetrics();
}
+ public void addCloudWatch(CloudWatch cloudWatch) {
+ cloudWatches.add(cloudWatch);
+ }
+
+ public List<CloudWatch> cloudWatches() {
+ return unmodifiableList(cloudWatches);
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/SystemMetrics.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/SystemMetrics.java
index 58b77ee1297..c05cad89852 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/SystemMetrics.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/SystemMetrics.java
@@ -9,6 +9,7 @@ import java.util.Set;
* @author gjoranv
*/
public class SystemMetrics {
+
public static final String CPU_UTIL = "cpu.util";
public static final String CPU_SYS_UTIL = "cpu.sys.util";
public static final String CPU_THROTTLED_TIME = "cpu.throttled_time.rate";
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
index 57d938f8a71..141794256b8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
@@ -72,6 +72,9 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.server.network.tls-connections-broken"));
metrics.add(new Metric("vds.server.network.failed-tls-config-reloads"));
+ // C++ Fnet metrics
+ metrics.add(new Metric("vds.server.fnet.num-connections"));
+
return metrics;
}
@@ -126,6 +129,8 @@ public class VespaMetricSet {
metrics.add(new Metric("serverActiveThreads.count"));
metrics.add(new Metric("serverActiveThreads.last"));
+ metrics.add(new Metric("jdisc.thread_pool.unhandled_exceptions.rate"));
+
metrics.add(new Metric("httpapi_latency.max"));
metrics.add(new Metric("httpapi_latency.sum"));
metrics.add(new Metric("httpapi_latency.count"));
@@ -179,6 +184,15 @@ public class VespaMetricSet {
metrics.add(new Metric("jdisc.http.request.content_size.sum"));
metrics.add(new Metric("jdisc.http.request.content_size.count"));
metrics.add(new Metric("jdisc.http.request.content_size.average")); // TODO: Remove in Vespa 8
+
+ metrics.add(new Metric("jdisc.http.ssl.handshake.failure.missing_client_cert.rate"));
+ metrics.add(new Metric("jdisc.http.ssl.handshake.failure.expired_client_cert.rate"));
+ metrics.add(new Metric("jdisc.http.ssl.handshake.failure.invalid_client_cert.rate"));
+ metrics.add(new Metric("jdisc.http.ssl.handshake.failure.incompatible_protocols.rate"));
+ metrics.add(new Metric("jdisc.http.ssl.handshake.failure.incompatible_ciphers.rate"));
+ metrics.add(new Metric("jdisc.http.ssl.handshake.failure.unknown.rate"));
+
+ metrics.add(new Metric("jdisc.http.handler.unhandled_exceptions.rate"));
return metrics;
}
@@ -288,6 +302,14 @@ public class VespaMetricSet {
return metrics;
}
+ private static void addSearchNodeExecutorMetrics(Set<Metric> metrics, String prefix) {
+ metrics.add(new Metric(prefix + ".queuesize.max"));
+ metrics.add(new Metric(prefix + ".queuesize.sum"));
+ metrics.add(new Metric(prefix + ".queuesize.count"));
+ metrics.add(new Metric(prefix + ".maxpending.last")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric(prefix + ".accepted.rate"));
+ }
+
private static Set<Metric> getSearchNodeMetrics() {
Set<Metric> metrics = new LinkedHashSet<>();
@@ -332,18 +354,12 @@ public class VespaMetricSet {
metrics.add(new Metric("content.proton.search_protocol.docsum.requested_documents.count"));
// Executors shared between all document dbs
- metrics.add(new Metric("content.proton.executor.proton.maxpending.last"));
- metrics.add(new Metric("content.proton.executor.proton.accepted.rate"));
- metrics.add(new Metric("content.proton.executor.flush.maxpending.last"));
- metrics.add(new Metric("content.proton.executor.flush.accepted.rate"));
- metrics.add(new Metric("content.proton.executor.match.maxpending.last"));
- metrics.add(new Metric("content.proton.executor.match.accepted.rate"));
- metrics.add(new Metric("content.proton.executor.docsum.maxpending.last"));
- metrics.add(new Metric("content.proton.executor.docsum.accepted.rate"));
- metrics.add(new Metric("content.proton.executor.shared.maxpending.last"));
- metrics.add(new Metric("content.proton.executor.shared.accepted.rate"));
- metrics.add(new Metric("content.proton.executor.warmup.maxpending.last"));
- metrics.add(new Metric("content.proton.executor.warmup.accepted.rate"));
+ addSearchNodeExecutorMetrics(metrics, "content.proton.executor.proton");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.executor.flush");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.executor.match");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.executor.docsum");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.executor.shared");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.executor.warmup");
// jobs
metrics.add(new Metric("content.proton.documentdb.job.total.average"));
@@ -357,18 +373,12 @@ public class VespaMetricSet {
metrics.add(new Metric("content.proton.documentdb.job.removed_documents_prune.average"));
// Threading service (per document db)
- metrics.add(new Metric("content.proton.documentdb.threading_service.master.maxpending.last"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.master.accepted.rate"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.index.maxpending.last"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.index.accepted.rate"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.summary.maxpending.last"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.summary.accepted.rate"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.index_field_inverter.maxpending.last"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.index_field_inverter.accepted.rate"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.index_field_writer.maxpending.last"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.index_field_writer.accepted.rate"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.attribute_field_writer.maxpending.last"));
- metrics.add(new Metric("content.proton.documentdb.threading_service.attribute_field_writer.accepted.rate"));
+ addSearchNodeExecutorMetrics(metrics, "content.proton.documentdb.threading_service.master");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.documentdb.threading_service.index");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.documentdb.threading_service.summary");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.documentdb.threading_service.index_field_inverter");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.documentdb.threading_service.index_field_writer");
+ addSearchNodeExecutorMetrics(metrics, "content.proton.documentdb.threading_service.attribute_field_writer");
// lid space
metrics.add(new Metric("content.proton.documentdb.ready.lid_space.lid_bloat_factor.average"));
@@ -453,10 +463,13 @@ public class VespaMetricSet {
metrics.add(new Metric("content.proton.documentdb.matching.query_latency.sum"));
metrics.add(new Metric("content.proton.documentdb.matching.query_latency.count"));
metrics.add(new Metric("content.proton.documentdb.matching.query_latency.average")); // TODO: Remove in Vespa 8
- metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.max"));
- metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.sum"));
- metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.count"));
+ metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.max")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.sum")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.count")); // TODO: Remove in Vespa 8
metrics.add(new Metric("content.proton.documentdb.matching.query_collateral_time.average")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("content.proton.documentdb.matching.query_setup_time.max"));
+ metrics.add(new Metric("content.proton.documentdb.matching.query_setup_time.sum"));
+ metrics.add(new Metric("content.proton.documentdb.matching.query_setup_time.count"));
metrics.add(new Metric("content.proton.documentdb.matching.docs_matched.rate"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.queries.rate"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.soft_doomed_queries.rate"));
@@ -468,10 +481,13 @@ public class VespaMetricSet {
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_latency.sum"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_latency.count"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_latency.average")); // TODO: Remove in Vespa 8
- metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.max"));
- metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.sum"));
- metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.count"));
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.max")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.sum")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.count")); // TODO: Remove in Vespa 8
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_collateral_time.average")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_setup_time.max"));
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_setup_time.sum"));
+ metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.query_setup_time.count"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.rerank_time.max"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.rerank_time.sum"));
metrics.add(new Metric("content.proton.documentdb.matching.rank_profile.rerank_time.count"));
@@ -513,13 +529,24 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.filestor.alldisks.averagequeuewait.sum.sum"));
metrics.add(new Metric("vds.filestor.alldisks.averagequeuewait.sum.count"));
metrics.add(new Metric("vds.filestor.alldisks.averagequeuewait.sum.average")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergemetadatareadlatency.max"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergemetadatareadlatency.sum"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergemetadatareadlatency.count"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergedatareadlatency.max"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergedatareadlatency.sum"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergedatareadlatency.count"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergedatawritelatency.max"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergedatawritelatency.sum"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.mergedatawritelatency.count"));
metrics.add(new Metric("vds.visitor.allthreads.queuesize.count.max"));
metrics.add(new Metric("vds.visitor.allthreads.queuesize.count.sum"));
metrics.add(new Metric("vds.visitor.allthreads.queuesize.count.count"));
metrics.add(new Metric("vds.visitor.allthreads.queuesize.count.average")); // TODO: Remove in Vespa 8
- metrics.add(new Metric("vds.visitor.allthreads.completed.sum.average"));
+ metrics.add(new Metric("vds.visitor.allthreads.completed.sum.average")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("vds.visitor.allthreads.completed.sum.rate"));
metrics.add(new Metric("vds.visitor.allthreads.created.sum.rate"));
+ metrics.add(new Metric("vds.visitor.allthreads.failed.sum.rate"));
metrics.add(new Metric("vds.visitor.allthreads.averagemessagesendtime.sum.max"));
metrics.add(new Metric("vds.visitor.allthreads.averagemessagesendtime.sum.sum"));
metrics.add(new Metric("vds.visitor.allthreads.averagemessagesendtime.sum.count"));
@@ -528,19 +555,27 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.visitor.allthreads.averageprocessingtime.sum.sum"));
metrics.add(new Metric("vds.visitor.allthreads.averageprocessingtime.sum.count"));
metrics.add(new Metric("vds.visitor.allthreads.averageprocessingtime.sum.average")); // TODO: Remove in Vespa 8
-
+
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.failed.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.latency.max"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.latency.sum"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.latency.count"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.put.sum.latency.average")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.failed.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.latency.max"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.latency.sum"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.latency.count"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.remove.sum.latency.average")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.failed.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.latency.max"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.latency.sum"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.latency.count"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.get.sum.latency.average")); // TODO: Remove in Vespa 8
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.failed.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.latency.max"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.latency.sum"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.update.sum.latency.count"));
@@ -560,13 +595,13 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.filestor.alldisks.allthreads.splitbuckets.count.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.joinbuckets.count.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.count.rate"));
+ metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.failed.rate"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.latency.max"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.latency.sum"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.latency.count"));
metrics.add(new Metric("vds.filestor.alldisks.allthreads.deletebuckets.latency.average")); // TODO: Remove in Vespa 8
metrics.add(new Metric("vds.filestor.alldisks.allthreads.setbucketstates.count.rate"));
-
//Distributor
metrics.add(new Metric("vds.idealstate.buckets_rechecking.average"));
metrics.add(new Metric("vds.idealstate.idealstate_diff.average"));
@@ -589,6 +624,8 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.idealstate.garbage_collection.done_ok.rate"));
metrics.add(new Metric("vds.idealstate.garbage_collection.done_failed.rate"));
metrics.add(new Metric("vds.idealstate.garbage_collection.pending.average"));
+ metrics.add(new Metric("vds.idealstate.garbage_collection.documents_removed.count"));
+ metrics.add(new Metric("vds.idealstate.garbage_collection.documents_removed.rate"));
metrics.add(new Metric("vds.distributor.puts.sum.latency.max"));
metrics.add(new Metric("vds.distributor.puts.sum.latency.sum"));
@@ -596,18 +633,24 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.distributor.puts.sum.latency.average")); // TODO: Remove in Vespa 8
metrics.add(new Metric("vds.distributor.puts.sum.ok.rate"));
metrics.add(new Metric("vds.distributor.puts.sum.failures.total.rate"));
+ metrics.add(new Metric("vds.distributor.puts.sum.failures.notfound.rate"));
+ metrics.add(new Metric("vds.distributor.puts.sum.failures.test_and_set_failed.rate"));
metrics.add(new Metric("vds.distributor.removes.sum.latency.max"));
metrics.add(new Metric("vds.distributor.removes.sum.latency.sum"));
metrics.add(new Metric("vds.distributor.removes.sum.latency.count"));
metrics.add(new Metric("vds.distributor.removes.sum.latency.average")); // TODO: Remove in Vespa 8
metrics.add(new Metric("vds.distributor.removes.sum.ok.rate"));
metrics.add(new Metric("vds.distributor.removes.sum.failures.total.rate"));
+ metrics.add(new Metric("vds.distributor.removes.sum.failures.notfound.rate"));
+ metrics.add(new Metric("vds.distributor.removes.sum.failures.test_and_set_failed.rate"));
metrics.add(new Metric("vds.distributor.updates.sum.latency.max"));
metrics.add(new Metric("vds.distributor.updates.sum.latency.sum"));
metrics.add(new Metric("vds.distributor.updates.sum.latency.count"));
metrics.add(new Metric("vds.distributor.updates.sum.latency.average")); // TODO: Remove in Vespa 8
metrics.add(new Metric("vds.distributor.updates.sum.ok.rate"));
metrics.add(new Metric("vds.distributor.updates.sum.failures.total.rate"));
+ metrics.add(new Metric("vds.distributor.updates.sum.failures.notfound.rate"));
+ metrics.add(new Metric("vds.distributor.updates.sum.failures.test_and_set_failed.rate"));
metrics.add(new Metric("vds.distributor.updates.sum.diverging_timestamp_updates.rate"));
metrics.add(new Metric("vds.distributor.removelocations.sum.ok.rate"));
metrics.add(new Metric("vds.distributor.removelocations.sum.failures.total.rate"));
@@ -617,6 +660,7 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.distributor.gets.sum.latency.average")); // TODO: Remove in Vespa 8
metrics.add(new Metric("vds.distributor.gets.sum.ok.rate"));
metrics.add(new Metric("vds.distributor.gets.sum.failures.total.rate"));
+ metrics.add(new Metric("vds.distributor.gets.sum.failures.notfound.rate"));
metrics.add(new Metric("vds.distributor.visitor.sum.latency.max"));
metrics.add(new Metric("vds.distributor.visitor.sum.latency.sum"));
metrics.add(new Metric("vds.distributor.visitor.sum.latency.count"));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/Metrics.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/Metrics.java
index 9b0d9dbfadc..1f81f16a80b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/Metrics.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/Metrics.java
@@ -28,4 +28,13 @@ public class Metrics {
return consumers.keySet().stream()
.anyMatch(existing -> existing.equalsIgnoreCase(id));
}
+
+ /**
+ * Returns true if any of the consumers have specified external metric systems.
+ */
+ public boolean usesExternalMetricSystems() {
+ return consumers.values().stream()
+ .anyMatch(consumer -> ! consumer.cloudWatches().isEmpty());
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java
new file mode 100644
index 00000000000..314ef9cc5a5
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java
@@ -0,0 +1,40 @@
+package com.yahoo.vespa.model.admin.monitoring.builder.xml;
+
+import com.yahoo.vespa.model.admin.monitoring.CloudWatch;
+import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;
+import org.w3c.dom.Element;
+
+import static com.yahoo.config.model.builder.xml.XmlHelper.getOptionalAttribute;
+import static com.yahoo.config.model.builder.xml.XmlHelper.getOptionalChild;
+
+/**
+ * @author gjoranv
+ */
+public class CloudWatchBuilder {
+
+ private static final String REGION_ATTRIBUTE = "region";
+ private static final String NAMESPACE_ATTRIBUTE = "namespace";
+ private static final String CREDENTIALS_ELEMENT = "credentials";
+ private static final String ACCESS_KEY_ATTRIBUTE = "access-key-name";
+ private static final String SECRET_KEY_ATTRIBUTE = "secret-key-name";
+ private static final String SHARED_CREDENTIALS_ELEMENT = "shared-credentials";
+ private static final String PROFILE_ATTRIBUTE = "profile";
+ private static final String FILE_ATTRIBUTE = "file";
+
+ public static CloudWatch buildCloudWatch(Element cloudwatchElement, MetricsConsumer consumer) {
+ CloudWatch cloudWatch = new CloudWatch(cloudwatchElement.getAttribute(REGION_ATTRIBUTE),
+ cloudwatchElement.getAttribute(NAMESPACE_ATTRIBUTE),
+ consumer);
+
+ getOptionalChild(cloudwatchElement, CREDENTIALS_ELEMENT)
+ .ifPresent(elem -> cloudWatch.setHostedAuth(elem.getAttribute(ACCESS_KEY_ATTRIBUTE),
+ elem.getAttribute(SECRET_KEY_ATTRIBUTE)));
+
+ getOptionalChild(cloudwatchElement, SHARED_CREDENTIALS_ELEMENT)
+ .ifPresent(elem -> cloudWatch.setSharedCredentials(elem.getAttribute(FILE_ATTRIBUTE),
+ getOptionalAttribute(elem, PROFILE_ATTRIBUTE)));
+
+ return cloudWatch;
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/MetricsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/MetricsBuilder.java
index b13fa4917e4..b686288868f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/MetricsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/MetricsBuilder.java
@@ -42,7 +42,11 @@ public class MetricsBuilder {
throwIfIllegalConsumerId(metrics, consumerId);
MetricSet metricSet = buildMetricSet(consumerId, consumerElement);
- metrics.addConsumer(new MetricsConsumer(consumerId, metricSet));
+ var consumer = new MetricsConsumer(consumerId, metricSet);
+ for (Element cloudwatchElement : XML.getChildren(consumerElement, "cloudwatch")) {
+ consumer.addCloudWatch(CloudWatchBuilder.buildCloudWatch(cloudwatchElement, consumer));
+ }
+ metrics.addConsumer(consumer);
}
return metrics;
}
@@ -58,11 +62,11 @@ public class MetricsBuilder {
private MetricSet buildMetricSet(String consumerId, Element consumerElement) {
List<Metric> metrics = XML.getChildren(consumerElement, "metric").stream()
- .map(metricElement -> metricFromElement(metricElement))
+ .map(MetricsBuilder::metricFromElement)
.collect(Collectors.toCollection(LinkedList::new));
List<MetricSet> metricSets = XML.getChildren(consumerElement, "metric-set").stream()
- .map(metricSetElement -> availableMetricSets.get(metricSetElement.getAttribute(ID_ATTRIBUTE)))
+ .map(metricSetElement -> getMetricSetOrThrow(metricSetElement.getAttribute(ID_ATTRIBUTE)))
.collect(Collectors.toCollection(LinkedList::new));
metricSets.add(defaultVespaMetricSet);
@@ -75,6 +79,11 @@ public class MetricsBuilder {
return "user-metrics-" + consumerName;
}
+ private MetricSet getMetricSetOrThrow(String id) {
+ if (! availableMetricSets.containsKey(id)) throw new IllegalArgumentException("No such metric-set: " + id);
+ return availableMetricSets.get(id);
+ }
+
private void throwIfIllegalConsumerId(Metrics metrics, String consumerId) {
if (consumerId.equalsIgnoreCase(VESPA_CONSUMER_ID) && applicationType != ApplicationType.HOSTED_INFRASTRUCTURE)
throw new IllegalArgumentException("'Vespa' is not allowed as metrics consumer id (case is ignored.)");
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/AwsAccessControlValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/AwsAccessControlValidator.java
new file mode 100644
index 00000000000..631ab0e2640
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/AwsAccessControlValidator.java
@@ -0,0 +1,48 @@
+// Copyright Verizon Media. 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.application.api.ValidationId;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.provision.CloudName;
+import com.yahoo.vespa.model.VespaModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.yahoo.collections.CollectionUtil.mkString;
+import static com.yahoo.vespa.model.application.validation.first.AccessControlOnFirstDeploymentValidator.needsAccessControlValidation;
+import static com.yahoo.vespa.model.container.http.AccessControl.hasHandlerThatNeedsProtection;
+
+/**
+ * @author gjoranv
+ */
+public class AwsAccessControlValidator extends Validator {
+
+ // NOTE: must be the same as the name in the declaration of the AWS cloud in hosted.
+ static final String AWS_CLOUD_NAME = "aws";
+
+ @Override
+ public void validate(VespaModel model, DeployState deployState) {
+
+ if (! needsAccessControlValidation(model, deployState)) return;
+ if(! deployState.zone().cloud().equals(CloudName.from(AWS_CLOUD_NAME))) return;
+
+ List<String> offendingClusters = new ArrayList<>();
+ for (var cluster : model.getContainerClusters().values()) {
+ var http = cluster.getHttp();
+ if (http == null
+ || ! http.getAccessControl().isPresent()
+ || ! http.getAccessControl().get().writeEnabled
+ || ! http.getAccessControl().get().readEnabled)
+
+ if (hasHandlerThatNeedsProtection(cluster) || ! cluster.getAllServlets().isEmpty())
+ offendingClusters.add(cluster.getName());
+ }
+ if (! offendingClusters.isEmpty())
+ deployState.validationOverrides()
+ .invalid(ValidationId.accessControl,
+ "Access-control must be enabled for read/write operations to container clusters in AWS production zones: " +
+ mkString(offendingClusters, "[", ", ", "]"), deployState.now());
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java
new file mode 100644
index 00000000000..462ac39fa84
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java
@@ -0,0 +1,37 @@
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.config.model.ConfigModelContext;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * @author gjoranv
+ */
+public class CloudWatchValidator extends Validator {
+
+ @Override
+ public void validate(VespaModel model, DeployState deployState) {
+ if (!deployState.isHosted()) return;
+ if (deployState.zone().system().isPublic()) return;
+ if (model.getAdmin().getApplicationType() != ConfigModelContext.ApplicationType.DEFAULT) return;
+
+ var offendingConsumers = model.getAdmin().getUserMetrics().getConsumers().values().stream()
+ .filter(consumer -> !consumer.cloudWatches().isEmpty())
+ .collect(toList());
+
+ if (! offendingConsumers.isEmpty()) {
+ throw new IllegalArgumentException("CloudWatch cannot be set up for non-public hosted Vespa and must " +
+ "be removed for consumers: " + consumerIds(offendingConsumers));
+ }
+ }
+
+ private List<String> consumerIds(List<MetricsConsumer> offendingConsumers) {
+ return offendingConsumers.stream().map(MetricsConsumer::getId).collect(toList());
+ }
+
+}
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/ComplexAttributeFieldsValidator.java
index f9762ce58fa..43c1a88b0a1 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/ComplexAttributeFieldsValidator.java
@@ -33,7 +33,7 @@ public class ComplexAttributeFieldsValidator extends Validator {
continue;
}
SearchCluster searchCluster = (SearchCluster) cluster;
- for (AbstractSearchCluster.SearchDefinitionSpec spec : searchCluster.getLocalSDS()) {
+ for (AbstractSearchCluster.SchemaSpec spec : searchCluster.getLocalSDS()) {
validateComplexFields(searchCluster.getClusterName(), spec.getSearchDefinition().getSearch());
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/DeploymentSpecValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/DeploymentSpecValidator.java
index ac38336a405..d5bea2a2959 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/DeploymentSpecValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/DeploymentSpecValidator.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.model.application.validation;
import com.yahoo.config.application.api.DeploymentInstanceSpec;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.provision.InstanceName;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.container.ContainerModel;
@@ -12,8 +13,7 @@ import java.util.List;
import java.util.Optional;
/**
- * Validates that deployment spec (deployment.xml) has valid values (for now
- * only global-service-id is validated)
+ * Validate deployment spec (deployment.xml).
*
* @author hmusum
* @author bratseth
@@ -30,11 +30,20 @@ public class DeploymentSpecValidator extends Validator {
List<ContainerModel> containers = model.getRoot().configModelRepo().getModels(ContainerModel.class);
for (DeploymentInstanceSpec instance : deploymentSpec.instances()) {
instance.globalServiceId().ifPresent(globalServiceId -> {
- if ( containers.stream().noneMatch(container -> container.getCluster().getName().equals(globalServiceId)))
- throw new IllegalArgumentException("The global-service-id in " + instance + ", '" + globalServiceId +
- "' specified in deployment.xml does not match any container cluster id");
+ requireClusterId(containers, instance.name(), "Attribute 'globalServiceId'", globalServiceId);
+ });
+ instance.endpoints().forEach(endpoint -> {
+ requireClusterId(containers, instance.name(), "Endpoint '" + endpoint.endpointId() + "'",
+ endpoint.containerId());
});
}
}
+ private static void requireClusterId(List<ContainerModel> containers, InstanceName instanceName, String context,
+ String id) {
+ if (containers.stream().noneMatch(container -> container.getCluster().getName().equals(id)))
+ throw new IllegalArgumentException(context + " in instance " + instanceName + ": '" + id +
+ "' specified in deployment.xml does not match any container cluster ID");
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java
index f00ad0f0dbb..4b8bbd4ff08 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java
@@ -12,7 +12,7 @@ public class EndpointCertificateSecretsValidator extends Validator {
@Override
public void validate(VespaModel model, DeployState deployState) {
if (deployState.endpointCertificateSecrets().isPresent() && deployState.endpointCertificateSecrets().get() == EndpointCertificateSecrets.MISSING) {
- throw new CertificateNotReadyException("TLS enabled, but could not retrieve certificate yet");
+ throw new CertificateNotReadyException("TLS enabled, but could not yet retrieve certificate for application " + deployState.getProperties().applicationId().serializedForm());
}
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java
index 7f8ff6edd85..b6f7ab4ff62 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java
@@ -33,24 +33,29 @@ import java.util.logging.Logger;
/**
* Validate rank setup for all search clusters (rank-profiles, index-schema, attributes configs), validating done
- * by running through the binary 'vespa-verify-ranksetup'
+ * by running the binary 'vespa-verify-ranksetup-bin'
*
* @author vegardh
*/
public class RankSetupValidator extends Validator {
private static final Logger log = Logger.getLogger(RankSetupValidator.class.getName());
- private final boolean force;
+ private static final String binaryName = "vespa-verify-ranksetup-bin ";
- public RankSetupValidator(boolean force) {
- this.force = force;
+ private final boolean ignoreValidationErrors;
+
+ public RankSetupValidator(boolean ignoreValidationErrors) {
+ this.ignoreValidationErrors = ignoreValidationErrors;
}
@Override
public void validate(VespaModel model, DeployState deployState) {
File cfgDir = null;
try {
- cfgDir = Files.createTempDirectory("deploy_ranksetup").toFile();
+ cfgDir = Files.createTempDirectory("verify-ranksetup." +
+ deployState.getProperties().applicationId().toFullString() +
+ ".")
+ .toFile();
for (AbstractSearchCluster cluster : model.getSearchClusters()) {
// Skipping rank expression checking for streaming clusters, not implemented yet
@@ -100,29 +105,29 @@ public class RankSetupValidator extends Validator {
IOUtils.recursiveDeleteDir(dir);
}
- private void writeConfigs(String dir, AbstractConfigProducer producer) throws IOException {
+ private void writeConfigs(String dir, AbstractConfigProducer<?> producer) throws IOException {
RankProfilesConfig.Builder rpcb = new RankProfilesConfig.Builder();
- RankProfilesConfig.Producer.class.cast(producer).getConfig(rpcb);
+ ((RankProfilesConfig.Producer) producer).getConfig(rpcb);
RankProfilesConfig rpc = new RankProfilesConfig(rpcb);
writeConfig(dir, RankProfilesConfig.getDefName() + ".cfg", rpc);
IndexschemaConfig.Builder iscb = new IndexschemaConfig.Builder();
- IndexschemaConfig.Producer.class.cast(producer).getConfig(iscb);
+ ((IndexschemaConfig.Producer) producer).getConfig(iscb);
IndexschemaConfig isc = new IndexschemaConfig(iscb);
writeConfig(dir, IndexschemaConfig.getDefName() + ".cfg", isc);
AttributesConfig.Builder acb = new AttributesConfig.Builder();
- AttributesConfig.Producer.class.cast(producer).getConfig(acb);
+ ((AttributesConfig.Producer) producer).getConfig(acb);
AttributesConfig ac = new AttributesConfig(acb);
writeConfig(dir, AttributesConfig.getDefName() + ".cfg", ac);
RankingConstantsConfig.Builder rccb = new RankingConstantsConfig.Builder();
- RankingConstantsConfig.Producer.class.cast(producer).getConfig(rccb);
+ ((RankingConstantsConfig.Producer) producer).getConfig(rccb);
RankingConstantsConfig rcc = new RankingConstantsConfig(rccb);
writeConfig(dir, RankingConstantsConfig.getDefName() + ".cfg", rcc);
ImportedFieldsConfig.Builder ifcb = new ImportedFieldsConfig.Builder();
- ImportedFieldsConfig.Producer.class.cast(producer).getConfig(ifcb);
+ ((ImportedFieldsConfig.Producer) producer).getConfig(ifcb);
ImportedFieldsConfig ifc = new ImportedFieldsConfig(ifcb);
writeConfig(dir, ImportedFieldsConfig.getDefName() + ".cfg", ifc);
}
@@ -132,8 +137,8 @@ public class RankSetupValidator extends Validator {
}
private boolean execValidate(String configId, SearchCluster sc, String sdName, DeployLogger deployLogger) {
- String job = "vespa-verify-ranksetup-bin " + configId;
- ProcessExecuter executer = new ProcessExecuter();
+ String job = String.format("%s %s", binaryName, configId);
+ ProcessExecuter executer = new ProcessExecuter(true);
try {
Pair<Integer, String> ret = executer.exec(job);
if (ret.getFirst() != 0) {
@@ -147,27 +152,28 @@ public class RankSetupValidator extends Validator {
}
private void validateWarn(Exception e, DeployLogger deployLogger) {
- String msg = "Unable to execute 'vespa-verify-ranksetup', validation of rank expressions will only take place when you start Vespa: " +
+ String msg = "Unable to execute '"+ binaryName + "', validation of rank expressions will only take place when you start Vespa: " +
Exceptions.toMessageString(e);
deployLogger.log(LogLevel.WARNING, msg);
}
private void validateFail(String output, SearchCluster sc, String sdName, DeployLogger deployLogger) {
- String errMsg = "For search cluster '" + sc.getClusterName() + "', search definition '" + sdName + "': error in rank setup. Details:\n";
+ StringBuilder errMsg = new StringBuilder("For search cluster '").append(sc.getClusterName()).append("', ")
+ .append("search definition '").append(sdName).append("': error in rank setup. Details:\n");
for (String line : output.split("\n")) {
// Remove debug lines from start script
if (line.startsWith("debug\t")) continue;
try {
- LogMessage logmsg = LogMessage.parseNativeFormat(line);
- errMsg = errMsg + logmsg.getLevel() + ": " + logmsg.getPayload() + "\n";
+ LogMessage logMessage = LogMessage.parseNativeFormat(line);
+ errMsg.append(logMessage.getLevel()).append(": ").append(logMessage.getPayload()).append("\n");
} catch (InvalidLogFormatException e) {
- errMsg = errMsg + line + "\n";
+ errMsg.append(line).append("\n");
}
}
- if (force) {
- deployLogger.log(LogLevel.WARNING, errMsg + "(Continuing because of force.)");
+ if (ignoreValidationErrors) {
+ deployLogger.log(LogLevel.WARNING, errMsg.append("(Continuing since ignoreValidationErrors flag is set.)").toString());
} else {
- throw new IllegalArgumentException(errMsg);
+ throw new IllegalArgumentException(errMsg.toString());
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankingConstantsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankingConstantsValidator.java
index 907418ea9f0..9568ea5c27c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankingConstantsValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankingConstantsValidator.java
@@ -9,7 +9,7 @@ import com.yahoo.path.Path;
import com.yahoo.searchdefinition.RankingConstant;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.application.validation.ConstantTensorJsonValidator.InvalidConstantTensor;
-import com.yahoo.vespa.model.search.SearchDefinition;
+import com.yahoo.vespa.model.search.NamedSchema;
import java.io.FileNotFoundException;
@@ -47,7 +47,7 @@ public class RankingConstantsValidator extends Validator {
ApplicationPackage applicationPackage = deployState.getApplicationPackage();
ExceptionMessageCollector exceptionMessageCollector = new ExceptionMessageCollector("Invalid constant tensor file(s):");
- for (SearchDefinition sd : deployState.getSearchDefinitions()) {
+ for (NamedSchema sd : deployState.getSchemas()) {
for (RankingConstant rc : sd.getSearch().rankingConstants().asMap().values()) {
try {
validateRankingConstant(rc, applicationPackage);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java
index 85ba75639eb..031ce0dbdd4 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/SearchDataTypeValidator.java
@@ -15,7 +15,7 @@ import com.yahoo.searchdefinition.document.SDDocumentType;
import com.yahoo.searchdefinition.document.SDField;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.search.AbstractSearchCluster;
-import com.yahoo.vespa.model.search.SearchDefinition;
+import com.yahoo.vespa.model.search.NamedSchema;
import java.util.List;
@@ -34,7 +34,7 @@ public class SearchDataTypeValidator extends Validator {
if (cluster.isStreaming()) {
continue;
}
- for (AbstractSearchCluster.SearchDefinitionSpec spec : cluster.getLocalSDS()) {
+ for (AbstractSearchCluster.SchemaSpec spec : cluster.getLocalSDS()) {
SDDocumentType docType = spec.getSearchDefinition().getSearch().getDocument();
if (docType == null) {
continue;
@@ -44,7 +44,7 @@ public class SearchDataTypeValidator extends Validator {
}
}
- private void validateDocument(AbstractSearchCluster cluster, SearchDefinition def, SDDocumentType doc) {
+ private void validateDocument(AbstractSearchCluster cluster, NamedSchema def, SDDocumentType doc) {
for (SDDocumentType child : doc.getTypes()) {
validateDocument(cluster, def, child);
}
@@ -84,7 +84,7 @@ public class SearchDataTypeValidator extends Validator {
}
}
- private void disallowIndexingOfMaps(AbstractSearchCluster cluster, SearchDefinition def, Field field) {
+ private void disallowIndexingOfMaps(AbstractSearchCluster cluster, NamedSchema def, Field field) {
DataType fieldType = field.getDataType();
if ((fieldType instanceof MapDataType) && (((SDField) field).doesIndexing())) {
throw new IllegalArgumentException("Field type '" + fieldType.getName() + "' cannot be indexed for search " +
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 1e4a45428b8..22dd0289390 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
@@ -59,6 +59,8 @@ public class Validation {
new SecretStoreValidator().validate(model, deployState);
new EndpointCertificateSecretsValidator().validate(model, deployState);
new AccessControlFilterValidator().validate(model, deployState);
+ new CloudWatchValidator().validate(model, deployState);
+ new AwsAccessControlValidator().validate(model, deployState);
List<ConfigChangeAction> result = Collections.emptyList();
if (deployState.getProperties().isFirstTimeDeployment()) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidator.java
index d14fe91a53b..162f6798462 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ClusterSizeReductionValidator.java
@@ -2,6 +2,8 @@
package com.yahoo.vespa.model.application.validation.change;
import com.yahoo.config.model.api.ConfigChangeAction;
+import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
@@ -21,35 +23,32 @@ public class ClusterSizeReductionValidator implements ChangeValidator {
@Override
public List<ConfigChangeAction> validate(VespaModel current, VespaModel next, ValidationOverrides overrides, Instant now) {
- for (ContainerCluster currentCluster : current.getContainerClusters().values()) {
- ContainerCluster nextCluster = next.getContainerClusters().get(currentCluster.getName());
- if (nextCluster == null) continue;
- validate(currentCluster.getContainers().size(),
- nextCluster.getContainers().size(),
- currentCluster.getName(),
+ for (var clusterId : current.allClusters()) {
+ Capacity currentCapacity = current.provisioned().all().get(clusterId);
+ Capacity nextCapacity = next.provisioned().all().get(clusterId);
+ if (currentCapacity == null || nextCapacity == null) continue;
+ validate(currentCapacity,
+ nextCapacity,
+ clusterId,
overrides,
now);
}
-
- for (ContentCluster currentCluster : current.getContentClusters().values()) {
- ContentCluster nextCluster = next.getContentClusters().get(currentCluster.getName());
- if (nextCluster == null) continue;
- validate(currentCluster.getSearch().getSearchNodes().size(),
- nextCluster.getSearch().getSearchNodes().size(),
- currentCluster.getName(),
- overrides,
- now);
- }
-
return Collections.emptyList();
}
- private void validate(int currentSize, int nextSize, String clusterName, ValidationOverrides overrides, Instant now) {
+ private void validate(Capacity current,
+ Capacity next,
+ ClusterSpec.Id clusterId,
+ ValidationOverrides overrides,
+ Instant now) {
+ int currentSize = current.minResources().nodes();
+ int nextSize = next.minResources().nodes();
// don't allow more than 50% reduction, but always allow to reduce size with 1
if ( nextSize < ((double)currentSize) * 0.5 && nextSize != currentSize - 1)
overrides.invalid(ValidationId.clusterSizeReduction,
- "Size reduction in '" + clusterName + "' is too large. Current size: " + currentSize +
- ", new size: " + nextSize + ". New size must be at least 50% of the current size",
+ "Size reduction in '" + clusterId.value() + "' is too large: " +
+ "New min size must be at least 50% of the current min size. " +
+ "Current size: " + currentSize + ", new size: " + nextSize,
now);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java
index 8fdcf249bbc..5343a322382 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java
@@ -5,6 +5,7 @@ import com.yahoo.collections.Pair;
import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.model.api.ConfigChangeAction;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.vespa.model.HostResource;
@@ -15,6 +16,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -27,35 +29,43 @@ public class ResourcesReductionValidator implements ChangeValidator {
@Override
public List<ConfigChangeAction> validate(VespaModel current, VespaModel next, ValidationOverrides overrides, Instant now) {
- var currentRequestedResourcesByClusterId = getRequestedResourcesByClusterId(current);
- var nextRequestedResourcesByClusterId = getRequestedResourcesByClusterId(next);
-
- for (var clusterTypeAndId : currentRequestedResourcesByClusterId.keySet()) {
- if (!nextRequestedResourcesByClusterId.containsKey(clusterTypeAndId)) continue;
- validate(currentRequestedResourcesByClusterId.get(clusterTypeAndId),
- nextRequestedResourcesByClusterId.get(clusterTypeAndId),
- clusterTypeAndId.getSecond(),
- overrides,
- now);
+ for (var clusterId : current.allClusters()) {
+ Capacity currentCapacity = current.provisioned().all().get(clusterId);
+ Capacity nextCapacity = next.provisioned().all().get(clusterId);
+ if (currentCapacity == null || nextCapacity == null) continue;
+ validate(currentCapacity, nextCapacity, clusterId, overrides, now);
}
return List.of();
}
- private void validate(NodeResources currentResources, NodeResources nextResources, ClusterSpec.Id clusterId,
- ValidationOverrides overrides, Instant now) {
+ private void validate(Capacity current,
+ Capacity next,
+ ClusterSpec.Id clusterId,
+ ValidationOverrides overrides,
+ Instant now) {
+ if (current.minResources().nodeResources() == NodeResources.unspecified) return;
+ if (next.minResources().nodeResources() == NodeResources.unspecified) return;
+
List<String> illegalChanges = Stream.of(
- validateResource("vCPU", currentResources.vcpu(), nextResources.vcpu()),
- validateResource("memory GB", currentResources.memoryGb(), nextResources.memoryGb()),
- validateResource("disk GB", currentResources.diskGb(), nextResources.diskGb()))
+ validateResource("vCPU",
+ current.minResources().nodeResources().vcpu(),
+ next.minResources().nodeResources().vcpu()),
+ validateResource("memory GB",
+ current.minResources().nodeResources().memoryGb(),
+ next.minResources().nodeResources().memoryGb()),
+ validateResource("disk GB",
+ current.minResources().nodeResources().diskGb(),
+ next.minResources().nodeResources().diskGb()))
.flatMap(Optional::stream)
.collect(Collectors.toList());
if (illegalChanges.isEmpty()) return;
overrides.invalid(ValidationId.resourcesReduction,
- "Resource reduction in '" + clusterId.value() + "' is too large. " +
- String.join(" ", illegalChanges) + " New resources must be at least 50% of the current resources",
- now);
+ "Resource reduction in '" + clusterId.value() + "' is too large. " +
+ String.join(" ", illegalChanges) +
+ " New min resources must be at least 50% of the current min resources",
+ now);
}
private static Optional<String> validateResource(String resourceName, double currentValue, double nextValue) {
@@ -64,15 +74,4 @@ public class ResourcesReductionValidator implements ChangeValidator {
return Optional.of(String.format(Locale.ENGLISH ,"Current %s: %.2f, new: %.2f.", resourceName, currentValue, nextValue));
}
- private static Map<Pair<ClusterSpec.Type, ClusterSpec.Id>, NodeResources> getRequestedResourcesByClusterId(VespaModel vespaModel) {
- return vespaModel.hostSystem().getHosts().stream()
- .map(HostResource::spec)
- .filter(spec -> spec.membership().isPresent() && spec.requestedResources().isPresent())
- .filter(spec -> !spec.membership().get().retired())
- .collect(Collectors.toMap(
- spec -> new Pair<>(spec.membership().get().cluster().type(), spec.membership().get().cluster().id()),
- spec -> spec.requestedResources().get(),
- (e1, e2) -> e1));
- }
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/first/AccessControlOnFirstDeploymentValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/first/AccessControlOnFirstDeploymentValidator.java
index 97153e42ee5..df5fe15ca82 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/first/AccessControlOnFirstDeploymentValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/first/AccessControlOnFirstDeploymentValidator.java
@@ -7,16 +7,16 @@ import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.application.validation.Validator;
+import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.ContainerCluster;
-import com.yahoo.vespa.model.container.ApplicationContainerCluster;
-import com.yahoo.vespa.model.container.component.Handler;
import java.util.ArrayList;
import java.util.List;
import static com.yahoo.collections.CollectionUtil.mkString;
-import static com.yahoo.vespa.model.container.http.AccessControl.isBuiltinGetOnly;
+import static com.yahoo.config.provision.InstanceName.defaultName;
+import static com.yahoo.vespa.model.container.http.AccessControl.hasHandlerThatNeedsProtection;
/**
* Validates that hosted applications in prod zones have write protection enabled.
@@ -28,10 +28,7 @@ public class AccessControlOnFirstDeploymentValidator extends Validator {
@Override
public void validate(VespaModel model, DeployState deployState) {
- if (! deployState.isHosted()) return;
- if (! deployState.zone().environment().isProduction()) return;
- if (deployState.zone().system().isPublic()) return;
- if (model.getAdmin().getApplicationType() != ApplicationType.DEFAULT) return;
+ if (! needsAccessControlValidation(model, deployState)) return;
List<String> offendingClusters = new ArrayList<>();
for (ContainerCluster<? extends Container> c : model.getContainerClusters().values()) {
@@ -44,23 +41,19 @@ public class AccessControlOnFirstDeploymentValidator extends Validator {
if (hasHandlerThatNeedsProtection(cluster) || ! cluster.getAllServlets().isEmpty())
offendingClusters.add(cluster.getName());
}
- if (! offendingClusters.isEmpty()
- && deployState.getApplicationPackage().getApplicationId().instance().equals(InstanceName.defaultName()))
+ if (! offendingClusters.isEmpty())
deployState.validationOverrides().invalid(ValidationId.accessControl,
"Access-control must be enabled for write operations to container clusters in production zones: " +
- mkString(offendingClusters, "[", ", ", "]."), deployState.now());
- }
-
- private boolean hasHandlerThatNeedsProtection(ApplicationContainerCluster cluster) {
- return cluster.getHandlers().stream().anyMatch(this::handlerNeedsProtection);
+ mkString(offendingClusters, "[", ", ", "]"), deployState.now());
}
- private boolean handlerNeedsProtection(Handler<?> handler) {
- return ! isBuiltinGetOnly(handler) && hasNonMbusBinding(handler);
- }
+ public static boolean needsAccessControlValidation(VespaModel model, DeployState deployState) {
+ if (! deployState.isHosted()) return false;
+ if (! deployState.zone().environment().isProduction()) return false;
+ if (deployState.zone().system().isPublic()) return false;
+ if (! deployState.getApplicationPackage().getApplicationId().instance().equals(defaultName())) return false;
+ if (model.getAdmin().getApplicationType() != ApplicationType.DEFAULT) return false;
- private boolean hasNonMbusBinding(Handler<?> handler) {
- return handler.getServerBindings().stream().anyMatch(binding -> ! binding.startsWith("mbus"));
+ return true;
}
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
index a146e2cd7e7..d2f06da992c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminBuilderBase.java
@@ -71,7 +71,7 @@ public abstract class DomAdminBuilderBase extends VespaDomBuilder.DomConfigProdu
Monitoring monitoring = getMonitoring(XML.getChild(adminElement,"monitoring"), deployState.isHosted());
Metrics metrics = new MetricsBuilder(applicationType, predefinedMetricSets)
.buildMetrics(XML.getChild(adminElement, "metrics"));
- FileDistributionConfigProducer fileDistributionConfigProducer = getFileDistributionConfigProducer(parent);
+ FileDistributionConfigProducer fileDistributionConfigProducer = getFileDistributionConfigProducer(parent, deployState.isHosted());
Admin admin = new Admin(parent, monitoring, metrics, multitenant, fileDistributionConfigProducer, deployState.isHosted());
admin.setApplicationType(applicationType);
@@ -81,8 +81,8 @@ public abstract class DomAdminBuilderBase extends VespaDomBuilder.DomConfigProdu
return admin;
}
- private FileDistributionConfigProducer getFileDistributionConfigProducer(AbstractConfigProducer parent) {
- return new FileDistributionConfigProducer(parent, fileRegistry, configServerSpecs);
+ private FileDistributionConfigProducer getFileDistributionConfigProducer(AbstractConfigProducer parent, boolean isHosted) {
+ return new FileDistributionConfigProducer(parent, fileRegistry, configServerSpecs, isHosted);
}
protected abstract void doBuildAdmin(DeployState deployState, Admin admin, Element adminE);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
index 61d0cd7cd1e..804a5442608 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
@@ -65,12 +65,12 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
createSlobroks(deployLogger, admin, allocateHosts(admin.hostSystem(), "slobroks", nodesSpecification));
}
else {
- createSlobroks(deployLogger, admin, pickContainerHostsForSlobrok(nodesSpecification.count(), 2));
+ createSlobroks(deployLogger, admin, pickContainerHostsForSlobrok(nodesSpecification.minResources().nodes(), 2));
}
}
private void assignLogserver(DeployState deployState, NodesSpecification nodesSpecification, Admin admin) {
- if (nodesSpecification.count() > 1) throw new IllegalArgumentException("You can only request a single log server");
+ if (nodesSpecification.minResources().nodes() > 1) throw new IllegalArgumentException("You can only request a single log server");
if (deployState.getProperties().applicationId().instance().isTester()) return; // No logserver is needed on tester applications
if (nodesSpecification.isDedicated()) {
Collection<HostResource> hosts = allocateHosts(admin.hostSystem(), "logserver", nodesSpecification);
@@ -79,7 +79,7 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
Logserver logserver = createLogserver(deployState.getDeployLogger(), admin, hosts);
createContainerOnLogserverHost(deployState, admin, logserver.getHostResource());
} else if (containerModels.iterator().hasNext()) {
- List<HostResource> hosts = sortedContainerHostsFrom(containerModels.iterator().next(), nodesSpecification.count(), false);
+ List<HostResource> hosts = sortedContainerHostsFrom(containerModels.iterator().next(), nodesSpecification.minResources().nodes(), false);
if (hosts.isEmpty()) return; // No log server can be created (and none is needed)
createLogserver(deployState.getDeployLogger(), admin, hosts);
@@ -91,8 +91,8 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
private NodesSpecification createNodesSpecificationForLogserver() {
DeployState deployState = context.getDeployState();
if (deployState.getProperties().useDedicatedNodeForLogserver() &&
- context.getApplicationType() == ConfigModelContext.ApplicationType.DEFAULT &&
- deployState.isHosted())
+ context.getApplicationType() == ConfigModelContext.ApplicationType.DEFAULT &&
+ deployState.isHosted())
return NodesSpecification.dedicated(1, context);
else
return NodesSpecification.nonDedicated(1, context);
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 f8f1f88e339..11fab0ada29 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
@@ -4,6 +4,7 @@ package com.yahoo.vespa.model.builder.xml.dom;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.text.XML;
import com.yahoo.config.model.producer.AbstractConfigProducer;
+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.w3c.dom.Element;
@@ -14,9 +15,13 @@ import org.w3c.dom.Element;
*/
public class DomClientProviderBuilder extends DomHandlerBuilder {
+ public DomClientProviderBuilder(ApplicationContainerCluster cluster) {
+ super(cluster);
+ }
+
@Override
- protected Handler doBuild(DeployState deployState, AbstractConfigProducer ancestor, Element clientElement) {
- Handler<? super Component<?, ?>> client = getHandler(clientElement);
+ protected Handler doBuild(DeployState deployState, AbstractConfigProducer parent, Element clientElement) {
+ Handler<? super Component<?, ?>> client = createHandler(clientElement);
for (Element binding : XML.getChildren(clientElement, "binding"))
client.addClientBindings(XML.getValue(binding));
@@ -24,7 +29,7 @@ public class DomClientProviderBuilder extends DomHandlerBuilder {
for (Element serverBinding : XML.getChildren(clientElement, "serverBinding"))
client.addServerBindings(XML.getValue(serverBinding));
- DomComponentBuilder.addChildren(deployState, ancestor, clientElement, client);
+ DomComponentBuilder.addChildren(deployState, parent, clientElement, client);
return client;
}
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 558c4428d15..ac6d089cf24 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
@@ -1,38 +1,86 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.builder.xml.dom;
+import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.text.XML;
-import com.yahoo.config.model.producer.AbstractConfigProducer;
+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.xml.BundleInstantiationSpecificationBuilder;
import org.w3c.dom.Element;
+import java.util.Set;
+
+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;
+import static com.yahoo.vespa.model.container.ContainerCluster.STATE_HANDLER_BINDING_1;
+import static com.yahoo.vespa.model.container.ContainerCluster.STATE_HANDLER_BINDING_2;
+import static com.yahoo.vespa.model.container.ContainerCluster.VIP_HANDLER_BINDING;
+import static java.util.logging.Level.INFO;
+
/**
* @author gjoranv
*/
public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Handler> {
+ private static final Set<String> reservedBindings = Set.of(METRICS_V2_HANDLER_BINDING_1,
+ METRICS_V2_HANDLER_BINDING_2,
+ STATE_HANDLER_BINDING_1,
+ STATE_HANDLER_BINDING_2,
+ VIP_HANDLER_BINDING);
+ private final ApplicationContainerCluster cluster;
+
+ public DomHandlerBuilder(ApplicationContainerCluster cluster) {
+ this.cluster = cluster;
+ }
+
@Override
- protected Handler doBuild(DeployState deployState, AbstractConfigProducer ancestor, Element handlerElement) {
- Handler<? super Component<?, ?>> handler = getHandler(handlerElement);
+ protected Handler doBuild(DeployState deployState, AbstractConfigProducer parent, Element handlerElement) {
+ Handler<? super Component<?, ?>> handler = createHandler(handlerElement);
for (Element binding : XML.getChildren(handlerElement, "binding"))
- handler.addServerBindings(XML.getValue(binding));
+ addServerBinding(handler, XML.getValue(binding), deployState.getDeployLogger());
for (Element clientBinding : XML.getChildren(handlerElement, "clientBinding"))
handler.addClientBindings(XML.getValue(clientBinding));
- DomComponentBuilder.addChildren(deployState, ancestor, handlerElement, handler);
+ DomComponentBuilder.addChildren(deployState, parent, handlerElement, handler);
return handler;
}
- protected Handler<? super Component<?, ?>> getHandler(Element handlerElement) {
+ Handler<? super Component<?, ?>> createHandler(Element handlerElement) {
BundleInstantiationSpecification bundleSpec = BundleInstantiationSpecificationBuilder.build(handlerElement);
return new Handler<>(new ComponentModel(bundleSpec));
}
+
+ private void addServerBinding(Handler<? super Component<?, ?>> handler, String binding, DeployLogger log) {
+ throwIfBindingIsReserved(binding, handler);
+ handler.addServerBindings(binding);
+ removeExistingServerBinding(binding, handler, log);
+ }
+
+ private void throwIfBindingIsReserved(String binding, Handler<?> newHandler) {
+ for (var reserved : reservedBindings) {
+ if (binding.equals(reserved)) {
+ throw new IllegalArgumentException("Binding '" + binding + "' is a reserved Vespa binding and " +
+ "cannot be used by handler: " + newHandler.getComponentId());
+ }
+ }
+ }
+
+ private void removeExistingServerBinding(String binding, Handler<?> newHandler, DeployLogger log) {
+ for (var handler : cluster.getHandlers()) {
+ if (handler.getServerBindings().contains(binding)) {
+ handler.removeServerBinding(binding);
+ log.log(INFO, "Binding '" + binding + "' was already in use by handler '" +
+ handler.getComponentId() + "', but will now be taken over by handler: " + newHandler.getComponentId());
+ }
+ }
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java
index d34a11abdf4..80c95ad6b59 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/ModelElement.java
@@ -172,7 +172,12 @@ public class ModelElement {
/** Returns the content of the attribute with the given name, or null if none */
public String stringAttribute(String name) {
- if ( ! xml.hasAttribute(name)) return null;
+ return stringAttribute(name, null);
+ }
+
+ /** Returns the content of the attribute with the given name, or the default value if none */
+ public String stringAttribute(String name, String defaultValue) {
+ if ( ! xml.hasAttribute(name)) return defaultValue;
return xml.getAttribute(name);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
index 2c88f965f1f..ad6eebe1ca5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
@@ -1,11 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.builder.xml.dom;
+import com.yahoo.collections.Pair;
import com.yahoo.component.Version;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.ConfigModelContext;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
+import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.text.XML;
@@ -15,9 +17,11 @@ import com.yahoo.vespa.model.container.xml.ContainerModelBuilder;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
+import java.util.function.Function;
+import java.util.regex.Pattern;
/**
* A common utility class to represent a requirement for nodes during model building.
@@ -27,11 +31,9 @@ import java.util.Set;
*/
public class NodesSpecification {
- private final boolean dedicated;
-
- private final int count;
+ private final ClusterResources min, max;
- private final int groups;
+ private final boolean dedicated;
/** The Vespa version we want the nodes to run */
private Version version;
@@ -43,50 +45,76 @@ public class NodesSpecification {
private final boolean required;
private final boolean canFail;
-
- private final boolean exclusive;
-
- /** Whether this requires running container and content processes co-located on the same node. */
- private final boolean combined;
- /** The resources each node should have, or empty to use the default */
- private final Optional<NodeResources> resources;
-
- /** The identifier of the custom docker image layer to use (not supported yet) */
- private final Optional<String> dockerImage;
+ private final boolean exclusive;
- private NodesSpecification(boolean dedicated, int count, int groups, Version version,
- boolean required, boolean canFail, boolean exclusive, boolean combined,
- Optional<NodeResources> resources, Optional<String> dockerImage) {
+ /** The repo part of a docker image (without tag), optional */
+ private final Optional<String> dockerImageRepo;
+
+ /** The ID of the cluster referencing this node specification, if any */
+ private final Optional<String> combinedId;
+
+ private NodesSpecification(ClusterResources min,
+ ClusterResources max,
+ boolean dedicated, Version version,
+ boolean required, boolean canFail, boolean exclusive,
+ Optional<String> dockerImageRepo,
+ Optional<String> combinedId) {
+ if (max.smallerThan(min))
+ throw new IllegalArgumentException("Min resources must be larger or equal to max resources, but " +
+ max + " is smaller than " + min);
+
+ // Non-scaled resources must be equal
+ if ( ! min.nodeResources().justNonNumbers().equals(max.nodeResources().justNonNumbers()))
+ throw new IllegalArgumentException("Min and max resources must have the same non-numeric settings, but " +
+ "min is " + min + " and max " + max);
+ if (min.nodeResources().bandwidthGbps() != max.nodeResources().bandwidthGbps())
+ throw new IllegalArgumentException("Min and max resources must have the same bandwith, but " +
+ "min is " + min + " and max " + max);
+
+ this.min = min;
+ this.max = max;
this.dedicated = dedicated;
- this.count = count;
- this.groups = groups;
this.version = version;
this.required = required;
this.canFail = canFail;
this.exclusive = exclusive;
- this.resources = resources;
- this.dockerImage = dockerImage;
- this.combined = combined;
+ this.dockerImageRepo = dockerImageRepo;
+ this.combinedId = combinedId;
}
- private NodesSpecification(boolean dedicated, boolean canFail, boolean combined, Version version, ModelElement nodesElement) {
- this(dedicated,
- nodesElement.integerAttribute("count", 1),
- nodesElement.integerAttribute("groups", 1),
- version,
- nodesElement.booleanAttribute("required", false),
- canFail,
- nodesElement.booleanAttribute("exclusive", false),
- combined,
- getResources(nodesElement),
- Optional.ofNullable(nodesElement.stringAttribute("docker-image")));
+ private static NodesSpecification create(boolean dedicated, boolean canFail, Version version,
+ ModelElement nodesElement, Optional<String> dockerImageRepo) {
+ var resolvedElement = resolveElement(nodesElement);
+ var combinedId = findCombinedId(nodesElement, resolvedElement);
+ var resources = toResources(resolvedElement);
+ return new NodesSpecification(resources.getFirst(),
+ resources.getSecond(),
+ dedicated,
+ version,
+ resolvedElement.booleanAttribute("required", false),
+ canFail,
+ resolvedElement.booleanAttribute("exclusive", false),
+ dockerImageToUse(resolvedElement, dockerImageRepo),
+ combinedId);
}
- private static NodesSpecification create(boolean dedicated, boolean canFail, Version version, ModelElement nodesElement) {
- var resolvedElement = resolveElement(nodesElement);
- boolean combined = resolvedElement != nodesElement || isReferencedByOtherElement(nodesElement);
- return new NodesSpecification(dedicated, canFail, combined, version, resolvedElement);
+ private static Pair<ClusterResources, ClusterResources> toResources(ModelElement nodesElement) {
+ Pair<Integer, Integer> nodes = toRange(nodesElement.stringAttribute("count"), 1, Integer::parseInt);
+ Pair<Integer, Integer> groups = toRange(nodesElement.stringAttribute("groups"), 1, Integer::parseInt);
+ var min = new ClusterResources(nodes.getFirst(), groups.getFirst(), nodeResources(nodesElement).getFirst());
+ var max = new ClusterResources(nodes.getSecond(), groups.getSecond(), nodeResources(nodesElement).getSecond());
+ return new Pair<>(min, max);
+ }
+
+ /** Returns the ID of the cluster referencing this node specification, if any */
+ private static Optional<String> findCombinedId(ModelElement nodesElement, ModelElement resolvedElement) {
+ if (resolvedElement != nodesElement) {
+ // Specification for a container cluster referencing nodes in a content cluster
+ return containerIdOf(nodesElement);
+ }
+ // Specification for a content cluster that is referenced by a container cluster
+ return containerIdReferencing(nodesElement);
}
/** Returns a requirement for dedicated nodes taken from the given <code>nodes</code> element */
@@ -94,7 +122,8 @@ public class NodesSpecification {
return create(true,
! context.getDeployState().getProperties().isBootstrap(),
context.getDeployState().getWantedNodeVespaVersion(),
- nodesElement);
+ nodesElement,
+ context.getDeployState().getWantedDockerImageRepo());
}
/**
@@ -110,7 +139,7 @@ public class NodesSpecification {
}
/**
- * Returns a requirement for undedicated or dedicated nodes taken from the <code>nodes</code> element
+ * Returns a requirement for non-dedicated or dedicated nodes taken from the <code>nodes</code> element
* contained in the given parent element, or empty if the parent element is null, or the nodes elements
* is not present.
*/
@@ -121,37 +150,42 @@ public class NodesSpecification {
if (nodesElement == null) return Optional.empty();
return Optional.of(create(nodesElement.booleanAttribute("dedicated", false),
! context.getDeployState().getProperties().isBootstrap(),
- context.getDeployState().getWantedNodeVespaVersion(), nodesElement));
+ context.getDeployState().getWantedNodeVespaVersion(),
+ nodesElement,
+ context.getDeployState().getWantedDockerImageRepo()));
}
- /** Returns a requirement from <code>count</code> nondedicated nodes in one group */
+ /**
+ * Returns a requirement from <code>count</code> non-dedicated nodes in one group
+ */
public static NodesSpecification nonDedicated(int count, ConfigModelContext context) {
- return new NodesSpecification(false,
- count,
- 1,
+ return new NodesSpecification(new ClusterResources(count, 1, NodeResources.unspecified),
+ new ClusterResources(count, 1, NodeResources.unspecified),
+ false,
context.getDeployState().getWantedNodeVespaVersion(),
false,
! context.getDeployState().getProperties().isBootstrap(),
false,
- false,
- Optional.empty(),
+ context.getDeployState().getWantedDockerImageRepo(),
Optional.empty());
}
/** Returns a requirement from <code>count</code> dedicated nodes in one group */
public static NodesSpecification dedicated(int count, ConfigModelContext context) {
- return new NodesSpecification(true,
- count,
- 1,
+ return new NodesSpecification(new ClusterResources(count, 1, NodeResources.unspecified),
+ new ClusterResources(count, 1, NodeResources.unspecified),
+ true,
context.getDeployState().getWantedNodeVespaVersion(),
false,
! context.getDeployState().getProperties().isBootstrap(),
false,
- false,
- Optional.empty(),
+ context.getDeployState().getWantedDockerImageRepo(),
Optional.empty());
}
+ public ClusterResources minResources() { return min; }
+ public ClusterResources maxResources() { return max; }
+
/**
* Returns whether this requires dedicated nodes.
* Otherwise the model encountering this request should reuse nodes requested for other purposes whenever possible.
@@ -165,42 +199,48 @@ public class NodesSpecification {
*/
public boolean isExclusive() { return exclusive; }
- /** Returns the number of nodes required */
- public int count() { return count; }
-
- /** Returns the number of host groups this specifies. Default is 1 */
- public int groups() { return groups; }
-
public Map<HostResource, ClusterMembership> provision(HostSystem hostSystem,
ClusterSpec.Type clusterType,
ClusterSpec.Id clusterId,
DeployLogger logger) {
- if (combined)
+ if (combinedId.isPresent())
clusterType = ClusterSpec.Type.combined;
- ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId, version, exclusive);
- return hostSystem.allocateHosts(cluster, Capacity.fromCount(count, resources, required, canFail), groups, logger);
+ ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId)
+ .vespaVersion(version)
+ .exclusive(exclusive)
+ .combinedId(combinedId.map(ClusterSpec.Id::from))
+ .dockerImageRepo(dockerImageRepo)
+ .build();
+ return hostSystem.allocateHosts(cluster, Capacity.from(min, max, required, canFail), logger);
}
- private static Optional<NodeResources> getResources(ModelElement nodesElement) {
+ private static Pair<NodeResources, NodeResources> nodeResources(ModelElement nodesElement) {
ModelElement resources = nodesElement.child("resources");
if (resources != null) {
- return Optional.of(new NodeResources(resources.requiredDoubleAttribute("vcpu"),
- parseGbAmount(resources.requiredStringAttribute("memory"), "B"),
- parseGbAmount(resources.requiredStringAttribute("disk"), "B"),
- Optional.ofNullable(resources.stringAttribute("bandwidth"))
- .map(b -> parseGbAmount(b, "BPS"))
- .orElse(0.3),
- parseOptionalDiskSpeed(resources.stringAttribute("disk-speed")),
- parseOptionalStorageType(resources.stringAttribute("storage-type"))));
+ return nodeResourcesFromResorcesElement(resources);
}
else if (nodesElement.stringAttribute("flavor") != null) { // legacy fallback
- return Optional.of(NodeResources.fromLegacyName(nodesElement.stringAttribute("flavor")));
+ var flavorResources = NodeResources.fromLegacyName(nodesElement.stringAttribute("flavor"));
+ return new Pair<>(flavorResources, flavorResources);
}
- else { // Get the default
- return Optional.empty();
+ else {
+ return new Pair<>(NodeResources.unspecified, NodeResources.unspecified);
}
}
+ private static Pair<NodeResources, NodeResources> nodeResourcesFromResorcesElement(ModelElement element) {
+ Pair<Double, Double> vcpu = toRange(element.requiredStringAttribute("vcpu"), .0, Double::parseDouble);
+ Pair<Double, Double> memory = toRange(element.requiredStringAttribute("memory"), .0, s -> parseGbAmount(s, "B"));
+ Pair<Double, Double> disk = toRange(element.requiredStringAttribute("disk"), .0, s -> parseGbAmount(s, "B"));
+ Pair<Double, Double> bandwith = toRange(element.stringAttribute("bandwith"), .3, s -> parseGbAmount(s, "BPS"));
+ NodeResources.DiskSpeed diskSpeed = parseOptionalDiskSpeed(element.stringAttribute("disk-speed"));
+ NodeResources.StorageType storageType = parseOptionalStorageType(element.stringAttribute("storage-type"));
+
+ var min = new NodeResources(vcpu.getFirst(), memory.getFirst(), disk.getFirst(), bandwith.getFirst(), diskSpeed, storageType);
+ var max = new NodeResources(vcpu.getSecond(), memory.getSecond(), disk.getSecond(), bandwith.getSecond(), diskSpeed, storageType);
+ return new Pair<>(min, max);
+ }
+
private static double parseGbAmount(String byteAmount, String unit) {
byteAmount = byteAmount.strip();
byteAmount = byteAmount.toUpperCase();
@@ -280,24 +320,35 @@ public class NodesSpecification {
return new ModelElement(referencedNodesElement);
}
- /** Returns whether the given nodesElement is referenced by any other nodes element */
- private static boolean isReferencedByOtherElement(ModelElement nodesElement) {
+ /** Returns the ID of the parent container element of nodesElement, if any */
+ private static Optional<String> containerIdOf(ModelElement nodesElement) {
+ var element = nodesElement.getXml();
+ for (var containerTag : List.of("container", "jdisc")) {
+ var container = findParentByTag(containerTag, element);
+ if (container.isEmpty()) continue;
+ return container.map(el -> el.getAttribute("id"));
+ }
+ return Optional.empty();
+ }
+
+ /** Returns the ID of the container element referencing nodesElement, if any */
+ private static Optional<String> containerIdReferencing(ModelElement nodesElement) {
var element = nodesElement.getXml();
var services = findParentByTag("services", element);
- if (services.isEmpty()) return false;
+ if (services.isEmpty()) return Optional.empty();
var content = findParentByTag("content", element);
- if (content.isEmpty()) return false;
+ if (content.isEmpty()) return Optional.empty();
var contentClusterId = content.get().getAttribute("id");
- if (contentClusterId.isEmpty()) return false;
+ if (contentClusterId.isEmpty()) return Optional.empty();
for (var rootChild : XML.getChildren(services.get())) {
if ( ! ContainerModelBuilder.isContainerTag(rootChild)) continue;
var nodes = XML.getChild(rootChild, "nodes");
if (nodes == null) continue;
if (!contentClusterId.equals(nodes.getAttribute("of"))) continue;
- return true;
+ return Optional.of(rootChild.getAttribute("id"));
}
- return false;
+ return Optional.empty();
}
private static Optional<Element> findChildById(Element parent, String id) {
@@ -319,11 +370,33 @@ public class NodesSpecification {
return new IllegalArgumentException("referenced service '" + referenceId + "' is not defined");
}
+ private static Optional<String> dockerImageToUse(ModelElement nodesElement, Optional<String> dockerImage) {
+ String dockerImageFromElement = nodesElement.stringAttribute("docker-image");
+ return dockerImageFromElement == null ? dockerImage : Optional.of(dockerImageFromElement);
+ }
+
+ /** Parses a value ("value") or value range ("[min-value, max-value]") */
+ private static <T> Pair<T, T> toRange(String s, T defaultValue, Function<String, T> valueParser) {
+ try {
+ if (s == null) return new Pair<>(defaultValue, defaultValue);
+ s = s.trim();
+ if (s.startsWith("[") && s.endsWith("]")) {
+ String[] numbers = s.substring(1, s.length() - 1).split(",");
+ if (numbers.length != 2) throw new IllegalArgumentException();
+ return new Pair<>(valueParser.apply(numbers[0].trim()), valueParser.apply(numbers[1].trim()));
+ } else {
+ return new Pair<>(valueParser.apply(s), valueParser.apply(s));
+ }
+ }
+ catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Expected a number or range on the form [min, max], but got '" + s + "'", e);
+ }
+ }
+
@Override
public String toString() {
- return "specification of " + count + (dedicated ? " dedicated " : " ") + "nodes" +
- (resources.isPresent() ? " with resources " + resources.get() : "") +
- (groups > 1 ? " in " + groups + " groups" : "");
+ return "specification of " + (dedicated ? "dedicated " : "") +
+ (min.equals(max) ? min : "min " + min + " max " + max);
}
}
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 97b78e1b9b1..c9caca1831f 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
@@ -141,8 +141,7 @@ public class VespaDomBuilder extends VespaModelBuilder {
}
private void initializeService(AbstractService t, DeployState deployState,
- HostSystem hostSystem, Element producerSpec)
- {
+ HostSystem hostSystem, Element producerSpec) {
initializeProducer(t, deployState, producerSpec);
if (producerSpec != null) {
if (producerSpec.hasAttribute(JVM_OPTIONS)) {
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 efd00528d54..63e6af03c44 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
@@ -1,6 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container;
+import ai.vespa.metricsproxy.http.application.ApplicationMetricsHandler;
import com.yahoo.component.ComponentId;
import com.yahoo.component.ComponentSpecification;
import com.yahoo.config.FileReference;
@@ -9,6 +10,9 @@ import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.container.BundlesConfig;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
+import com.yahoo.container.handler.ThreadpoolConfig;
+import com.yahoo.container.handler.metrics.MetricsProxyApiConfig;
+import com.yahoo.container.handler.metrics.MetricsV2Handler;
import com.yahoo.container.jdisc.ContainerMbusConfig;
import com.yahoo.container.jdisc.messagebus.MbusServerProvider;
import com.yahoo.jdisc.http.ServletPathsConfig;
@@ -17,8 +21,10 @@ import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
import com.yahoo.vespa.defaults.Defaults;
+import com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.ConfigProducerGroup;
+import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.Servlet;
import com.yahoo.vespa.model.container.jersey.Jersey2Servlet;
import com.yahoo.vespa.model.container.jersey.RestApi;
@@ -45,7 +51,12 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
RankProfilesConfig.Producer,
RankingConstantsConfig.Producer,
ServletPathsConfig.Producer,
- ContainerMbusConfig.Producer {
+ ContainerMbusConfig.Producer,
+ MetricsProxyApiConfig.Producer {
+
+ public static final String METRICS_V2_HANDLER_CLASS = MetricsV2Handler.class.getName();
+ public static final String METRICS_V2_HANDLER_BINDING_1 = "http://*" + MetricsV2Handler.V2_PATH;
+ public static final String METRICS_V2_HANDLER_BINDING_2 = METRICS_V2_HANDLER_BINDING_1 + "/*";
private final Set<FileReference> applicationBundles = new LinkedHashSet<>();
@@ -58,6 +69,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
private MbusParams mbusParams;
private boolean messageBusEnabled = true;
+ private final double softStartSeconds;
private Integer memoryPercentage = null;
@@ -72,7 +84,9 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
addSimpleComponent("com.yahoo.container.jdisc.DeprecatedSecretStoreProvider");
addSimpleComponent("com.yahoo.container.jdisc.CertificateStoreProvider");
addSimpleComponent("com.yahoo.container.jdisc.AthenzIdentityProviderProvider");
+ addMetricsV2Handler();
addTestrunnerComponentsIfTester(deployState);
+ softStartSeconds = deployState.getProperties().defaultSoftStartSeconds();
}
@Override
@@ -99,6 +113,13 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
}
}
+ public void addMetricsV2Handler() {
+ Handler<AbstractConfigProducer<?>> handler = new Handler<>(
+ new ComponentModel(METRICS_V2_HANDLER_CLASS, null, null, null));
+ handler.addServerBindings(METRICS_V2_HANDLER_BINDING_1, METRICS_V2_HANDLER_BINDING_2);
+ addComponent(handler);
+ }
+
private void addTestrunnerComponentsIfTester(DeployState deployState) {
if (deployState.isHosted() && deployState.getProperties().applicationId().instance().isTester())
addPlatformBundle(Paths.get(Defaults.getDefaults().underVespaHome("lib/jars/vespa-testrunner-components-jar-with-dependencies.jar")));
@@ -188,10 +209,17 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
}
@Override
+ public void getConfig(MetricsProxyApiConfig.Builder builder) {
+ builder.metricsPort(MetricsProxyContainer.BASEPORT)
+ .metricsApiPath(ApplicationMetricsHandler.VALUES_PATH);
+ }
+
+ @Override
public void getConfig(QrStartConfig.Builder builder) {
super.getConfig(builder);
builder.jvm.verbosegc(true)
.availableProcessors(0)
+ .compressedClassSpaceSize(0) //TODO Reduce, next step is 512m
.minHeapsize(1536)
.heapsize(1536);
if (getMemoryPercentage().isPresent()) {
@@ -223,6 +251,11 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
null))));
}
+ @Override
+ public void getConfig(ThreadpoolConfig.Builder builder) {
+ builder.softStartSeconds(softStartSeconds);
+ }
+
public static class MbusParams {
// the amount of the maxpendingbytes to process concurrently, typically 0.2 (20%)
final Double maxConcurrentFactor;
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 7e2d6680827..31c8724d634 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
@@ -140,7 +140,7 @@ public abstract class Container extends AbstractService implements
if (http == null) {
return defaultHttpServer;
} else {
- return http.getHttpServer();
+ return http.getHttpServer().orElse(null);
}
}
@@ -228,10 +228,10 @@ public abstract class Container extends AbstractService implements
// XXX unused - remove:
from.allocatePort("http/1");
portsMeta.on(offset++).tag("http").tag("external");
- } else if (getHttp().getHttpServer() == null) {
+ } else if (getHttp().getHttpServer().isEmpty()) {
// no http server ports
} else {
- for (ConnectorFactory connectorFactory : getHttp().getHttpServer().getConnectorFactories()) {
+ for (ConnectorFactory connectorFactory : getHttp().getHttpServer().get().getConnectorFactories()) {
int port = getPort(connectorFactory);
String name = "http/" + connectorFactory.getName();
from.requirePort(port, name);
@@ -280,7 +280,7 @@ public abstract class Container extends AbstractService implements
final Http http = getHttp();
if (http != null) {
// TODO: allow the user to specify health port manually
- if (http.getHttpServer() == null) {
+ if (http.getHttpServer().isEmpty()) {
return -1;
} else {
return getRelativePort(0);
@@ -303,7 +303,7 @@ public abstract class Container extends AbstractService implements
.slobrokId(serviceSlobrokId())).
filedistributor(filedistributorConfig());
if (clusterName != null) {
- builder.discriminator(clusterName+"."+name);
+ builder.discriminator(clusterName + "." + name);
} else {
builder.discriminator(name);
}
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 6fa446bf365..e2b1f97a6eb 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
@@ -19,6 +19,7 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.core.ApplicationMetadataConfig;
import com.yahoo.container.core.document.ContainerDocumentConfig;
import com.yahoo.container.handler.ThreadPoolProvider;
+import com.yahoo.container.handler.ThreadpoolConfig;
import com.yahoo.container.jdisc.JdiscBindingsConfig;
import com.yahoo.container.jdisc.config.HealthMonitorConfig;
import com.yahoo.container.jdisc.state.StateHandler;
@@ -99,7 +100,9 @@ public abstract class ContainerCluster<CONTAINER extends Container>
DocprocConfig.Producer,
ClusterInfoConfig.Producer,
RoutingProviderConfig.Producer,
- ConfigserverConfig.Producer {
+ ConfigserverConfig.Producer,
+ ThreadpoolConfig.Producer
+{
/**
* URI prefix used for internal, usually programmatic, APIs. URIs using this
@@ -111,15 +114,20 @@ public abstract class ContainerCluster<CONTAINER extends Container>
public static final String APPLICATION_STATUS_HANDLER_CLASS = "com.yahoo.container.handler.observability.ApplicationStatusHandler";
public static final String BINDINGS_OVERVIEW_HANDLER_CLASS = BindingsOverviewHandler.class.getName();
- public static final String STATE_HANDLER_CLASS = "com.yahoo.container.jdisc.state.StateHandler";
public static final String LOG_HANDLER_CLASS = com.yahoo.container.handler.LogHandler.class.getName();
public static final String DEFAULT_LINGUISTICS_PROVIDER = "com.yahoo.language.provider.DefaultLinguisticsProvider";
public static final String CMS = "-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15 -XX:NewRatio=1";
public static final String G1GC = "-XX:+UseG1GC -XX:MaxTenuringThreshold=15";
+ public static final String STATE_HANDLER_CLASS = "com.yahoo.container.jdisc.state.StateHandler";
+ public static final String STATE_HANDLER_BINDING_1 = "http://*" + StateHandler.STATE_API_ROOT;
+ public static final String STATE_HANDLER_BINDING_2 = STATE_HANDLER_BINDING_1 + "/*";
+
public static final String ROOT_HANDLER_PATH = "/";
public static final String ROOT_HANDLER_BINDING = "http://*" + ROOT_HANDLER_PATH;
+ public static final String VIP_HANDLER_BINDING = "http://*/status.html";
+
private final String name;
protected List<CONTAINER> containers = new ArrayList<>();
@@ -200,15 +208,11 @@ public abstract class ContainerCluster<CONTAINER extends Container>
public void addMetricStateHandler() {
Handler<AbstractConfigProducer<?>> stateHandler = new Handler<>(
new ComponentModel(STATE_HANDLER_CLASS, null, null, null));
- stateHandler.addServerBindings("http://*" + StateHandler.STATE_API_ROOT,
- "http://*" + StateHandler.STATE_API_ROOT + "/*");
+ stateHandler.addServerBindings(STATE_HANDLER_BINDING_1, STATE_HANDLER_BINDING_2);
addComponent(stateHandler);
}
public void addDefaultRootHandler() {
- if (hasHandlerWithBinding(ROOT_HANDLER_BINDING))
- return;
-
Handler<AbstractConfigProducer<?>> handler = new Handler<>(
new ComponentModel(BundleInstantiationSpecification.getFromStrings(
BINDINGS_OVERVIEW_HANDLER_CLASS, null, null), null)); // null bundle, as the handler is in container-disc
@@ -216,15 +220,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
addComponent(handler);
}
- private boolean hasHandlerWithBinding(String binding) {
- Collection<Handler<?>> handlers = getHandlers();
- for (Handler handler : handlers) {
- if (handler.getServerBindings().contains(binding))
- return true;
- }
- return false;
- }
-
public void addApplicationStatusHandler() {
Handler<AbstractConfigProducer<?>> statusHandler = new Handler<>(
new ComponentModel(BundleInstantiationSpecification.getInternalHandlerSpecificationFromStrings(
@@ -235,7 +230,7 @@ public abstract class ContainerCluster<CONTAINER extends Container>
public void addVipHandler() {
Handler<?> vipHandler = Handler.fromClassName(FileStatusHandlerComponent.CLASS);
- vipHandler.addServerBindings("http://*/status.html");
+ vipHandler.addServerBindings(VIP_HANDLER_BINDING);
addComponent(vipHandler);
}
@@ -484,6 +479,7 @@ public abstract class ContainerCluster<CONTAINER extends Container>
builder.jvm
.verbosegc(false)
.availableProcessors(2)
+ .compressedClassSpaceSize(32)
.minHeapsize(32)
.heapsize(512)
.heapSizeAsPercentageOfPhysicalMemory(0)
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java
index c671749cff0..a466dabe984 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/ConfigProducerGroup.java
@@ -4,7 +4,12 @@ package com.yahoo.vespa.model.container.component;
import com.yahoo.component.ComponentId;
import com.yahoo.config.model.producer.AbstractConfigProducer;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
/**
* A group of config producers that have a component id.
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java
index ee61b34987a..3d9a1b2e665 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/FileStatusHandlerComponent.java
@@ -10,6 +10,7 @@ import com.yahoo.osgi.provider.model.ComponentModel;
* @author Tony Vaagenes
*/
public class FileStatusHandlerComponent extends Handler implements VipStatusConfig.Producer {
+
public static final String CLASS = "com.yahoo.container.handler.VipStatusHandler";
private final String fileName;
@@ -26,4 +27,5 @@ public class FileStatusHandlerComponent extends Handler implements VipStatusConf
builder.accessdisk(true).
statusfile(fileName);
}
+
}
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 e07c3216850..82484e07773 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
@@ -8,7 +8,9 @@ import com.yahoo.config.model.producer.AbstractConfigProducer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
/**
* Models a jdisc RequestHandler (including ClientProvider).
@@ -21,7 +23,7 @@ import java.util.List;
*/
public class Handler<CHILD extends AbstractConfigProducer<?>> extends Component<CHILD, ComponentModel> {
- private List<String> serverBindings = new ArrayList<>();
+ private Set<String> serverBindings = new LinkedHashSet<>();
private List<String> clientBindings = new ArrayList<>();
public Handler(ComponentModel model) {
@@ -40,12 +42,16 @@ public class Handler<CHILD extends AbstractConfigProducer<?>> extends Component<
serverBindings.addAll(Arrays.asList(bindings));
}
+ public void removeServerBinding(String binding) {
+ serverBindings.remove(binding);
+ }
+
public void addClientBindings(String... bindings) {
clientBindings.addAll(Arrays.asList(bindings));
}
- public final List<String> getServerBindings() {
- return Collections.unmodifiableList(serverBindings);
+ public final Set<String> getServerBindings() {
+ return Collections.unmodifiableSet(serverBindings);
}
public final List<String> getClientBindings() {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java
index f3758def2b1..470b82496a3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.container.StatisticsConfig;
+import com.yahoo.container.core.VipStatusConfig;
import com.yahoo.container.jdisc.config.HealthMonitorConfig;
import com.yahoo.net.HostName;
import com.yahoo.vespa.defaults.Defaults;
@@ -29,7 +30,8 @@ public class ConfigserverCluster extends AbstractConfigProducer
ZookeeperServerConfig.Producer,
ConfigserverConfig.Producer,
StatisticsConfig.Producer,
- HealthMonitorConfig.Producer {
+ HealthMonitorConfig.Producer,
+ VipStatusConfig.Producer {
private final CloudConfigOptions options;
private ContainerCluster containerCluster;
@@ -116,8 +118,8 @@ public class ConfigserverCluster extends AbstractConfigProducer
}
builder.serverId(HostName.getLocalhost());
- if (!containerCluster.getHttp().getHttpServer().getConnectorFactories().isEmpty()) {
- builder.httpport(containerCluster.getHttp().getHttpServer().getConnectorFactories().get(0).getListenPort());
+ if (!containerCluster.getHttp().getHttpServer().get().getConnectorFactories().isEmpty()) {
+ builder.httpport(containerCluster.getHttp().getHttpServer().get().getConnectorFactories().get(0).getListenPort());
}
if (options.useVespaVersionInRequest().isPresent()) {
builder.useVespaVersionInRequest(options.useVespaVersionInRequest().get());
@@ -178,4 +180,8 @@ public class ConfigserverCluster extends AbstractConfigProducer
builder.snapshot_interval(60.0);
}
+ @Override
+ public void getConfig(VipStatusConfig.Builder builder) {
+ builder.initiallyInRotation(false);
+ }
}
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 d3ba2718d71..dd48e65c340 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
@@ -1,10 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.http;
-import com.google.common.collect.ImmutableList;
import com.yahoo.component.ComponentId;
import com.yahoo.component.ComponentSpecification;
import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent;
import com.yahoo.vespa.model.container.component.Handler;
@@ -15,7 +15,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -24,23 +23,23 @@ import java.util.stream.Stream;
* Helper class for http access control.
*
* @author gjoranv
+ * @author bjorncs
*/
public final class AccessControl {
public static final ComponentId ACCESS_CONTROL_CHAIN_ID = ComponentId.fromString("access-control-chain");
- private static final List<String> UNPROTECTED_HANDLERS = ImmutableList.of(
+ public static final List<String> UNPROTECTED_HANDLERS = List.of(
FileStatusHandlerComponent.CLASS,
ContainerCluster.APPLICATION_STATUS_HANDLER_CLASS,
ContainerCluster.BINDINGS_OVERVIEW_HANDLER_CLASS,
ContainerCluster.STATE_HANDLER_CLASS,
- ContainerCluster.LOG_HANDLER_CLASS
+ ContainerCluster.LOG_HANDLER_CLASS,
+ ApplicationContainerCluster.METRICS_V2_HANDLER_CLASS
);
public static final class Builder {
private String domain;
- private String applicationId;
- private Optional<String> vespaDomain = Optional.empty();
private boolean readEnabled = false;
private boolean writeEnabled = true;
private final Set<String> excludeBindings = new LinkedHashSet<>();
@@ -48,9 +47,8 @@ public final class AccessControl {
private Collection<Servlet> servlets = Collections.emptyList();
private final DeployLogger logger;
- public Builder(String domain, String applicationId, DeployLogger logger) {
+ public Builder(String domain, DeployLogger logger) {
this.domain = domain;
- this.applicationId = applicationId;
this.logger = logger;
}
@@ -69,52 +67,37 @@ public final class AccessControl {
return this;
}
- public Builder vespaDomain(String vespaDomain) {
- this.vespaDomain = Optional.ofNullable(vespaDomain);
- return this;
- }
-
- public Builder setHandlers(Collection<Handler<?>> handlers) {
- this.handlers = handlers;
- return this;
- }
-
- public Builder setServlets(Collection<Servlet> servlets) {
- this.servlets = servlets;
+ public Builder setHandlers(ApplicationContainerCluster cluster) {
+ this.handlers = cluster.getHandlers();
+ this.servlets = cluster.getAllServlets();
return this;
}
public AccessControl build() {
- return new AccessControl(domain, applicationId, writeEnabled, readEnabled,
- excludeBindings, vespaDomain, servlets, handlers, logger);
+ return new AccessControl(domain, writeEnabled, readEnabled,
+ excludeBindings, servlets, handlers, logger);
}
}
public final String domain;
- public final String applicationId;
public final boolean readEnabled;
public final boolean writeEnabled;
- public final Optional<String> vespaDomain;
private final Set<String> excludedBindings;
private final Collection<Handler<?>> handlers;
private final Collection<Servlet> servlets;
private final DeployLogger logger;
private AccessControl(String domain,
- String applicationId,
boolean writeEnabled,
boolean readEnabled,
Set<String> excludedBindings,
- Optional<String> vespaDomain,
Collection<Servlet> servlets,
Collection<Handler<?>> handlers,
DeployLogger logger) {
this.domain = domain;
- this.applicationId = applicationId;
this.readEnabled = readEnabled;
this.writeEnabled = writeEnabled;
this.excludedBindings = Collections.unmodifiableSet(excludedBindings);
- this.vespaDomain = vespaDomain;
this.handlers = handlers;
this.servlets = servlets;
this.logger = logger;
@@ -125,6 +108,10 @@ public final class AccessControl {
.collect(Collectors.toCollection(ArrayList::new));
}
+ public static boolean hasHandlerThatNeedsProtection(ApplicationContainerCluster cluster) {
+ return cluster.getHandlers().stream().anyMatch(AccessControl::handlerNeedsProtection);
+ }
+
private Stream<Binding> getHandlerBindings() {
return handlers.stream()
.filter(this::shouldHandlerBeProtected)
@@ -144,7 +131,7 @@ public final class AccessControl {
&& handler.getServerBindings().stream().noneMatch(excludedBindings::contains);
}
- public static boolean isBuiltinGetOnly(Handler<?> handler) {
+ private static boolean isBuiltinGetOnly(Handler<?> handler) {
return UNPROTECTED_HANDLERS.contains(handler.getClassId().getName());
}
@@ -159,4 +146,13 @@ public final class AccessControl {
private static Stream<String> servletBindings(Servlet servlet) {
return Stream.of("http://*/").map(protocol -> protocol + servlet.bindingPath);
}
+
+ private static boolean handlerNeedsProtection(Handler<?> handler) {
+ return ! isBuiltinGetOnly(handler) && hasNonMbusBinding(handler);
+ }
+
+ private static boolean hasNonMbusBinding(Handler<?> handler) {
+ return handler.getServerBindings().stream().anyMatch(binding -> ! binding.startsWith("mbus"));
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java
index 400ddf80cf9..0fcf7b2d06c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/Http.java
@@ -8,46 +8,39 @@ import com.yahoo.vespa.model.container.component.chain.Chain;
import com.yahoo.vespa.model.container.component.chain.ChainedComponent;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Represents the http servers and filters of a container cluster.
*
* @author Tony Vaagenes
+ * @author bjorncs
*/
public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> implements ServerConfig.Producer {
- private FilterChains filterChains;
- private JettyHttpServer httpServer;
- private List<Binding> bindings;
- private final Optional<AccessControl> accessControl;
+ private final FilterChains filterChains;
+ private final List<Binding> bindings = new CopyOnWriteArrayList<>();
+ private volatile JettyHttpServer httpServer;
+ private volatile AccessControl accessControl;
- public Http(List<Binding> bindings) {
- this(bindings, null);
+ public Http(FilterChains chains) {
+ super("http");
+ this.filterChains = chains;
}
- public Http(List<Binding> bindings, AccessControl accessControl) {
- super( "http");
- this.bindings = Collections.unmodifiableList(bindings);
- this.accessControl = Optional.ofNullable(accessControl);
- }
-
- public void setFilterChains(FilterChains filterChains) {
- this.filterChains = filterChains;
- }
-
- public void setBindings(List<Binding> bindings) {
- this.bindings = Collections.unmodifiableList(bindings);
+ public void setAccessControl(AccessControl accessControl) {
+ if (this.accessControl != null) throw new IllegalStateException("Access control already assigned");
+ this.accessControl = accessControl;
}
public FilterChains getFilterChains() {
return filterChains;
}
- public JettyHttpServer getHttpServer() {
- return httpServer;
+ public Optional<JettyHttpServer> getHttpServer() {
+ return Optional.ofNullable(httpServer);
}
public void setHttpServer(JettyHttpServer newServer) {
@@ -76,25 +69,21 @@ public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> impl
}
public Optional<AccessControl> getAccessControl() {
- return accessControl;
+ return Optional.ofNullable(accessControl);
}
@Override
public void getConfig(ServerConfig.Builder builder) {
for (Binding binding : bindings) {
builder.filter(new ServerConfig.Filter.Builder()
- .id(binding.filterId().stringValue())
- .binding(binding.binding()));
+ .id(binding.filterId().stringValue())
+ .binding(binding.binding()));
}
}
@Override
public void validate() {
- validate(bindings);
- }
-
- void validate(Collection<Binding> bindings) {
- if (bindings.isEmpty()) return;
+ if (((Collection<Binding>) bindings).isEmpty()) return;
if (filterChains == null)
throw new IllegalArgumentException("Null FilterChains are not allowed when there are filter bindings");
@@ -107,5 +96,4 @@ public class Http extends AbstractConfigProducer<AbstractConfigProducer<?>> impl
throw new RuntimeException("Can't find filter " + binding.filterId() + " for binding " + binding.binding());
}
}
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredFilebasedSslProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredFilebasedSslProvider.java
index 4f84a01ff94..4a331718985 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredFilebasedSslProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredFilebasedSslProvider.java
@@ -8,6 +8,7 @@ import com.yahoo.jdisc.http.ssl.impl.ConfiguredSslContextFactoryProvider;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.model.container.component.SimpleComponent;
+import java.util.List;
import java.util.Optional;
import static com.yahoo.component.ComponentSpecification.fromString;
@@ -16,6 +17,7 @@ import static com.yahoo.component.ComponentSpecification.fromString;
* Configure SSL using file references
*
* @author mortent
+ * @author bjorncs
*/
public class ConfiguredFilebasedSslProvider extends SimpleComponent implements ConnectorConfig.Producer {
public static final String COMPONENT_ID_PREFIX = "configured-ssl-provider@";
@@ -26,8 +28,16 @@ public class ConfiguredFilebasedSslProvider extends SimpleComponent implements C
private final String certificatePath;
private final String caCertificatePath;
private final ConnectorConfig.Ssl.ClientAuth.Enum clientAuthentication;
+ private final List<String> cipherSuites;
+ private final List<String> protocolVersions;
- public ConfiguredFilebasedSslProvider(String servername, String privateKeyPath, String certificatePath, String caCertificatePath, String clientAuthentication) {
+ public ConfiguredFilebasedSslProvider(String servername,
+ String privateKeyPath,
+ String certificatePath,
+ String caCertificatePath,
+ String clientAuthentication,
+ List<String> cipherSuites,
+ List<String> protocolVersions) {
super(new ComponentModel(
new BundleInstantiationSpecification(new ComponentId(COMPONENT_ID_PREFIX+servername),
fromString(COMPONENT_CLASS),
@@ -36,15 +46,21 @@ public class ConfiguredFilebasedSslProvider extends SimpleComponent implements C
this.certificatePath = certificatePath;
this.caCertificatePath = caCertificatePath;
this.clientAuthentication = mapToConfigEnum(clientAuthentication);
+ this.cipherSuites = cipherSuites;
+ this.protocolVersions = protocolVersions;
}
@Override
public void getConfig(ConnectorConfig.Builder builder) {
- builder.ssl.enabled(true);
- builder.ssl.privateKeyFile(privateKeyPath);
- builder.ssl.certificateFile(certificatePath);
- builder.ssl.caCertificateFile(Optional.ofNullable(caCertificatePath).orElse(""));
- builder.ssl.clientAuth(clientAuthentication);
+ builder.ssl(
+ new ConnectorConfig.Ssl.Builder()
+ .enabled(true)
+ .privateKeyFile(privateKeyPath)
+ .certificateFile(certificatePath)
+ .caCertificateFile(Optional.ofNullable(caCertificatePath).orElse(""))
+ .clientAuth(clientAuthentication)
+ .enabledCipherSuites(cipherSuites)
+ .enabledProtocols(protocolVersions));
}
public SimpleComponent getComponent() {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
index 12db3b87243..fb8e9dffbbb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
@@ -7,6 +7,7 @@ import com.yahoo.jdisc.http.ConnectorConfig.Ssl.ClientAuth;
import com.yahoo.vespa.model.container.component.SimpleComponent;
import com.yahoo.vespa.model.container.http.ConnectorFactory;
+import java.time.Duration;
import java.util.List;
/**
@@ -17,42 +18,49 @@ import java.util.List;
public class HostedSslConnectorFactory extends ConnectorFactory {
private static final List<String> INSECURE_WHITELISTED_PATHS = List.of("/status.html");
+ private static final String DEFAULT_HOSTED_TRUSTSTORE = "/opt/yahoo/share/ssl/certs/athenz_certificate_bundle.pem";
private final boolean enforceClientAuth;
+ private final String proxyProtocol;
/**
- * Create connector factory that uses a certificate provided by the config-model / configserver.
+ * Create connector factory that uses a certificate provided by the config-model / configserver and default hosted Vespa truststore.
*/
- public static HostedSslConnectorFactory withProvidedCertificate(String serverName, EndpointCertificateSecrets endpointCertificateSecrets) {
- return new HostedSslConnectorFactory(createConfiguredDirectSslProvider(serverName, endpointCertificateSecrets, /*tlsCaCertificates*/null), false);
+ // TODO Enforce client authentication
+ public static HostedSslConnectorFactory withProvidedCertificate(String proxyProtocol, String serverName, EndpointCertificateSecrets endpointCertificateSecrets) {
+ return new HostedSslConnectorFactory(proxyProtocol,
+ createConfiguredDirectSslProvider(serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null), false);
}
/**
* Create connector factory that uses a certificate provided by the config-model / configserver and a truststore configured by the application.
*/
- public static HostedSslConnectorFactory withProvidedCertificateAndTruststore(String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates) {
- return new HostedSslConnectorFactory(createConfiguredDirectSslProvider(serverName, endpointCertificateSecrets, tlsCaCertificates), true);
+ public static HostedSslConnectorFactory withProvidedCertificateAndTruststore(
+ String proxyProtocol, String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates) {
+ return new HostedSslConnectorFactory(proxyProtocol,
+ createConfiguredDirectSslProvider(serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates), true);
}
/**
* Create connector factory that uses the default certificate and truststore provided by Vespa (through Vespa-global TLS configuration).
*/
- public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName) {
- return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true);
+ public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String proxyProtocol, String serverName) {
+ return new HostedSslConnectorFactory(proxyProtocol, new DefaultSslProvider(serverName), true);
}
- private HostedSslConnectorFactory(SimpleComponent sslProviderComponent, boolean enforceClientAuth) {
+ private HostedSslConnectorFactory(String proxyProtocol, SimpleComponent sslProviderComponent, boolean enforceClientAuth) {
super("tls4443", 4443, sslProviderComponent);
+ this.proxyProtocol = proxyProtocol;
this.enforceClientAuth = enforceClientAuth;
}
private static ConfiguredDirectSslProvider createConfiguredDirectSslProvider(
- String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates) {
+ String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificatesPath, String tlsCaCertificates) {
return new ConfiguredDirectSslProvider(
serverName,
endpointCertificateSecrets.key(),
endpointCertificateSecrets.certificate(),
- /*caCertificatePath*/null,
+ tlsCaCertificatesPath,
tlsCaCertificates,
ClientAuth.Enum.WANT_AUTH);
}
@@ -60,9 +68,27 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
@Override
public void getConfig(ConnectorConfig.Builder connectorBuilder) {
super.getConfig(connectorBuilder);
- connectorBuilder.tlsClientAuthEnforcer(new ConnectorConfig.TlsClientAuthEnforcer.Builder()
- .pathWhitelist(INSECURE_WHITELISTED_PATHS)
- .enable(enforceClientAuth));
+ connectorBuilder
+ .tlsClientAuthEnforcer(new ConnectorConfig.TlsClientAuthEnforcer.Builder()
+ .pathWhitelist(INSECURE_WHITELISTED_PATHS)
+ .enable(enforceClientAuth))
+ .proxyProtocol(configureProxyProtocol())
+ .idleTimeout(Duration.ofMinutes(3).toSeconds())
+ .maxConnectionLife(Duration.ofMinutes(10).toSeconds());
+ }
+
+ private ConnectorConfig.ProxyProtocol.Builder configureProxyProtocol() {
+ ConnectorConfig.ProxyProtocol.Builder proxyProtocolBuilder = new ConnectorConfig.ProxyProtocol.Builder();
+ switch (proxyProtocol) {
+ case "https-only":
+ return proxyProtocolBuilder.enabled(false).mixedMode(false);
+ case "https+proxy-protocol":
+ return proxyProtocolBuilder.enabled(true).mixedMode(true);
+ case "proxy-protocol-only":
+ return proxyProtocolBuilder.enabled(true).mixedMode(false);
+ default:
+ throw new IllegalArgumentException("Unknown proxy-protocol settings: " + proxyProtocol);
+ }
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
index b37caf22216..bfde9b9add1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
@@ -6,19 +6,18 @@ import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.builder.xml.XmlHelper;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.text.XML;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder;
-import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.ApplicationContainerCluster;
+import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.component.chain.Chain;
import com.yahoo.vespa.model.container.http.AccessControl;
+import com.yahoo.vespa.model.container.http.Binding;
import com.yahoo.vespa.model.container.http.FilterChains;
import com.yahoo.vespa.model.container.http.Http;
-import com.yahoo.vespa.model.container.http.Binding;
import org.w3c.dom.Element;
import java.util.ArrayList;
@@ -55,24 +54,18 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http>
filterChains = new FilterChainsBuilder().newChainsInstance(ancestor);
}
- Http http = new Http(bindings, accessControl);
- http.setFilterChains(filterChains);
-
- buildHttpServers(deployState, ancestor, http, spec);
-
+ Http http = new Http(filterChains);
+ http.getBindings().addAll(bindings);
+ http.setAccessControl(accessControl);
+ http.setHttpServer(new JettyHttpServerBuilder().build(deployState, ancestor, spec));
return http;
}
private AccessControl buildAccessControl(DeployState deployState, AbstractConfigProducer ancestor, Element accessControlElem) {
- String application = XmlHelper.getOptionalChildValue(accessControlElem, "application")
- .orElse(getDeployedApplicationId(deployState, ancestor).value());
-
- AccessControl.Builder builder = new AccessControl.Builder(accessControlElem.getAttribute("domain"), application, deployState.getDeployLogger());
+ AthenzDomain domain = getAccessControlDomain(deployState, accessControlElem);
+ AccessControl.Builder builder = new AccessControl.Builder(domain.value(), deployState.getDeployLogger());
- getContainerCluster(ancestor).ifPresent(cluster -> {
- builder.setHandlers(cluster.getHandlers());
- builder.setServlets(cluster.getAllServlets());
- });
+ getContainerCluster(ancestor).ifPresent(builder::setHandlers);
XmlHelper.getOptionalAttribute(accessControlElem, "read").ifPresent(
readAttr -> builder.readEnabled(Boolean.valueOf(readAttr)));
@@ -85,17 +78,29 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http>
.map(XML::getValue)
.forEach(builder::excludeBinding);
}
- XmlHelper.getOptionalChildValue(accessControlElem, "vespa-domain").ifPresent(builder::vespaDomain);
return builder.build();
}
- /**
- * Returns the id of the deployed application, or the default value if not explicitly set (self-hosted).
- */
- private static ApplicationName getDeployedApplicationId(DeployState deployState, AbstractConfigProducer ancestor) {
- return getContainerCluster(ancestor)
- .map(cluster -> deployState.getProperties().applicationId().application())
- .orElse(ApplicationId.defaultId().application());
+ // TODO Fail if domain is not provided through deploy properties
+ private static AthenzDomain getAccessControlDomain(DeployState deployState, Element accessControlElem) {
+ AthenzDomain tenantDomain = deployState.getProperties().athenzDomain().orElse(null);
+ AthenzDomain explicitDomain = XmlHelper.getOptionalAttribute(accessControlElem, "domain")
+ .map(AthenzDomain::from)
+ .orElse(null);
+ if (tenantDomain == null) {
+ if (explicitDomain == null) {
+ throw new IllegalStateException("No Athenz domain provided for 'access-control'");
+ }
+ deployState.getDeployLogger().log(Level.WARNING, "Athenz tenant is not provided by deploy call. This will soon be handled as failure.");
+ }
+ if (explicitDomain != null) {
+ if (tenantDomain != null && !explicitDomain.equals(tenantDomain)) {
+ throw new IllegalArgumentException(
+ String.format("Domain in access-control ('%s') does not match tenant domain ('%s')", explicitDomain.value(), tenantDomain.value()));
+ }
+ deployState.getDeployLogger().log(Level.WARNING, "Domain in 'access-control' is deprecated and will be removed soon");
+ }
+ return tenantDomain != null ? tenantDomain : explicitDomain;
}
private static Optional<ApplicationContainerCluster> getContainerCluster(AbstractConfigProducer configProducer) {
@@ -125,10 +130,6 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http>
return result;
}
- private void buildHttpServers(DeployState deployState, AbstractConfigProducer ancestor, Http http, Element spec) {
- http.setHttpServer(new JettyHttpServerBuilder().build(deployState, ancestor, spec));
- }
-
static int readPort(ModelElement spec, boolean isHosted, DeployLogger logger) {
Integer port = spec.integerAttribute("port");
if (port == null)
@@ -139,13 +140,9 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http>
int legalPortInHostedVespa = Container.BASEPORT;
if (isHosted && port != legalPortInHostedVespa && ! spec.booleanAttribute("required", false)) {
- // TODO: After January 2020:
- // - Set required='true' for the http server on port 4443 in the tester services.xml in InternalStepRunner
- // - Enable 2 currently ignored tests in this module
- // - throw IllegalArgumentException here instead of warning
- logger.log(Level.WARNING, "Illegal port " + port + " in http server '" +
- spec.stringAttribute("id") + "'" +
- ": Port must be set to " + legalPortInHostedVespa);
+ throw new IllegalArgumentException("Illegal port " + port + " in http server '" +
+ spec.stringAttribute("id") + "'" +
+ ": Port must be set to " + legalPortInHostedVespa);
}
return port;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java
index db831a1ec2f..562026ab4dd 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java
@@ -9,13 +9,17 @@ import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder;
import com.yahoo.vespa.model.container.component.SimpleComponent;
import com.yahoo.vespa.model.container.http.ConnectorFactory;
-import com.yahoo.vespa.model.container.http.ssl.CustomSslProvider;
import com.yahoo.vespa.model.container.http.ssl.ConfiguredFilebasedSslProvider;
+import com.yahoo.vespa.model.container.http.ssl.CustomSslProvider;
import com.yahoo.vespa.model.container.http.ssl.DefaultSslProvider;
import org.w3c.dom.Element;
+import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
+import static java.util.stream.Collectors.toList;
+
/**
* @author Einar M R Rosenvinge
* @author mortent
@@ -40,12 +44,16 @@ public class JettyConnectorBuilder extends VespaDomBuilder.DomConfigProducerBuil
String certificateFile = XML.getValue(XML.getChild(sslConfigurator, "certificate-file"));
Optional<String> caCertificateFile = XmlHelper.getOptionalChildValue(sslConfigurator, "ca-certificates-file");
Optional<String> clientAuthentication = XmlHelper.getOptionalChildValue(sslConfigurator, "client-authentication");
+ List<String> cipherSuites = extractOptionalCommaSeparatedList(sslConfigurator, "cipher-suites");
+ List<String> protocols = extractOptionalCommaSeparatedList(sslConfigurator, "protocols");
return new ConfiguredFilebasedSslProvider(
serverName,
privateKeyFile,
certificateFile,
caCertificateFile.orElse(null),
- clientAuthentication.orElse(null));
+ clientAuthentication.orElse(null),
+ cipherSuites,
+ protocols);
} else if (sslProviderConfigurator != null) {
String className = sslProviderConfigurator.getAttribute("class");
String bundle = sslProviderConfigurator.getAttribute("bundle");
@@ -55,4 +63,13 @@ public class JettyConnectorBuilder extends VespaDomBuilder.DomConfigProducerBuil
}
}
+ private static List<String> extractOptionalCommaSeparatedList(Element sslElement, String listElementName) {
+ return XmlHelper.getOptionalChildValue(sslElement, listElementName)
+ .map(element ->
+ Arrays.stream(element.split(","))
+ .filter(listEntry -> !listEntry.isBlank())
+ .map(String::trim)
+ .collect(toList()))
+ .orElse(List.of());
+ }
}
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 e19d81e7fb2..4c4b1ca7f82 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
@@ -58,7 +58,8 @@ public class ContainerSearch extends ContainerSubsystem<SearchChains>
private void initializeDispatchers(Collection<AbstractSearchCluster> searchClusters) {
for (AbstractSearchCluster searchCluster : searchClusters) {
if ( ! ( searchCluster instanceof IndexedSearchCluster)) continue;
- owningCluster.addComponent(new DispatcherComponent((IndexedSearchCluster)searchCluster));
+ var dispatcher = new DispatcherComponent((IndexedSearchCluster)searchCluster);
+ owningCluster.addComponent(dispatcher);
}
}
@@ -130,7 +131,7 @@ public class ContainerSearch extends ContainerSubsystem<SearchChains>
AbstractSearchCluster sys = findClusterWithId(searchClusters, i);
QrSearchersConfig.Searchcluster.Builder scB = new QrSearchersConfig.Searchcluster.Builder().
name(sys.getClusterName());
- for (AbstractSearchCluster.SearchDefinitionSpec spec : sys.getLocalSDS()) {
+ for (AbstractSearchCluster.SchemaSpec spec : sys.getLocalSDS()) {
scB.searchdef(spec.getSearchDefinition().getName());
}
scB.rankprofiles(new QrSearchersConfig.Searchcluster.Rankprofiles.Builder().configid(sys.getConfigId()));
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 704188e80e8..284aa3b46c0 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
@@ -1,6 +1,7 @@
// Copyright 2019 Oath Inc. 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.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.config.search.DispatchConfig;
import com.yahoo.vespa.model.container.component.Component;
@@ -13,7 +14,7 @@ import com.yahoo.vespa.model.search.IndexedSearchCluster;
*
* @author bratseth
*/
-public class DispatcherComponent extends Component<DispatcherComponent, ComponentModel>
+public class DispatcherComponent extends Component<AbstractConfigProducer<?>, ComponentModel>
implements DispatchConfig.Producer {
private final IndexedSearchCluster indexedSearchCluster;
@@ -21,14 +22,17 @@ public class DispatcherComponent extends Component<DispatcherComponent, Componen
public DispatcherComponent(IndexedSearchCluster indexedSearchCluster) {
super(toComponentModel(indexedSearchCluster));
this.indexedSearchCluster = indexedSearchCluster;
+ String clusterName = indexedSearchCluster.getClusterName();
+ var rpcResoucePool = new RpcResourcePoolComponent(clusterName);
+ inject(rpcResoucePool);
+ addComponent(rpcResoucePool);
}
private static ComponentModel toComponentModel(IndexedSearchCluster indexedSearchCluster) {
String dispatcherComponentId = "dispatcher." + indexedSearchCluster.getClusterName(); // used by ClusterSearcher
return new ComponentModel(dispatcherComponentId,
- "com.yahoo.search.dispatch.Dispatcher",
- BundleMapper.searchAndDocprocBundle,
- null);
+ com.yahoo.search.dispatch.Dispatcher.class.getName(),
+ BundleMapper.searchAndDocprocBundle);
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java
index 0abb0803405..0a9618e7b08 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java
@@ -232,8 +232,7 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer
return propB;
}
- private QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder createVariantPropertyFieldConfig(
- String fullName, Object value) {
+ private QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder createVariantPropertyFieldConfig(String fullName, Object value) {
QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder propB = new QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder();
if (value instanceof SubstituteString)
value=value.toString(); // Send only types understood by configBuilder downwards
@@ -251,7 +250,7 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer
qtB.matchaspath(true);
for (QueryProfileType inherited : profileType.inherited())
qtB.inherit(inherited.getId().stringValue());
- List<FieldDescription> fields=new ArrayList<>(profileType.declaredFields().values());
+ List<FieldDescription> fields = new ArrayList<>(profileType.declaredFields().values());
Collections.sort(fields);
for (FieldDescription field : fields)
qtB.field(createConfig(field));
@@ -260,22 +259,20 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer
private QueryProfilesConfig.Queryprofiletype.Field.Builder createConfig(FieldDescription field) {
QueryProfilesConfig.Queryprofiletype.Field.Builder fB = new QueryProfilesConfig.Queryprofiletype.Field.Builder();
- fB.
- name(field.getName()).
- type(field.getType().stringValue());
+ fB.name(field.getName()).type(field.getType().stringValue());
if ( ! field.isOverridable())
fB.overridable(false);
if (field.isMandatory())
fB.mandatory(true);
- String aliases=toSpaceSeparatedString(field.getAliases());
- if (!aliases.isEmpty())
+ String aliases = toSpaceSeparatedString(field.getAliases());
+ if ( ! aliases.isEmpty())
fB.alias(aliases);
return fB;
}
public String toSpaceSeparatedString(List<String> list) {
- StringBuilder b=new StringBuilder();
- for (Iterator<String> i=list.iterator(); i.hasNext(); ) {
+ StringBuilder b = new StringBuilder();
+ for (Iterator<String> i = list.iterator(); i.hasNext(); ) {
b.append(i.next());
if (i.hasNext())
b.append(" ");
@@ -290,10 +287,7 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer
}
}
- /**
- * The config produced by this
- * @return query profiles config
- */
+ /** Returns the config produced by this */
public QueryProfilesConfig getConfig() {
QueryProfilesConfig.Builder qB = new QueryProfilesConfig.Builder();
getConfig(qB);
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
new file mode 100644
index 00000000000..2689c2ce71b
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java
@@ -0,0 +1,18 @@
+// Copyright 2020 Oath Inc. 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.osgi.provider.model.ComponentModel;
+import com.yahoo.vespa.model.container.component.Component;
+import com.yahoo.vespa.model.container.xml.BundleMapper;
+
+public class RpcResourcePoolComponent extends Component<RpcResourcePoolComponent, ComponentModel> {
+
+ public RpcResourcePoolComponent(String clusterName) {
+ super(toComponentModel(clusterName));
+ }
+
+ private static ComponentModel toComponentModel(String clusterName) {
+ String componentId = "rpcresourcepool." + clusterName;
+ return new ComponentModel(componentId, com.yahoo.search.dispatch.rpc.RpcResourcePool.class.getName(), BundleMapper.searchAndDocprocBundle);
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java
index e05b2d27e09..4ecc666a9f2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/LocalProvider.java
@@ -118,7 +118,7 @@ public class LocalProvider extends Provider implements
public List<String> getDocumentTypes() {
List<String> documentTypes = new ArrayList<>();
- for (AbstractSearchCluster.SearchDefinitionSpec spec : searchCluster.getLocalSDS()) {
+ for (AbstractSearchCluster.SchemaSpec spec : searchCluster.getLocalSDS()) {
documentTypes.add(spec.getSearchDefinition().getSearch().getDocument().getName());
}
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 2828fcf09d0..19d1b6546a6 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
@@ -9,6 +9,8 @@ import org.w3c.dom.Element;
import java.util.Arrays;
import java.util.List;
+import static com.yahoo.vespa.model.container.xml.ContainerModelBuilder.SEARCH_HANDLER_CLASS;
+
/**
* This object builds a bundle instantiation spec from an XML element.
*
@@ -36,7 +38,7 @@ public class BundleInstantiationSpecificationBuilder {
private static void validate(BundleInstantiationSpecification instSpec) {
List<String> forbiddenClasses = Arrays.asList(
- "com.yahoo.search.handler.SearchHandler",
+ SEARCH_HANDLER_CLASS,
"com.yahoo.processing.handler.ProcessingHandler");
for (String forbiddenClass: forbiddenClasses) {
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 aef2697a5dd..4bd9f5fa8b0 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
@@ -19,12 +19,15 @@ import com.yahoo.config.model.builder.xml.ConfigModelBuilder;
import com.yahoo.config.model.builder.xml.ConfigModelId;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AbstractConfigProducer;
+import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.AthenzService;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
+import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
+import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
@@ -57,9 +60,11 @@ import com.yahoo.vespa.model.container.SecretStore;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent;
import com.yahoo.vespa.model.container.component.Handler;
+import com.yahoo.vespa.model.container.component.chain.Chain;
import com.yahoo.vespa.model.container.component.chain.ProcessingHandler;
import com.yahoo.vespa.model.container.docproc.ContainerDocproc;
import com.yahoo.vespa.model.container.docproc.DocprocChains;
+import com.yahoo.vespa.model.container.http.AccessControl;
import com.yahoo.vespa.model.container.http.ConnectorFactory;
import com.yahoo.vespa.model.container.http.FilterChains;
import com.yahoo.vespa.model.container.http.Http;
@@ -88,6 +93,7 @@ import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import static com.yahoo.vespa.model.container.http.AccessControl.ACCESS_CONTROL_CHAIN_ID;
import static java.util.logging.Level.WARNING;
/**
@@ -106,6 +112,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
private static final String DEPRECATED_CONTAINER_TAG = "jdisc";
private static final String ENVIRONMENT_VARIABLES_ELEMENT = "environment-variables";
+ static final String SEARCH_HANDLER_CLASS = com.yahoo.search.handler.SearchHandler.class.getName();
+ static final String SEARCH_HANDLER_BINDING = "http://*/search/*";
+
public enum Networking { disable, enable }
private ApplicationPackage app;
@@ -175,7 +184,6 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
DocumentFactoryBuilder.buildDocumentFactories(cluster, spec);
addConfiguredComponents(deployState, cluster, spec);
addSecretStore(cluster, spec);
- addHandlers(deployState, cluster, spec);
addRestApis(deployState, spec, cluster);
addServlets(deployState, spec, cluster);
@@ -188,8 +196,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
cluster.addDefaultHandlersExceptStatus();
addStatusHandlers(cluster, context.getDeployState().isHosted());
+ addUserHandlers(deployState, cluster, spec);
- addHttp(deployState, spec, cluster, context.getApplicationType(), deployState.getProperties().applicationId().instance().isTester());
+ addHttp(deployState, spec, cluster, context);
addAccessLogs(deployState, cluster, spec);
addRoutingAliases(cluster, spec, deployState.zone().environment());
@@ -287,7 +296,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
private void addClientProviders(DeployState deployState, Element spec, ApplicationContainerCluster cluster) {
for (Element clientSpec: XML.getChildren(spec, "client")) {
- cluster.addComponent(new DomClientProviderBuilder().build(deployState, cluster, clientSpec));
+ cluster.addComponent(new DomClientProviderBuilder(cluster).build(deployState, cluster, clientSpec));
}
}
@@ -302,7 +311,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
AccessLogBuilder.buildIfNotDisabled(deployState, cluster, accessLog).ifPresent(cluster::addComponent);
}
- if (accessLogElements.isEmpty() && cluster.getSearch() != null)
+ if (accessLogElements.isEmpty() && deployState.getAccessLoggingEnabledByDefault())
cluster.addDefaultSearchAccessLog();
}
@@ -311,21 +320,23 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
}
- private void addHttp(DeployState deployState, Element spec, ApplicationContainerCluster cluster, ApplicationType applicationType, boolean isTesterApplication) {
+ private void addHttp(DeployState deployState, Element spec, ApplicationContainerCluster cluster, ConfigModelContext context) {
Element httpElement = XML.getChild(spec, "http");
if (httpElement != null) {
cluster.setHttp(buildHttp(deployState, cluster, httpElement));
}
- if (deployState.isHosted() && applicationType == ApplicationType.DEFAULT && !isTesterApplication) {
+ if (isHostedTenantApplication(context)) {
+ addHostedImplicitHttpIfNotPresent(cluster);
+ addHostedImplicitAccessControlIfNotPresent(deployState, cluster);
addAdditionalHostedConnector(deployState, cluster);
}
}
private void addAdditionalHostedConnector(DeployState deployState, ApplicationContainerCluster cluster) {
- addImplicitHttpIfNotPresent(cluster);
- JettyHttpServer server = cluster.getHttp().getHttpServer();
+ JettyHttpServer server = cluster.getHttp().getHttpServer().get();
String serverName = server.getComponentId().getName();
+ String proxyProtocol = deployState.getProperties().proxyProtocol();
// If the deployment contains certificate/private key reference, setup TLS port
if (deployState.endpointCertificateSecrets().isPresent()) {
boolean authorizeClient = deployState.zone().system().isPublic();
@@ -334,27 +345,47 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
}
EndpointCertificateSecrets endpointCertificateSecrets = deployState.endpointCertificateSecrets().get();
HostedSslConnectorFactory connectorFactory = authorizeClient
- ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(serverName, endpointCertificateSecrets, deployState.tlsClientAuthority().get())
- : HostedSslConnectorFactory.withProvidedCertificate(serverName, endpointCertificateSecrets);
+ ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(proxyProtocol, serverName, endpointCertificateSecrets, deployState.tlsClientAuthority().get())
+ : HostedSslConnectorFactory.withProvidedCertificate(proxyProtocol, serverName, endpointCertificateSecrets);
server.addConnector(connectorFactory);
} else {
- server.addConnector(HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName));
+ server.addConnector(HostedSslConnectorFactory.withDefaultCertificateAndTruststore(proxyProtocol, serverName));
}
}
- private static void addImplicitHttpIfNotPresent(ApplicationContainerCluster cluster) {
+ private static boolean isHostedTenantApplication(ConfigModelContext context) {
+ var deployState = context.getDeployState();
+ boolean isTesterApplication = deployState.getProperties().applicationId().instance().isTester();
+ return deployState.isHosted() && context.getApplicationType() == ApplicationType.DEFAULT && !isTesterApplication;
+ }
+
+ private static void addHostedImplicitHttpIfNotPresent(ApplicationContainerCluster cluster) {
if(cluster.getHttp() == null) {
- Http http = new Http(Collections.emptyList());
- http.setFilterChains(new FilterChains(cluster));
- cluster.setHttp(http);
+ cluster.setHttp(new Http(new FilterChains(cluster)));
}
- if(cluster.getHttp().getHttpServer() == null) {
+ if(cluster.getHttp().getHttpServer().isEmpty()) {
JettyHttpServer defaultHttpServer = new JettyHttpServer(new ComponentId("DefaultHttpServer"));
cluster.getHttp().setHttpServer(defaultHttpServer);
defaultHttpServer.addConnector(new ConnectorFactory("SearchServer", Defaults.getDefaults().vespaWebServicePort()));
}
}
+ private void addHostedImplicitAccessControlIfNotPresent(DeployState deployState, ApplicationContainerCluster cluster) {
+ Http http = cluster.getHttp();
+ if (http.getAccessControl().isPresent()) return; // access control added explicitly
+ AthenzDomain tenantDomain = deployState.getProperties().athenzDomain().orElse(null);
+ if (tenantDomain == null) return; // tenant domain not present, cannot add access control. this should eventually be a failure.
+ AccessControl accessControl =
+ new AccessControl.Builder(tenantDomain.value(), deployState.getDeployLogger())
+ .setHandlers(cluster)
+ .readEnabled(false)
+ .writeEnabled(false)
+ .build();
+ http.getFilterChains().add(new Chain<>(FilterChains.emptyChainSpec(ACCESS_CONTROL_CHAIN_ID)));
+ http.setAccessControl(accessControl);
+ http.getBindings().addAll(accessControl.getBindings());
+ }
+
private Http buildHttp(DeployState deployState, ApplicationContainerCluster cluster, Element httpElement) {
Http http = new HttpBuilder().build(deployState, cluster, httpElement);
@@ -441,10 +472,10 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
containerSearch.setPageTemplates(PageTemplates.create(applicationPackage));
}
- private void addHandlers(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
+ private void addUserHandlers(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
for (Element component: XML.getChildren(spec, "handler")) {
cluster.addComponent(
- new DomHandlerBuilder().build(deployState, cluster, component));
+ new DomHandlerBuilder(cluster).build(deployState, cluster, component));
}
}
@@ -532,14 +563,17 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
cluster.setJvmGCOptions(buildJvmGCOptions(context.getDeployState().zone(), jvmGCOptions, context.getDeployState().isHosted()));
}
+ /**
+ * Add nodes to cluster according to the given containerElement.
+ *
+ * Note: DO NOT change allocation behaviour to allow version X and Y of the config-model to allocate a different set
+ * of nodes. Such changes must be guarded by a common condition (e.g. feature flag) so the behaviour can be changed
+ * simultaneously for all active config models.
+ */
private void addNodesFromXml(ApplicationContainerCluster cluster, Element containerElement, ConfigModelContext context) {
Element nodesElement = XML.getChild(containerElement, "nodes");
- if (nodesElement == null) { // default single node on localhost
- ApplicationContainer node = new ApplicationContainer(cluster, "container.0", 0, cluster.isHostedVespa());
- HostResource host = allocateSingleNodeHost(cluster, log, containerElement, context);
- node.setHostResource(host);
- node.initService(context.getDeployLogger());
- cluster.addContainers(Collections.singleton(node));
+ if (nodesElement == null) {
+ cluster.addContainers(allocateWithoutNodesTag(cluster, context));
} else {
List<ApplicationContainer> nodes = createNodes(cluster, nodesElement, context);
@@ -615,30 +649,33 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
" must be an integer percentage ending by the '%' sign");
}
}
-
- /** Creates a single host when there is no nodes tag */
- private HostResource allocateSingleNodeHost(ApplicationContainerCluster cluster, DeployLogger logger, Element containerElement, ConfigModelContext context) {
+
+ /** Allocate a container cluster without a nodes tag */
+ private List<ApplicationContainer> allocateWithoutNodesTag(ApplicationContainerCluster cluster, ConfigModelContext context) {
DeployState deployState = context.getDeployState();
HostSystem hostSystem = cluster.hostSystem();
if (deployState.isHosted()) {
- Optional<HostResource> singleContentHost = getHostResourceFromContentClusters(cluster, containerElement, context);
- if (singleContentHost.isPresent()) { // there is a content cluster; put the container on its first node
- return singleContentHost.get();
- }
- else { // request 1 node
- ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
- ClusterSpec.Id.from(cluster.getName()),
- deployState.getWantedNodeVespaVersion(),
- false);
- Capacity capacity = Capacity.fromCount(1,
- Optional.empty(),
- false,
- ! deployState.getProperties().isBootstrap());
- return hostSystem.allocateHosts(clusterSpec, capacity, 1, logger).keySet().iterator().next();
- }
- } else {
- return hostSystem.getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC);
+ // request just enough nodes to satisfy environment capacity requirement
+ ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
+ ClusterSpec.Id.from(cluster.getName()))
+ .vespaVersion(deployState.getWantedNodeVespaVersion())
+ .dockerImageRepo(deployState.getWantedDockerImageRepo())
+ .build();
+ int nodeCount = deployState.zone().environment().isProduction() ? 2 : 1;
+ Capacity capacity = Capacity.from(new ClusterResources(nodeCount, 1, NodeResources.unspecified),
+ false,
+ !deployState.getProperties().isBootstrap());
+ var hosts = hostSystem.allocateHosts(clusterSpec, capacity, log);
+ return createNodesFromHosts(log, hosts, cluster);
}
+ return singleHostContainerCluster(cluster, hostSystem.getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC), context);
+ }
+
+ private List<ApplicationContainer> singleHostContainerCluster(ApplicationContainerCluster cluster, HostResource host, ConfigModelContext context) {
+ ApplicationContainer node = new ApplicationContainer(cluster, "container.0", 0, cluster.isHostedVespa());
+ node.setHostResource(host);
+ node.initService(context.getDeployLogger());
+ return List.of(node);
}
private List<ApplicationContainer> createNodesFromNodeCount(ApplicationContainerCluster cluster, Element nodesElement, ConfigModelContext context) {
@@ -652,13 +689,13 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
private List<ApplicationContainer> createNodesFromNodeType(ApplicationContainerCluster cluster, Element nodesElement, ConfigModelContext context) {
NodeType type = NodeType.valueOf(nodesElement.getAttribute("type"));
- ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
- ClusterSpec.Id.from(cluster.getName()),
- context.getDeployState().getWantedNodeVespaVersion(),
- false);
+ ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from(cluster.getName()))
+ .vespaVersion(context.getDeployState().getWantedNodeVespaVersion())
+ .dockerImageRepo(context.getDeployState().getWantedDockerImageRepo())
+ .build();
Map<HostResource, ClusterMembership> hosts =
cluster.getRoot().hostSystem().allocateHosts(clusterSpec,
- Capacity.fromRequiredNodeType(type), 1, log);
+ Capacity.fromRequiredNodeType(type), log);
return createNodesFromHosts(context.getDeployLogger(), hosts, cluster);
}
@@ -766,7 +803,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
ProcessingHandler<SearchChains> searchHandler = new ProcessingHandler<>(cluster.getSearch().getChains(),
"com.yahoo.search.handler.SearchHandler");
- String[] defaultBindings = {"http://*/search/*"};
+ String[] defaultBindings = {SEARCH_HANDLER_BINDING};
for (String binding: serverBindings(searchElement, defaultBindings)) {
searchHandler.addServerBindings(binding);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
index 75161859068..fcaba66ef69 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
@@ -16,8 +16,8 @@ import com.yahoo.vespa.model.search.AbstractSearchCluster;
import com.yahoo.vespa.model.search.IndexedSearchCluster;
import com.yahoo.vespa.model.search.NodeSpec;
import com.yahoo.vespa.model.search.SearchCluster;
-import com.yahoo.vespa.model.search.SearchDefinition;
-import com.yahoo.vespa.model.search.SearchDefinitionXMLHandler;
+import com.yahoo.vespa.model.search.NamedSchema;
+import com.yahoo.vespa.model.search.SchemaDefinitionXMLHandler;
import com.yahoo.vespa.model.search.SearchNode;
import com.yahoo.vespa.model.search.StreamingSearchCluster;
import com.yahoo.vespa.model.search.TransactionLogServer;
@@ -52,6 +52,7 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
private final String clusterName;
private final Map<String, NewDocumentType> documentDefinitions;
private final Set<NewDocumentType> globallyDistributedDocuments;
+ private Double visibilityDelay = 0.0;
/** The search nodes of this if it does not have an indexed cluster */
private List<SearchNode> nonIndexed = new ArrayList<>();
@@ -135,21 +136,21 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
private void buildIndexedSearchCluster(DeployState deployState, ModelElement clusterElem,
String clusterName, ContentSearchCluster search) {
- List<ModelElement> indexedDefs = getIndexedSearchDefinitions(clusterElem);
+ List<ModelElement> indexedDefs = getIndexedSchemas(clusterElem);
if (!indexedDefs.isEmpty()) {
IndexedSearchCluster isc = new IndexedSearchCluster(search, clusterName, 0, deployState);
isc.setRoutingSelector(clusterElem.childAsString("documents.selection"));
Double visibilityDelay = clusterElem.childAsDouble("engine.proton.visibility-delay");
if (visibilityDelay != null) {
- isc.setVisibilityDelay(visibilityDelay);
+ search.setVisibilityDelay(visibilityDelay);
}
search.addSearchCluster(deployState, isc, getQueryTimeout(clusterElem), indexedDefs);
}
}
- private List<ModelElement> getIndexedSearchDefinitions(ModelElement clusterElem) {
+ private List<ModelElement> getIndexedSchemas(ModelElement clusterElem) {
List<ModelElement> indexedDefs = new ArrayList<>();
ModelElement docElem = clusterElem.child("documents");
if (docElem == null) {
@@ -179,29 +180,36 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
this.flushOnShutdown = flushOnShutdown;
}
+ public void setVisibilityDelay(double delay) {
+ this.visibilityDelay=delay;
+ if (hasIndexedCluster()) {
+ indexedCluster.setVisibilityDelay(delay);
+ }
+ }
+
private void addSearchCluster(DeployState deployState, SearchCluster cluster, Double queryTimeout, List<ModelElement> documentDefs) {
- addSearchDefinitions(deployState, documentDefs, cluster);
+ addSchemas(deployState, documentDefs, cluster);
if (queryTimeout != null) {
cluster.setQueryTimeout(queryTimeout);
}
cluster.defaultDocumentsConfig();
- cluster.deriveSearchDefinitions(deployState);
+ cluster.deriveSchemas(deployState);
addCluster(cluster);
}
- private void addSearchDefinitions(DeployState deployState, List<ModelElement> searchDefs, AbstractSearchCluster sc) {
+ private void addSchemas(DeployState deployState, List<ModelElement> searchDefs, AbstractSearchCluster sc) {
for (ModelElement e : searchDefs) {
- SearchDefinitionXMLHandler searchDefinitionXMLHandler = new SearchDefinitionXMLHandler(e);
- SearchDefinition searchDefinition =
- searchDefinitionXMLHandler.getResponsibleSearchDefinition(deployState.getSearchDefinitions());
+ SchemaDefinitionXMLHandler schemaDefinitionXMLHandler = new SchemaDefinitionXMLHandler(e);
+ NamedSchema searchDefinition =
+ schemaDefinitionXMLHandler.getResponsibleSearchDefinition(deployState.getSchemas());
if (searchDefinition == null)
throw new RuntimeException("Search definition parsing error or file does not exist: '" +
- searchDefinitionXMLHandler.getName() + "'");
+ schemaDefinitionXMLHandler.getName() + "'");
// TODO: remove explicit building of user configs when the complete content model is built using builders.
- sc.getLocalSDS().add(new AbstractSearchCluster.SearchDefinitionSpec(searchDefinition,
- UserConfigBuilder.build(e.getXml(), deployState, deployState.getDeployLogger())));
+ sc.getLocalSDS().add(new AbstractSearchCluster.SchemaSpec(searchDefinition,
+ UserConfigBuilder.build(e.getXml(), deployState, deployState.getDeployLogger())));
//need to get the document names from this sdfile
sc.addDocumentNames(searchDefinition);
}
@@ -307,7 +315,6 @@ public class ContentSearchCluster extends AbstractConfigProducer implements Prot
@Override
public void getConfig(ProtonConfig.Builder builder) {
- double visibilityDelay = hasIndexedCluster() ? getIndexed().getVisibilityDelay() : 0.0;
builder.feeding.concurrency(0.40); // As if specified 0.8 in services.xml
boolean hasAnyNonIndexedCluster = false;
for (NewDocumentType type : TopologicalDocumentTypeSorter.sort(documentDefinitions.values())) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java b/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java
index 0d15207b6ce..0f9eb5341ab 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchTuning.java
@@ -11,18 +11,25 @@ public class DispatchTuning {
public static final DispatchTuning empty = new DispatchTuning.Builder().build();
- public enum DispatchPolicy { ROUNDROBIN, ADAPTIVE};
+ public enum DispatchPolicy { ROUNDROBIN, ADAPTIVE}
private final Integer maxHitsPerPartition;
private DispatchPolicy dispatchPolicy;
private final Double minGroupCoverage;
private final Double minActiveDocsCoverage;
+ public Double getTopkProbability() {
+ return topkProbability;
+ }
+
+ private final Double topkProbability;
+
private DispatchTuning(Builder builder) {
maxHitsPerPartition = builder.maxHitsPerPartition;
dispatchPolicy = builder.dispatchPolicy;
minGroupCoverage = builder.minGroupCoverage;
minActiveDocsCoverage = builder.minActiveDocsCoverage;
+ topkProbability = builder.topKProbability;
}
/** Returns the max number of hits to fetch from each partition, or null to fetch all */
@@ -46,6 +53,7 @@ public class DispatchTuning {
private DispatchPolicy dispatchPolicy;
private Double minGroupCoverage;
private Double minActiveDocsCoverage;
+ private Double topKProbability;
public DispatchTuning build() {
return new DispatchTuning(this);
@@ -55,6 +63,10 @@ public class DispatchTuning {
this.maxHitsPerPartition = maxHitsPerPartition;
return this;
}
+ public Builder setTopKProbability(Double topKProbability) {
+ this.topKProbability = topKProbability;
+ return this;
+ }
public Builder setDispatchPolicy(String policy) {
if (policy != null)
dispatchPolicy = toDispatchPolicy(policy);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
index adfc703f747..f0c374b398f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/StorageGroup.java
@@ -376,6 +376,10 @@ public class StorageGroup {
* specified using a group count attribute.
* <li>Neither element is present: Create a single node.
* </ul>
+ *
+ * Note: DO NOT change allocation behaviour to allow version X and Y of the config-model to allocate a different
+ * set of nodes. Such changes must be guarded by a common condition (e.g. feature flag) so the behaviour can be
+ * changed simultaneously for all active config models.
*/
private GroupBuilder collectGroup(boolean isHosted, Optional<ModelElement> groupElement, Optional<ModelElement> nodesElement, String name, String index) {
StorageGroup group = new StorageGroup(
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
index 9b17412b83a..6dd3e619ec2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
@@ -136,10 +136,7 @@ public class ContentCluster extends AbstractConfigProducer implements
c.rootGroup = new StorageGroup.Builder(contentElement, context).buildRootGroup(deployState, redundancyBuilder, c);
validateThatGroupSiblingsAreUnique(c.clusterName, c.rootGroup);
c.search.handleRedundancy(c.redundancy);
-
- IndexedSearchCluster index = c.search.getIndexed();
- if (index != null)
- setupIndexedCluster(index, contentElement, deployState.getDeployLogger());
+ setupSearchCluster(c.search, contentElement, deployState.getDeployLogger());
if (c.search.hasIndexedCluster() && !(c.persistenceFactory instanceof ProtonEngine.Factory) )
throw new RuntimeException("Indexed search requires proton as engine");
@@ -166,17 +163,25 @@ public class ContentCluster extends AbstractConfigProducer implements
return c;
}
- private void setupIndexedCluster(IndexedSearchCluster index, ModelElement element, DeployLogger logger) {
+ private void setupSearchCluster(ContentSearchCluster csc, ModelElement element, DeployLogger logger) {
ContentSearch search = DomContentSearchBuilder.build(element);
+ Double visibilityDelay = search.getVisibilityDelay();
+ if (visibilityDelay != null) {
+ csc.setVisibilityDelay(visibilityDelay);
+ }
+ if (csc.hasIndexedCluster()) {
+ setupIndexedCluster(csc.getIndexed(), search, element, logger);
+ }
+
+
+ }
+ private void setupIndexedCluster(IndexedSearchCluster index, ContentSearch search, ModelElement element, DeployLogger logger) {
Double queryTimeout = search.getQueryTimeout();
if (queryTimeout != null) {
Preconditions.checkState(index.getQueryTimeout() == null,
- "In " + index + ": You may not specify query-timeout in both proton and content.");
+ "In " + index + ": You may not specify query-timeout in both proton and content.");
index.setQueryTimeout(queryTimeout);
}
- Double visibilityDelay = search.getVisibilityDelay();
- if (visibilityDelay != null)
- index.setVisibilityDelay(visibilityDelay);
index.setSearchCoverage(DomSearchCoverageBuilder.build(element));
index.setDispatchSpec(DomDispatchBuilder.build(element));
@@ -281,7 +286,7 @@ public class ContentCluster extends AbstractConfigProducer implements
.orElse(NodesSpecification.nonDedicated(3, context));
Collection<HostResource> hosts = nodesSpecification.isDedicated() ?
getControllerHosts(nodesSpecification, admin, clusterName, context) :
- drawControllerHosts(nodesSpecification.count(), rootGroup, containers);
+ drawControllerHosts(nodesSpecification.minResources().nodes(), rootGroup, containers);
clusterControllers = createClusterControllers(new ClusterControllerCluster(contentCluster, "standalone"),
hosts,
clusterName,
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilder.java
index b53d66632a8..d599a1a1aca 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomTuningDispatchBuilder.java
@@ -23,6 +23,7 @@ public class DomTuningDispatchBuilder {
return builder.build();
}
builder.setMaxHitsPerPartition(dispatchElement.childAsInteger("max-hits-per-partition"));
+ builder.setTopKProbability(dispatchElement.childAsDouble("top-k-probability"));
builder.setDispatchPolicy(dispatchElement.childAsString("dispatch-policy"));
builder.setMinGroupCoverage(dispatchElement.childAsDouble("min-group-coverage"));
builder.setMinActiveDocsCoverage(dispatchElement.childAsDouble("min-active-docs-coverage"));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributionConfigProducer.java b/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributionConfigProducer.java
index 9662540e8df..2c6808b5773 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributionConfigProducer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributionConfigProducer.java
@@ -20,8 +20,9 @@ public class FileDistributionConfigProducer extends AbstractConfigProducer {
private final Map<Host, FileDistributionConfigProvider> fileDistributionConfigProviders = new IdentityHashMap<>();
private final FileDistributor fileDistributor;
- public FileDistributionConfigProducer(AbstractConfigProducer ancestor, FileRegistry fileRegistry, List<ConfigServerSpec> configServerSpec) {
- this(ancestor, new FileDistributor(fileRegistry, configServerSpec));
+ public FileDistributionConfigProducer(AbstractConfigProducer ancestor, FileRegistry fileRegistry,
+ List<ConfigServerSpec> configServerSpec, boolean isHosted) {
+ this(ancestor, new FileDistributor(fileRegistry, configServerSpec, isHosted));
}
private FileDistributionConfigProducer(AbstractConfigProducer parent, FileDistributor fileDistributor) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java b/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java
index 576b009c846..5ccf86f9ba8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java
@@ -11,7 +11,10 @@ import com.yahoo.vespa.model.Host;
import java.util.*;
/**
- * Responsible for directing distribution of files to hosts.
+ * Sends RPC requests to hosts (tenant hosts and config servers) to start download of files. This is used during prepare
+ * of an application. Services themselves will also request files, the work done in this class is done so that hosts can
+ * start downloading files before services gets new config that needs these files. This also tries to make sure that
+ * all config servers (not just the one where the application was deployed) have the files available.
*
* @author Tony Vaagenes
*/
@@ -19,61 +22,42 @@ public class FileDistributor {
private final FileRegistry fileRegistry;
private final List<ConfigServerSpec> configServerSpecs;
+ private final boolean isHosted;
- /** A map from files to the hosts to which that file should be distributed */
+ /** A map from file reference to the hosts to which that file reference should be distributed */
private final Map<FileReference, Set<Host>> filesToHosts = new LinkedHashMap<>();
+ public FileDistributor(FileRegistry fileRegistry, List<ConfigServerSpec> configServerSpecs, boolean isHosted) {
+ this.fileRegistry = fileRegistry;
+ this.configServerSpecs = configServerSpecs;
+ this.isHosted = isHosted;
+ }
+
/**
* Adds the given file to the associated application packages' registry of file and marks the file
- * for distribution to the given hosts.
+ * for distribution to the given host.
* <b>Note: This class receives ownership of the given collection.</b>
*
* @return the reference to the file, created by the application package
*/
- public FileReference sendFileToHosts(String relativePath, Collection<Host> hosts) {
- FileReference reference = fileRegistry.addFile(relativePath);
- addToFilesToDistribute(reference, hosts);
-
- return reference;
+ public FileReference sendFileToHost(String relativePath, Host host) {
+ return addFileReference(fileRegistry.addFile(relativePath), host);
}
/**
* Adds the given file to the associated application packages' registry of file and marks the file
- * for distribution to the given hosts.
+ * for distribution to the given host.
* <b>Note: This class receives ownership of the given collection.</b>
*
* @return the reference to the file, created by the application package
*/
- public FileReference sendUriToHosts(String uri, Collection<Host> hosts) {
- FileReference reference = fileRegistry.addUri(uri);
- if (reference != null) {
- addToFilesToDistribute(reference, hosts);
- }
-
- return reference;
- }
-
- /** Same as sendFileToHost(relativePath,Collections.singletonList(host) */
- public FileReference sendFileToHost(String relativePath, Host host) {
- return sendFileToHosts(relativePath, Arrays.asList(host));
- }
-
public FileReference sendUriToHost(String uri, Host host) {
- return sendUriToHosts(uri, Arrays.asList(host));
- }
-
- private void addToFilesToDistribute(FileReference reference, Collection<Host> hosts) {
- Set<Host> oldHosts = getHosts(reference);
- oldHosts.addAll(hosts);
- }
-
- private Set<Host> getHosts(FileReference reference) {
- return filesToHosts.computeIfAbsent(reference, k -> new HashSet<>());
+ return addFileReference(fileRegistry.addUri(uri), host);
}
- public FileDistributor(FileRegistry fileRegistry, List<ConfigServerSpec> configServerSpecs) {
- this.fileRegistry = fileRegistry;
- this.configServerSpecs = configServerSpecs;
+ private FileReference addFileReference(FileReference reference, Host host) {
+ filesToHosts.computeIfAbsent(reference, k -> new HashSet<>()).add(host);
+ return reference;
}
/** Returns the files which has been marked for distribution to the given host */
@@ -107,16 +91,20 @@ public class FileDistributor {
// should only be called during deploy
public void sendDeployedFiles(FileDistribution dbHandler) {
String fileSourceHost = fileSourceHost();
- for (Host host : getTargetHosts()) {
- if ( ! host.getHostname().equals(fileSourceHost)) {
- dbHandler.startDownload(host.getHostname(), ConfigProxy.BASEPORT, filesToSendToHost(host));
- }
- }
+
// Ask other config servers to download, for redundancy
- if (configServerSpecs != null)
- configServerSpecs.stream()
- .filter(configServerSpec -> !configServerSpec.getHostName().equals(fileSourceHost))
- .forEach(spec -> dbHandler.startDownload(spec.getHostName(), spec.getConfigServerPort(), allFilesToSend()));
+ configServerSpecs.stream()
+ .filter(spec -> !spec.getHostName().equals(fileSourceHost))
+ .forEach(spec -> dbHandler.startDownload(spec.getHostName(), spec.getConfigServerPort(), allFilesToSend()));
+
+ // Skip starting download for application hosts when on hosted, since this is just a hint and requests for files
+ // will fail until the application is activated (this call is done when preparing an application deployment)
+ // due to authorization of RPC requests on config servers only considering files belonging to active applications
+ if (isHosted) return;
+
+ getTargetHosts().stream()
+ .filter(host -> ! host.getHostname().equals(fileSourceHost))
+ .forEach(host -> dbHandler.startDownload(host.getHostname(), ConfigProxy.BASEPORT, filesToSendToHost(host)));
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java
index 8a88e720bed..fe6c6c52e2d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java
@@ -29,7 +29,7 @@ public abstract class AbstractSearchCluster extends AbstractConfigProducer
protected int index;
private Double visibilityDelay = 0.0;
private List<String> documentNames = new ArrayList<>();
- private List<SearchDefinitionSpec> localSDS = new LinkedList<>();
+ private List<SchemaSpec> localSDS = new LinkedList<>();
public AbstractSearchCluster(AbstractConfigProducer parent, String clusterName, int index) {
super(parent, "cluster." + clusterName);
@@ -38,11 +38,11 @@ public abstract class AbstractSearchCluster extends AbstractConfigProducer
}
public void prepareToDistributeFiles(List<SearchNode> backends) {
- for (SearchDefinitionSpec sds : localSDS)
+ for (SchemaSpec sds : localSDS)
sds.getSearchDefinition().getSearch().rankingConstants().sendTo(backends);
}
- public void addDocumentNames(SearchDefinition searchDefinition) {
+ public void addDocumentNames(NamedSchema searchDefinition) {
String dName = searchDefinition.getSearch().getDocument().getDocumentName().getName();
documentNames.add(dName);
}
@@ -50,7 +50,7 @@ public abstract class AbstractSearchCluster extends AbstractConfigProducer
/** Returns a List with document names used in this search cluster */
public List<String> getDocumentNames() { return documentNames; }
- public List<SearchDefinitionSpec> getLocalSDS() {
+ public List<SchemaSpec> getLocalSDS() {
return localSDS;
}
@@ -107,18 +107,17 @@ public abstract class AbstractSearchCluster extends AbstractConfigProducer
}
}
- public static final class SearchDefinitionSpec {
+ public static final class SchemaSpec {
- private final SearchDefinition searchDefinition;
+ private final NamedSchema searchDefinition;
private final UserConfigRepo userConfigRepo;
- public SearchDefinitionSpec(SearchDefinition searchDefinition,
- UserConfigRepo userConfigRepo) {
+ public SchemaSpec(NamedSchema searchDefinition, UserConfigRepo userConfigRepo) {
this.searchDefinition = searchDefinition;
this.userConfigRepo = userConfigRepo;
}
- public SearchDefinition getSearchDefinition() {
+ public NamedSchema getSearchDefinition() {
return searchDefinition;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
index 1b4c03a2182..56adc227df4 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
@@ -53,6 +53,7 @@ public class IndexedSearchCluster extends SearchCluster
private final DispatchGroup rootDispatch;
private DispatchSpec dispatchSpec;
private final boolean useAdaptiveDispatch;
+ private final double defaultTopKProbability;
private List<SearchNode> searchNodes = new ArrayList<>();
/**
@@ -70,6 +71,7 @@ public class IndexedSearchCluster extends SearchCluster
unionCfg = new UnionConfiguration(this, documentDbs);
rootDispatch = new DispatchGroup(this);
useAdaptiveDispatch = deployState.getProperties().useAdaptiveDispatch();
+ defaultTopKProbability = deployState.getProperties().defaultTopKProbability();
}
@Override
@@ -154,8 +156,7 @@ public class IndexedSearchCluster extends SearchCluster
private void fillDocumentDBConfig(DocumentDatabase sdoc, ProtonConfig.Documentdb.Builder ddbB) {
ddbB.inputdoctypename(sdoc.getInputDocType())
- .configid(sdoc.getConfigId())
- .visibilitydelay(getVisibilityDelay());
+ .configid(sdoc.getConfigId());
}
@Override
@@ -196,13 +197,15 @@ public class IndexedSearchCluster extends SearchCluster
routingSelector = sb.toString();
}
}
+
@Override
- protected void deriveAllSearchDefinitions(List<SearchDefinitionSpec> localSearches, DeployState deployState) {
- for (SearchDefinitionSpec spec : localSearches) {
+ protected void deriveAllSchemas(List<SchemaSpec> localSearches, DeployState deployState) {
+ for (SchemaSpec spec : localSearches) {
com.yahoo.searchdefinition.Search search = spec.getSearchDefinition().getSearch();
if ( ! (search instanceof DocumentOnlySearch)) {
DocumentDatabase db = new DocumentDatabase(this, search.getName(),
- new DerivedConfiguration(search, deployState.getDeployLogger(),
+ new DerivedConfiguration(search,
+ deployState.getDeployLogger(),
deployState.getProperties(),
deployState.rankProfileRegistry(),
deployState.getQueryProfiles().getRegistry(),
@@ -306,7 +309,11 @@ public class IndexedSearchCluster extends SearchCluster
}
if (useAdaptiveDispatch)
builder.distributionPolicy(DistributionPolicy.ADAPTIVE);
-
+ if (tuning.dispatch.getTopkProbability() != null) {
+ builder.topKProbability(tuning.dispatch.getTopkProbability());
+ } else {
+ builder.topKProbability(defaultTopKProbability);
+ }
if (tuning.dispatch.getMinActiveDocsCoverage() != null)
builder.minActivedocsPercentage(tuning.dispatch.getMinActiveDocsCoverage());
if (tuning.dispatch.getMinGroupCoverage() != null)
@@ -334,6 +341,7 @@ public class IndexedSearchCluster extends SearchCluster
if (searchCoverage.getMaxWaitAfterCoverageFactor() != null)
builder.maxWaitAfterCoverageFactor(searchCoverage.getMaxWaitAfterCoverageFactor());
}
+ builder.warmuptime(5.0);
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java b/config-model/src/main/java/com/yahoo/vespa/model/search/NamedSchema.java
index 860f89792e2..ba81073709e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinition.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/NamedSchema.java
@@ -8,7 +8,8 @@ import java.util.Collection;
/**
* @author Tony Vaagenes
*/
-public class SearchDefinition {
+// TODO: This class is quite pointless
+public class NamedSchema {
private final Search search;
private final String name;
@@ -23,15 +24,15 @@ public class SearchDefinition {
return name;
}
- public SearchDefinition(String name, Search search) {
+ public NamedSchema(String name, Search search) {
this.name = name;
this.search = search;
}
//Find search definition from a collection with the name specified
- public static SearchDefinition findByName(final String searchDefinitionName, Collection<SearchDefinition> searchDefinitions) {
- for (SearchDefinition candidate : searchDefinitions) {
- if (candidate.getName().equals(searchDefinitionName) )
+ public static NamedSchema findByName(String schemaName, Collection<NamedSchema> schemas) {
+ for (NamedSchema candidate : schemas) {
+ if (candidate.getName().equals(schemaName) )
return candidate;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java b/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java
index c337c77d2a2..5d8b0b688d8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/NodeFlavorTuning.java
@@ -30,6 +30,7 @@ public class NodeFlavorTuning implements ProtonConfig.Producer {
public void getConfig(ProtonConfig.Builder builder) {
setHwInfo(builder);
tuneDiskWriteSpeed(builder);
+ tuneRequestThreads(builder);
tuneDocumentStoreMaxFileSize(builder.summary.log);
tuneFlushStrategyMemoryLimits(builder.flush.memory);
tuneFlushStrategyTlsSize(builder.flush.memory);
@@ -105,6 +106,12 @@ public class NodeFlavorTuning implements ProtonConfig.Producer {
}
}
+ private void tuneRequestThreads(ProtonConfig.Builder builder) {
+ int numCores = (int)Math.ceil(nodeFlavor.getMinCpuCores());
+ builder.numsearcherthreads(numCores);
+ builder.numsummarythreads(numCores);
+ }
+
private void tuneWriteFilter(ProtonConfig.Writefilter.Builder builder) {
// "Reserve" 1GB of memory for other processes running on the content node (config-proxy, cluster-controller, metrics-proxy)
double reservedMemoryGb = 1;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinitionXMLHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/search/SchemaDefinitionXMLHandler.java
index 1054253e3f0..b505b5e681c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchDefinitionXMLHandler.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/SchemaDefinitionXMLHandler.java
@@ -7,15 +7,15 @@ import java.io.Serializable;
import java.util.List;
/**
- * Represents a single searchdefinition file.
+ * Represents a single schema file.
*
* @author arnej27959
*/
-public class SearchDefinitionXMLHandler implements Serializable {
+public class SchemaDefinitionXMLHandler implements Serializable {
private String sdName;
- public SearchDefinitionXMLHandler(ModelElement elem) {
+ public SchemaDefinitionXMLHandler(ModelElement elem) {
sdName = elem.stringAttribute("name");
if (sdName == null) {
sdName = elem.stringAttribute("type");
@@ -24,8 +24,8 @@ public class SearchDefinitionXMLHandler implements Serializable {
public String getName() { return sdName; }
- public SearchDefinition getResponsibleSearchDefinition(List<SearchDefinition> searchDefinitions) {
- return SearchDefinition.findByName( getName(), searchDefinitions );
+ public NamedSchema getResponsibleSearchDefinition(List<NamedSchema> schemas) {
+ return NamedSchema.findByName(getName(), schemas );
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/SearchCluster.java
index 60b3cb6987c..0139e949c7a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/SearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/SearchCluster.java
@@ -35,22 +35,14 @@ public abstract class SearchCluster extends AbstractSearchCluster
super(parent, clusterName, index);
}
- public void writeFiles(File directory) throws java.io.IOException {
- if (!directory.isDirectory() && !directory.mkdirs()) {
- throw new java.io.IOException("Cannot create directory: "+ directory);
- }
- writeSdFiles(directory);
- super.writeFiles(directory);
- }
-
/**
* Must be called after cluster is built, to derive SD configs
* Derives the search definitions from the application package..
* Also stores the document names contained in the search
* definitions.
*/
- public void deriveSearchDefinitions(DeployState deployState) {
- deriveAllSearchDefinitions(getLocalSDS(), deployState);
+ public void deriveSchemas(DeployState deployState) {
+ deriveAllSchemas(getLocalSDS(), deployState);
}
@Override
@@ -140,7 +132,7 @@ public abstract class SearchCluster extends AbstractSearchCluster
return false;
}
- protected abstract void deriveAllSearchDefinitions(List<SearchDefinitionSpec> localSearches, DeployState deployState);
+ protected abstract void deriveAllSchemas(List<SchemaSpec> localSearches, DeployState deployState);
public abstract void defaultDocumentsConfig();
public abstract DerivedConfiguration getSdConfig();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/StreamingSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/StreamingSearchCluster.java
index d668adea116..28e7b3eb37a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/StreamingSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/StreamingSearchCluster.java
@@ -91,7 +91,7 @@ public class StreamingSearchCluster extends SearchCluster implements
}
@Override
- protected void deriveAllSearchDefinitions(List<SearchDefinitionSpec> local, DeployState deployState) {
+ protected void deriveAllSchemas(List<SchemaSpec> local, DeployState deployState) {
if (local.size() == 1) {
deriveSingleSearchDefinition(local.get(0).getSearchDefinition().getSearch(), deployState);
} else if (local.size() > 1){
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/Tuning.java b/config-model/src/main/java/com/yahoo/vespa/model/search/Tuning.java
index f5a91297e9e..f93bf6fc872 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/Tuning.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/Tuning.java
@@ -241,7 +241,12 @@ public class Tuning extends AbstractConfigProducer implements ProtonConfig.Produ
public Integer level = null;
public void getConfig(ProtonConfig.Summary.Cache.Compression.Builder compression) {
- if (type != null) compression.type(ProtonConfig.Summary.Cache.Compression.Type.Enum.valueOf(type.name));
+ if (type != null) compression.type(ProtonConfig.Summary.Cache.Compression.Type.Enum.valueOf(type.name));
+ if (level != null) compression.level(level);
+ }
+
+ public void getConfig(ProtonConfig.Summary.Log.Compact.Compression.Builder compression) {
+ if (type != null) compression.type(ProtonConfig.Summary.Log.Compact.Compression.Type.Enum.valueOf(type.name));
if (level != null) compression.level(level);
}
@@ -281,6 +286,12 @@ public class Tuning extends AbstractConfigProducer implements ProtonConfig.Produ
}
}
+ public void getConfig(ProtonConfig.Summary.Log.Compact.Builder compact) {
+ if (compression != null) {
+ compression.getConfig(compact.compression);
+ }
+ }
+
public void getConfig(ProtonConfig.Summary.Log.Chunk.Builder chunk) {
if (outputInt) {
if (maxSize!=null) chunk.maxbytes(maxSize.intValue());
@@ -288,7 +299,7 @@ public class Tuning extends AbstractConfigProducer implements ProtonConfig.Produ
throw new IllegalStateException("Fix this, chunk does not have long types");
}
if (compression != null) {
- compression.getConfig(chunk.compression);
+ compression.getConfig(chunk.compression);
}
}
}
@@ -303,6 +314,7 @@ public class Tuning extends AbstractConfigProducer implements ProtonConfig.Produ
if (minFileSizeFactor!=null) log.minfilesizefactor(minFileSizeFactor);
if (chunk != null) {
chunk.getConfig(log.chunk);
+ chunk.getConfig(log.compact);
}
}
}