summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-model-api/abi-spec.json19
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java42
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java57
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java39
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java45
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java14
-rw-r--r--config-model/src/main/resources/schema/deployment.rnc15
-rw-r--r--config-model/src/test/schema-test-files/deployment.xml6
-rw-r--r--config-provisioning/abi-spec.json3
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java7
-rw-r--r--config-provisioning/src/test/java/com/yahoo/config/provision/SystemNameTest.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java16
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java14
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/DefaultRpcAuthorizerProvider.java25
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java17
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/MapEncoder.java10
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java10
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java21
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java49
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceForwarder.java15
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/NameServiceDispatcher.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java31
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializer.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java46
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java33
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java12
-rw-r--r--document/src/tests/serialization/vespadocumentserializer_test.cpp54
-rw-r--r--document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp33
-rw-r--r--document/src/vespa/document/fieldvalue/tensorfieldvalue.cpp14
-rw-r--r--eval/CMakeLists.txt1
-rw-r--r--eval/src/tests/tensor/tensor_serialization/tensor_serialization_test.cpp402
-rw-r--r--eval/src/tests/tensor/tensor_slime_serialization/.gitignore1
-rw-r--r--eval/src/tests/tensor/tensor_slime_serialization/CMakeLists.txt8
-rw-r--r--eval/src/tests/tensor/tensor_slime_serialization/tensor_slime_serialization_test.cpp185
-rw-r--r--eval/src/vespa/eval/tensor/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/tensor/serialization/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/tensor/serialization/slime_binary_format.cpp100
-rw-r--r--eval/src/vespa/eval/tensor/serialization/slime_binary_format.h26
-rw-r--r--eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp13
-rw-r--r--eval/src/vespa/eval/tensor/tensor_factory.cpp65
-rw-r--r--eval/src/vespa/eval/tensor/tensor_factory.h27
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java5
-rw-r--r--metrics-proxy/pom.xml27
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java77
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java7
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JacksonUtil.java4
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java2
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java19
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java13
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java153
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModelTest.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java8
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_test.cpp27
-rw-r--r--searchcore/src/tests/proton/docsummary/docsummary.cpp28
-rw-r--r--searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp23
-rw-r--r--searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp8
-rw-r--r--searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_compaction_test.cpp70
-rw-r--r--searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp12
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp12
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.cpp21
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp27
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.cpp27
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.h12
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/maintenance_jobs_injector.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/maintenancecontroller.cpp20
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.cpp45
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.h28
-rw-r--r--searchlib/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/attribute/imported_attribute_vector/imported_attribute_vector_test.cpp34
-rw-r--r--searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp137
-rw-r--r--searchlib/src/tests/features/constant/constant_test.cpp26
-rw-r--r--searchlib/src/tests/features/tensor/tensor_test.cpp2
-rw-r--r--searchlib/src/tests/index/field_length_calculator/CMakeLists.txt9
-rw-r--r--searchlib/src/tests/index/field_length_calculator/field_length_calculator_test.cpp68
-rw-r--r--searchlib/src/tests/memoryindex/field_inverter/field_inverter_test.cpp27
-rw-r--r--searchlib/src/vespa/searchlib/index/field_length_calculator.h42
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp27
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/field_inverter.h14
-rw-r--r--searchlib/src/vespa/searchlib/test/memoryindex/ordered_field_index_inserter.h9
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp1
-rw-r--r--vespajlib/abi-spec.json4
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/IndexedDoubleTensor.java6
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/IndexedFloatTensor.java10
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java111
-rw-r--r--vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java32
-rw-r--r--vespalib/src/tests/util/rcuvector/CMakeLists.txt2
110 files changed, 1640 insertions, 1207 deletions
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json
index b67b496a7ce..c7b80dce778 100644
--- a/config-model-api/abi-spec.json
+++ b/config-model-api/abi-spec.json
@@ -287,7 +287,7 @@
"public"
],
"methods": [
- "public void <init>(java.util.Optional, com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy, java.util.Optional, java.util.List, java.util.List, java.lang.String, java.util.Optional, java.util.Optional, com.yahoo.config.application.api.Notifications)",
+ "public void <init>(java.util.Optional, com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy, java.util.Optional, java.util.List, java.util.List, java.lang.String, java.util.Optional, java.util.Optional, com.yahoo.config.application.api.Notifications, java.util.List)",
"public java.util.Optional globalServiceId()",
"public com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy upgradePolicy()",
"public java.util.Optional majorVersion()",
@@ -297,6 +297,7 @@
"public java.util.List steps()",
"public java.util.List zones()",
"public com.yahoo.config.application.api.Notifications notifications()",
+ "public java.util.List endpoints()",
"public java.lang.String xmlForm()",
"public boolean includes(com.yahoo.config.provision.Environment, java.util.Optional)",
"public static com.yahoo.config.application.api.DeploymentSpec fromXml(java.io.Reader)",
@@ -313,6 +314,22 @@
"public static final com.yahoo.config.application.api.DeploymentSpec empty"
]
},
+ "com.yahoo.config.application.api.Endpoint": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public"
+ ],
+ "methods": [
+ "public void <init>(java.util.Optional, java.lang.String, java.util.Set)",
+ "public java.lang.String endpointId()",
+ "public java.lang.String containerId()",
+ "public java.util.Set regions()",
+ "public boolean equals(java.lang.Object)",
+ "public int hashCode()"
+ ],
+ "fields": []
+ },
"com.yahoo.config.application.api.FileRegistry$Entry": {
"superClass": "java.lang.Object",
"interfaces": [],
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java
index 66111b91aa0..ada5ee23a7c 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java
@@ -46,7 +46,8 @@ public class DeploymentSpec {
"<deployment version='1.0'/>",
Optional.empty(),
Optional.empty(),
- Notifications.none());
+ Notifications.none(),
+ List.of());
private final Optional<String> globalServiceId;
private final UpgradePolicy upgradePolicy;
@@ -57,10 +58,12 @@ public class DeploymentSpec {
private final Optional<AthenzDomain> athenzDomain;
private final Optional<AthenzService> athenzService;
private final Notifications notifications;
+ private final List<Endpoint> endpoints;
public DeploymentSpec(Optional<String> globalServiceId, UpgradePolicy upgradePolicy, Optional<Integer> majorVersion,
List<ChangeBlocker> changeBlockers, List<Step> steps, String xmlForm,
- Optional<AthenzDomain> athenzDomain, Optional<AthenzService> athenzService, Notifications notifications) {
+ Optional<AthenzDomain> athenzDomain, Optional<AthenzService> athenzService, Notifications notifications,
+ List<Endpoint> endpoints) {
validateTotalDelay(steps);
this.globalServiceId = globalServiceId;
this.upgradePolicy = upgradePolicy;
@@ -71,8 +74,10 @@ public class DeploymentSpec {
this.athenzDomain = athenzDomain;
this.athenzService = athenzService;
this.notifications = notifications;
+ this.endpoints = List.copyOf(Objects.requireNonNull(endpoints, "Missing endpoints parameter"));
validateZones(this.steps);
validateAthenz();
+ validateEndpoints(this.steps, globalServiceId, this.endpoints);
}
/** Throw an IllegalArgumentException if the total delay exceeds 24 hours */
@@ -94,6 +99,26 @@ public class DeploymentSpec {
ensureUnique(zone, zones);
}
+ /** Throw an IllegalArgumentException if an endpoint refers to a region that is not declared in 'prod' */
+ private void validateEndpoints(List<Step> steps, Optional<String> globalServiceId, List<Endpoint> endpoints) {
+ if (globalServiceId.isPresent() && ! endpoints.isEmpty()) {
+ throw new IllegalArgumentException("Providing both 'endpoints' and 'global-service-id'. Use only 'endpoints'.");
+ }
+
+ var stepZones = steps.stream()
+ .flatMap(s -> s.zones().stream())
+ .flatMap(z -> z.region.stream())
+ .collect(Collectors.toSet());
+
+ for (var endpoint : endpoints){
+ for (var endpointRegion : endpoint.regions()) {
+ if (! stepZones.contains(endpointRegion)) {
+ throw new IllegalArgumentException("Region used in endpoint that is not declared in 'prod': " + endpointRegion);
+ }
+ }
+ }
+ }
+
/*
* Throw an IllegalArgumentException if Athenz configuration violates:
* domain not configured -> no zone can configure service
@@ -101,16 +126,16 @@ public class DeploymentSpec {
*/
private void validateAthenz() {
// If athenz domain is not set, athenz service cannot be set on any level
- if (! athenzDomain.isPresent()) {
+ if (athenzDomain.isEmpty()) {
for (DeclaredZone zone : zones()) {
if(zone.athenzService().isPresent()) {
throw new IllegalArgumentException("Athenz service configured for zone: " + zone + ", but Athenz domain is not configured");
}
}
// if athenz domain is not set, athenz service must be set implicitly or directly on all zones.
- } else if(! athenzService.isPresent()) {
+ } else if (athenzService.isEmpty()) {
for (DeclaredZone zone : zones()) {
- if(! zone.athenzService().isPresent()) {
+ if (zone.athenzService().isEmpty()) {
throw new IllegalArgumentException("Athenz domain is configured, but Athenz service not configured for zone: " + zone);
}
}
@@ -199,6 +224,9 @@ public class DeploymentSpec {
/** Returns the notification configuration */
public Notifications notifications() { return notifications; }
+ /** Returns the rotations configuration */
+ public List<Endpoint> endpoints() { return endpoints; }
+
/** Returns the XML form of this spec, or null if it was not created by fromXml, nor is empty */
public String xmlForm() { return xmlForm; }
@@ -369,7 +397,7 @@ public class DeploymentSpec {
Optional<AthenzService> athenzService, Optional<String> testerFlavor) {
if (environment != Environment.prod && region.isPresent())
throw new IllegalArgumentException("Non-prod environments cannot specify a region");
- if (environment == Environment.prod && ! region.isPresent())
+ if (environment == Environment.prod && region.isEmpty())
throw new IllegalArgumentException("Prod environments must be specified with a region");
this.environment = environment;
this.region = region;
@@ -417,7 +445,7 @@ public class DeploymentSpec {
@Override
public String toString() {
- return environment + ( region.isPresent() ? "." + region.get() : "");
+ return environment + (region.map(regionName -> "." + regionName).orElse(""));
}
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java b/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java
new file mode 100644
index 00000000000..73328027540
--- /dev/null
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java
@@ -0,0 +1,57 @@
+package com.yahoo.config.application.api;
+
+import com.yahoo.config.provision.RegionName;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a (global) endpoint in 'deployments.xml'. It contains the name of the
+ * endpoint (endpointId) and the name of the container cluster that the endpoint
+ * should point to.
+ *
+ * If the endpointId is not set, it will default to the same as the containerId.
+ */
+public class Endpoint {
+ private final Optional<String> endpointId;
+ private final String containerId;
+ private final Set<RegionName> regions;
+
+ public Endpoint(Optional<String> endpointId, String containerId, Set<String> regions) {
+ this.endpointId = endpointId;
+ this.containerId = containerId;
+ this.regions = Set.copyOf(
+ Objects.requireNonNull(
+ regions.stream().map(RegionName::from).collect(Collectors.toList()),
+ "Missing 'regions' parameter"));
+ }
+
+ public String endpointId() {
+ return endpointId.orElse(containerId);
+ }
+
+ public String containerId() {
+ return containerId;
+ }
+
+ public Set<RegionName> regions() {
+ return regions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Endpoint endpoint = (Endpoint) o;
+ return Objects.equals(endpointId, endpoint.endpointId) &&
+ Objects.equals(containerId, endpoint.containerId) &&
+ Objects.equals(regions, endpoint.regions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(endpointId, containerId, regions);
+ }
+}
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java
index 8e899b28a06..58795f6ea9e 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java
@@ -6,6 +6,7 @@ import com.yahoo.config.application.api.DeploymentSpec.DeclaredZone;
import com.yahoo.config.application.api.DeploymentSpec.Delay;
import com.yahoo.config.application.api.DeploymentSpec.ParallelZones;
import com.yahoo.config.application.api.DeploymentSpec.Step;
+import com.yahoo.config.application.api.Endpoint;
import com.yahoo.config.application.api.Notifications;
import com.yahoo.config.application.api.Notifications.Role;
import com.yahoo.config.application.api.Notifications.When;
@@ -25,6 +26,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -40,6 +42,8 @@ public class DeploymentSpecXmlReader {
private static final String stagingTag = "staging";
private static final String blockChangeTag = "block-change";
private static final String prodTag = "prod";
+ private static final String endpointsTag = "endpoints";
+ private static final String endpointTag = "endpoint";
private final boolean validate;
@@ -123,7 +127,8 @@ public class DeploymentSpecXmlReader {
xmlForm,
athenzDomain,
athenzService,
- readNotifications(root));
+ readNotifications(root),
+ readEndpoints(root));
}
private Notifications readNotifications(Element root) {
@@ -152,6 +157,38 @@ public class DeploymentSpecXmlReader {
return Notifications.of(emailAddresses, emailRoles);
}
+ private List<Endpoint> readEndpoints(Element root) {
+ final var endpointsElement = XML.getChild(root, endpointsTag);
+ if (endpointsElement == null) { return Collections.emptyList(); }
+
+ final var endpoints = new ArrayList<Endpoint>();
+
+ for (var endpointElement : XML.getChildren(endpointsElement, endpointTag)) {
+ final Optional<String> rotationId = stringAttribute("id", endpointElement);
+ final Optional<String> containerId = stringAttribute("container-id", endpointElement);
+ final var regions = new HashSet<String>();
+
+ if (containerId.isEmpty()) {
+ throw new IllegalArgumentException("Missing 'container-id' from 'endpoint' tag.");
+ }
+
+ for (var regionElement : XML.getChildren(endpointElement, "region")) {
+ var region = regionElement.getTextContent();
+ if (region == null || region.isEmpty() || region.isBlank()) {
+ throw new IllegalArgumentException("Empty 'region' element in 'endpoint' tag.");
+ }
+ if (regions.contains(region)) {
+ throw new IllegalArgumentException("Duplicate 'region' element in 'endpoint' tag: " + region);
+ }
+ regions.add(region);
+ }
+
+ endpoints.add(new Endpoint(rotationId, containerId.get(), regions));
+ }
+
+ return endpoints;
+ }
+
/**
* Imposes some constraints on tag order which are not expressible in the schema
*/
diff --git a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java
index 46e6dc457e9..3049d511bf1 100644
--- a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java
+++ b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java
@@ -1,9 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.application.api;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.yahoo.config.provision.Deployment;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.RegionName;
import org.junit.Test;
@@ -11,7 +9,11 @@ import org.junit.Test;
import java.io.StringReader;
import java.time.Instant;
import java.time.ZoneId;
+import java.util.Collections;
+import java.util.List;
import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
import static com.yahoo.config.application.api.Notifications.Role.author;
import static com.yahoo.config.application.api.Notifications.When.failing;
@@ -452,4 +454,43 @@ public class DeploymentSpecTest {
assertEquals(Optional.of("d-2-8-50"), spec.steps().get(2).zones().get(0).testerFlavor());
}
+ @Test
+ public void noEndpoints() {
+ assertEquals(Collections.emptyList(), DeploymentSpec.fromXml("<deployment />").endpoints());
+ }
+
+ @Test
+ public void emptyEndpoints() {
+ final var spec = DeploymentSpec.fromXml("<deployment><endpoints/></deployment>");
+ assertEquals(Collections.emptyList(), spec.endpoints());
+ }
+
+ @Test
+ public void someEndpoints() {
+ final var spec = DeploymentSpec.fromXml("" +
+ "<deployment>" +
+ " <prod>" +
+ " <region active=\"true\">us-east</region>" +
+ " </prod>" +
+ " <endpoints>" +
+ " <endpoint id=\"foo\" container-id=\"bar\">" +
+ " <region>us-east</region>" +
+ " </endpoint>" +
+ " <endpoint id=\"nalle\" container-id=\"frosk\" />" +
+ " <endpoint container-id=\"quux\" />" +
+ " </endpoints>" +
+ "</deployment>");
+
+ assertEquals(
+ List.of("foo", "nalle", "quux"),
+ spec.endpoints().stream().map(Endpoint::endpointId).collect(Collectors.toList())
+ );
+
+ assertEquals(
+ List.of("bar", "frosk", "quux"),
+ spec.endpoints().stream().map(Endpoint::containerId).collect(Collectors.toList())
+ );
+
+ assertEquals(Set.of(RegionName.from("us-east")), spec.endpoints().get(0).regions());
+ }
}
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 dccd19e7c63..47c6b2dbb52 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
@@ -9,6 +9,7 @@ import ai.vespa.metricsproxy.core.MetricsConsumers;
import ai.vespa.metricsproxy.core.MetricsManager;
import ai.vespa.metricsproxy.core.MonitoringConfig;
import ai.vespa.metricsproxy.core.VespaMetrics;
+import ai.vespa.metricsproxy.http.GenericMetricsHandler;
import ai.vespa.metricsproxy.metric.ExternalMetrics;
import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
@@ -20,6 +21,7 @@ import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Zone;
+import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.admin.Admin;
@@ -27,6 +29,7 @@ import com.yahoo.vespa.model.admin.monitoring.MetricSet;
import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;
import com.yahoo.vespa.model.admin.monitoring.Monitoring;
import com.yahoo.vespa.model.container.ContainerCluster;
+import com.yahoo.vespa.model.container.component.Handler;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -66,6 +69,8 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
static final Path METRICS_PROXY_BUNDLE_FILE = absoluteBundlePath((Paths.get(METRICS_PROXY_NAME + JAR_WITH_DEPS.suffix)));
static final String METRICS_PROXY_BUNDLE_NAME = "com.yahoo.vespa." + METRICS_PROXY_NAME;
+ private static final String METRICS_HANDLER_BINDING = "/metrics/v1/values";
+
static final class AppDimensionNames {
static final String ZONE = "zone";
static final String APPLICATION_ID = "applicationId"; // tenant.app.instance
@@ -90,6 +95,7 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
addPlatformBundle(METRICS_PROXY_BUNDLE_FILE);
addClusterComponents();
+ addGenericMetricsHandler();
}
private void addClusterComponents() {
@@ -103,6 +109,14 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
addMetricsProxyComponent(VespaMetrics.class);
}
+ private void addGenericMetricsHandler() {
+ Handler<AbstractConfigProducer<?>> metricsHandler = new Handler<>(
+ new ComponentModel(GenericMetricsHandler.class.getName(), null, METRICS_PROXY_BUNDLE_NAME, null));
+ metricsHandler.addServerBindings("http://*" + METRICS_HANDLER_BINDING,
+ "http://*" + METRICS_HANDLER_BINDING + "/*");
+ addComponent(metricsHandler);
+ }
+
@Override
protected void doPrepare(DeployState deployState) { }
diff --git a/config-model/src/main/resources/schema/deployment.rnc b/config-model/src/main/resources/schema/deployment.rnc
index 15a4e70e33f..7b15a1c062d 100644
--- a/config-model/src/main/resources/schema/deployment.rnc
+++ b/config-model/src/main/resources/schema/deployment.rnc
@@ -10,6 +10,7 @@ start = element deployment {
Upgrade? &
BlockChange* &
Notifications? &
+ Endpoints? &
Test? &
Staging? &
Prod*
@@ -73,3 +74,17 @@ Delay = element delay {
Parallel = element parallel {
Region*
}
+
+EndpointRegion = element region {
+ text
+}
+
+Endpoint = element endpoint {
+ attribute id { xsd:string }? &
+ attribute container-id { xsd:string } &
+ EndpointRegion*
+}
+
+Endpoints = element endpoints {
+ Endpoint+
+}
diff --git a/config-model/src/test/schema-test-files/deployment.xml b/config-model/src/test/schema-test-files/deployment.xml
index ea65dbec661..0b30067d78a 100644
--- a/config-model/src/test/schema-test-files/deployment.xml
+++ b/config-model/src/test/schema-test-files/deployment.xml
@@ -20,4 +20,10 @@
<region active='true'>us-south-2</region>
</parallel>
</prod>
+ <endpoints>
+ <endpoint id="foo" container-id="bar">
+ <region>us-east</region>
+ </endpoint>
+ <endpoint container-id="bar" />
+ </endpoints>
</deployment>
diff --git a/config-provisioning/abi-spec.json b/config-provisioning/abi-spec.json
index 66cc491d471..3cfa301894a 100644
--- a/config-provisioning/abi-spec.json
+++ b/config-provisioning/abi-spec.json
@@ -783,7 +783,8 @@
"public java.lang.String value()",
"public boolean isPublic()",
"public boolean isCd()",
- "public static java.util.Set all()"
+ "public static java.util.Set all()",
+ "public static java.util.Set allOf(java.util.function.Predicate)"
],
"fields": [
"public static final enum com.yahoo.config.provision.SystemName cd",
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java b/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java
index 088d0bdb3c3..ba462b9eb64 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java
@@ -3,6 +3,9 @@ package com.yahoo.config.provision;
import java.util.EnumSet;
import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Systems in hosted Vespa
@@ -72,4 +75,8 @@ public enum SystemName {
public boolean isCd() { return isCd; }
public static Set<SystemName> all() { return EnumSet.allOf(SystemName.class); }
+
+ public static Set<SystemName> allOf(Predicate<SystemName> predicate) {
+ return Stream.of(values()).filter(predicate::test).collect(Collectors.toSet());
+ }
}
diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/SystemNameTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/SystemNameTest.java
index 6ffca4918a8..eb066958254 100644
--- a/config-provisioning/src/test/java/com/yahoo/config/provision/SystemNameTest.java
+++ b/config-provisioning/src/test/java/com/yahoo/config/provision/SystemNameTest.java
@@ -3,6 +3,8 @@ package com.yahoo.config.provision;
import org.junit.Test;
+import java.util.Set;
+
import static org.junit.Assert.assertEquals;
/**
@@ -15,4 +17,10 @@ public class SystemNameTest {
assertEquals(name, SystemName.from(name.value()));
}
}
+
+ @Test
+ public void allOf() {
+ assertEquals(Set.of(SystemName.cd, SystemName.PublicCd, SystemName.vaas), SystemName.allOf(SystemName::isCd));
+ assertEquals(Set.of(SystemName.PublicCd, SystemName.Public, SystemName.vaas), SystemName.allOf(SystemName::isPublic));
+ }
} \ No newline at end of file
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index 3aea22ce8b2..0ceec0ab00b 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -451,12 +451,16 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
private Application getApplication(ApplicationId applicationId) {
+ return getApplication(applicationId, Optional.empty());
+ }
+
+ private Application getApplication(ApplicationId applicationId, Optional<Version> version) {
try {
Tenant tenant = tenantRepository.getTenant(applicationId.tenant());
if (tenant == null) throw new IllegalArgumentException("Tenant '" + applicationId.tenant() + "' not found");
long sessionId = getSessionIdForApplication(tenant, applicationId);
RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId, 0);
- return session.ensureApplicationLoaded().getForVersionOrLatest(Optional.empty(), clock.instant());
+ return session.ensureApplicationLoaded().getForVersionOrLatest(version, clock.instant());
} catch (Exception e) {
log.log(LogLevel.WARNING, "Failed getting application for '" + applicationId + "'", e);
throw e;
@@ -500,12 +504,14 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
// ---------------- Convergence ----------------------------------------------------------------
- public HttpResponse checkServiceForConfigConvergence(ApplicationId applicationId, String hostAndPort, URI uri, Duration timeout) {
- return convergeChecker.checkService(getApplication(applicationId), hostAndPort, uri, timeout);
+ public HttpResponse checkServiceForConfigConvergence(ApplicationId applicationId, String hostAndPort, URI uri,
+ Duration timeout, Optional<Version> vespaVersion) {
+ return convergeChecker.checkService(getApplication(applicationId, vespaVersion), hostAndPort, uri, timeout);
}
- public HttpResponse servicesToCheckForConfigConvergence(ApplicationId applicationId, URI uri, Duration timeoutPerService) {
- return convergeChecker.servicesToCheck(getApplication(applicationId), uri, timeoutPerService);
+ public HttpResponse servicesToCheckForConfigConvergence(ApplicationId applicationId, URI uri,
+ Duration timeoutPerService, Optional<Version> vespaVersion) {
+ return convergeChecker.servicesToCheck(getApplication(applicationId, vespaVersion), uri, timeoutPerService);
}
// ---------------- Logs ----------------------------------------------------------------
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
index 4fbda42fdc7..97c1c9c89b0 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
@@ -8,6 +8,7 @@ import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.PortInfo;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.log.LogLevel;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.config.server.http.JSONResponse;
import org.glassfish.jersey.client.ClientProperties;
@@ -43,6 +44,7 @@ import static com.yahoo.config.model.api.container.ContainerServiceType.QRSERVER
*/
public class ConfigConvergenceChecker extends AbstractComponent {
+ private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ConfigConvergenceChecker.class.getName());
private static final ApplicationId routingApplicationId = ApplicationId.from("hosted-vespa", "routing", "default");
private static final String statePath = "/state/v1/";
private static final String configSubPath = "config";
@@ -68,6 +70,7 @@ public class ConfigConvergenceChecker extends AbstractComponent {
/** Check all services in given application. Returns the minimum current generation of all services */
public ServiceListResponse servicesToCheck(Application application, URI requestUrl, Duration timeoutPerService) {
+ log.log(LogLevel.INFO, "Finding services to check config convergence for in '" + application);
List<ServiceInfo> servicesToCheck = new ArrayList<>();
application.getModel().getHosts()
.forEach(host -> host.getServices().stream()
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
index e04f83cc648..d9592dc9352 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.config.server.http.v2;
import com.google.inject.Inject;
+import com.yahoo.component.Version;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
@@ -62,7 +63,8 @@ public class ApplicationHandler extends HttpHandler {
if (isServiceConvergeRequest(request)) {
// Expects both hostname and port in the request (hostname:port)
String hostAndPort = getHostNameFromRequest(request);
- return applicationRepository.checkServiceForConfigConvergence(applicationId, hostAndPort, request.getUri(), timeout);
+ return applicationRepository.checkServiceForConfigConvergence(applicationId, hostAndPort, request.getUri(),
+ timeout, getVespaVersionFromRequest(request));
}
if (isClusterControllerStatusRequest(request)) {
@@ -89,7 +91,8 @@ public class ApplicationHandler extends HttpHandler {
}
if (isServiceConvergeListRequest(request)) {
- return applicationRepository.servicesToCheckForConfigConvergence(applicationId, request.getUri(), timeout);
+ return applicationRepository.servicesToCheckForConfigConvergence(applicationId, request.getUri(), timeout,
+ getVespaVersionFromRequest(request));
}
if (isFiledistributionStatusRequest(request)) {
@@ -225,6 +228,13 @@ public class ApplicationHandler extends HttpHandler {
.build();
}
+ private static Optional<Version> getVespaVersionFromRequest(HttpRequest request) {
+ String vespaVersion = request.getProperty("vespaVersion");
+ return (vespaVersion == null || vespaVersion.isEmpty())
+ ? Optional.empty()
+ : Optional.of(Version.fromString(vespaVersion));
+ }
+
private static class DeleteApplicationResponse extends JSONResponse {
DeleteApplicationResponse(int status, ApplicationId applicationId) {
super(status);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/DefaultRpcAuthorizerProvider.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/DefaultRpcAuthorizerProvider.java
index ff502ff2cc0..c7bbecc157c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/DefaultRpcAuthorizerProvider.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/DefaultRpcAuthorizerProvider.java
@@ -8,6 +8,8 @@ import com.yahoo.container.di.componentgraph.Provider;
import com.yahoo.security.tls.TransportSecurityUtils;
import com.yahoo.vespa.config.server.host.HostRegistries;
import com.yahoo.vespa.config.server.rpc.RequestHandlerProvider;
+import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
/**
* A provider for {@link RpcAuthorizer}. The instance provided is dependent on the configuration of the configserver.
@@ -22,14 +24,29 @@ public class DefaultRpcAuthorizerProvider implements Provider<RpcAuthorizer> {
public DefaultRpcAuthorizerProvider(ConfigserverConfig config,
NodeIdentifier nodeIdentifier,
HostRegistries hostRegistries,
- RequestHandlerProvider handlerProvider) {
- // TODO Re-enable once performance/stability issues with MultiTenantRpcAuthorizer are fixed
+ RequestHandlerProvider handlerProvider,
+ FlagSource flagSource) {
+ String authorizerMode = Flags.CONFIGSERVER_RPC_AUTHORIZER.bindTo(flagSource).value();
+ boolean useMultiTenantAuthorizer =
+ TransportSecurityUtils.isTransportSecurityEnabled() && config.multitenant() && config.hostedVespa() && !authorizerMode.equals("disable");
this.rpcAuthorizer =
- TransportSecurityUtils.isTransportSecurityEnabled() && config.multitenant() && config.hostedVespa() && false
- ? new MultiTenantRpcAuthorizer(nodeIdentifier, hostRegistries, handlerProvider)
+ useMultiTenantAuthorizer
+ ? new MultiTenantRpcAuthorizer(nodeIdentifier, hostRegistries, handlerProvider, toMultiTenantRpcAuthorizerMode(authorizerMode), getThreadPoolSize(config))
: new NoopRpcAuthorizer();
}
+ private static MultiTenantRpcAuthorizer.Mode toMultiTenantRpcAuthorizerMode(String authorizerMode) {
+ switch (authorizerMode) {
+ case "log-only": return MultiTenantRpcAuthorizer.Mode.LOG_ONLY;
+ case "enforce": return MultiTenantRpcAuthorizer.Mode.ENFORCE;
+ default: throw new IllegalArgumentException("Invalid authorizer mode: " + authorizerMode);
+ }
+ }
+
+ private static int getThreadPoolSize(ConfigserverConfig config) {
+ return config.numRpcThreads() != 0 ? config.numRpcThreads() : 8;
+ }
+
@Override
public RpcAuthorizer get() {
return rpcAuthorizer;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
index 2527569bea4..a63f709a99f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
@@ -1,7 +1,6 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.rpc.security;
-import com.google.inject.Inject;
import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.config.FileReference;
import com.yahoo.config.provision.ApplicationId;
@@ -49,15 +48,16 @@ public class MultiTenantRpcAuthorizer implements RpcAuthorizer {
private final Executor executor;
private final Mode mode;
- @Inject
public MultiTenantRpcAuthorizer(NodeIdentifier nodeIdentifier,
HostRegistries hostRegistries,
- RequestHandlerProvider handlerProvider) {
+ RequestHandlerProvider handlerProvider,
+ Mode mode,
+ int threadPoolSize) {
this(nodeIdentifier,
hostRegistries.getTenantHostRegistry(),
handlerProvider,
- Executors.newFixedThreadPool(4, new DaemonThreadFactory("RPC-Authorizer-")),
- Mode.LOG_ONLY); // TODO Change default mode
+ Executors.newFixedThreadPool(threadPoolSize, new DaemonThreadFactory("multi-tenant-rpc-authorizer-")),
+ mode);
}
MultiTenantRpcAuthorizer(NodeIdentifier nodeIdentifier,
@@ -112,10 +112,10 @@ public class MultiTenantRpcAuthorizer implements RpcAuthorizer {
} else {
String hostname = configRequest.getClientHostName();
Optional<RequestHandler> tenantHandler =
- Optional.of(hostRegistry.getKeyForHost(hostname))
+ Optional.ofNullable(hostRegistry.getKeyForHost(hostname))
.flatMap(this::getTenantHandler);
if (tenantHandler.isEmpty()) {
- return; // unknown tenant
+ return; // unknown host
}
ApplicationId resolvedApplication = tenantHandler.get().resolveApplicationId(hostname);
ApplicationId peerOwner = applicationId(peerIdentity);
@@ -158,7 +158,8 @@ public class MultiTenantRpcAuthorizer implements RpcAuthorizer {
private void handleAuthorizationFailure(Request request, Throwable throwable) {
String errorMessage = String.format("For request '%s' from '%s': %s", request.methodName(), request.target().toString(), throwable.getMessage());
- log.log(LogLevel.WARNING, errorMessage, throwable);
+ log.log(LogLevel.WARNING, errorMessage);
+ log.log(LogLevel.DEBUG, throwable, throwable::getMessage);
if (mode == Mode.ENFORCE) {
JrtErrorCode error = throwable instanceof AuthorizationException ? JrtErrorCode.UNAUTHORIZED : JrtErrorCode.AUTHORIZATION_FAILED;
request.setError(error.code, errorMessage);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
index 24eb862c528..a31737b6e13 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSessionRepo.java
@@ -187,7 +187,7 @@ public class RemoteSessionRepo extends SessionRepo<RemoteSession> {
if (applicationRepo.requireActiveSessionOf(applicationId) == session.getSessionId()) {
log.log(LogLevel.DEBUG, () -> "Found active application for session " + session.getSessionId() + " , loading it");
reloadHandler.reloadConfig(session.ensureApplicationLoaded());
- log.log(LogLevel.INFO, session.logPre() + "Application activated successfully: " + applicationId);
+ log.log(LogLevel.INFO, session.logPre() + "Application activated successfully: " + applicationId + " (generation " + session.getSessionId() + ")");
return;
}
}
diff --git a/container-search/src/main/java/com/yahoo/fs4/MapEncoder.java b/container-search/src/main/java/com/yahoo/fs4/MapEncoder.java
index 3e45d7e5f53..f7e01bc1254 100644
--- a/container-search/src/main/java/com/yahoo/fs4/MapEncoder.java
+++ b/container-search/src/main/java/com/yahoo/fs4/MapEncoder.java
@@ -21,7 +21,9 @@ public class MapEncoder {
// TODO: Time to refactor
private static byte [] getUtf8(Object value) {
- if (value instanceof Tensor) {
+ if (value == null) {
+ return Utf8.toBytes("");
+ } else if (value instanceof Tensor) {
return TypedBinaryFormat.encode((Tensor)value);
} else {
return Utf8.toBytes(value.toString());
@@ -70,11 +72,7 @@ public class MapEncoder {
buffer.putInt(utf8.length);
buffer.put(utf8);
Object value = entry.getValue();
- if (value == null) {
- utf8 = Utf8.toBytes("");
- } else {
- utf8 = getUtf8(value);
- }
+ utf8 = getUtf8(value);
buffer.putInt(utf8.length);
buffer.put(utf8);
}
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java
index 39a1587afea..13041aa4035 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java
@@ -5,6 +5,8 @@ import ai.vespa.searchlib.searchprotocol.protobuf.SearchProtocol.StringProperty;
import ai.vespa.searchlib.searchprotocol.protobuf.SearchProtocol.TensorProperty;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
+import com.yahoo.data.access.simple.Value;
+import com.yahoo.data.access.slime.SlimeAdapter;
import com.yahoo.document.GlobalId;
import com.yahoo.fs4.GetDocSumsPacket;
import com.yahoo.io.GrowableByteBuffer;
@@ -23,6 +25,7 @@ import com.yahoo.search.query.Sorting.Order;
import com.yahoo.search.result.Coverage;
import com.yahoo.search.result.Relevance;
import com.yahoo.searchlib.aggregation.Grouping;
+import com.yahoo.slime.BinaryFormat;
import com.yahoo.vespa.objects.BufferSerializer;
import java.nio.ByteBuffer;
@@ -221,7 +224,12 @@ public class ProtobufSerialization {
if(sorting != null) {
result.hits().setSorted(true);
}
-
+ var slimeTrace = protobuf.getSlimeTrace();
+ if (slimeTrace != null && !slimeTrace.isEmpty()) {
+ var traces = new Value.ArrayValue();
+ traces.add(new SlimeAdapter(BinaryFormat.decode(slimeTrace.toByteArray()).get()));
+ query.trace(traces, query.getTraceLevel());
+ }
return result;
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
index 43ae29ee922..03b3d586b73 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.configserver;
+import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
@@ -72,7 +73,12 @@ public interface ConfigServer {
NodeRepository nodeRepository();
/** Get service convergence status for given deployment */
- Optional<ServiceConvergence> serviceConvergence(DeploymentId deployment);
+ default Optional<ServiceConvergence> serviceConvergence(DeploymentId deployment) {
+ return serviceConvergence(deployment, Optional.empty());
+ }
+
+ /** Get service convergence status for given deployment, using the nodes in the model at the given Vespa version. */
+ Optional<ServiceConvergence> serviceConvergence(DeploymentId deployment, Optional<Version> version);
/** Get all load balancers in given zone */
List<LoadBalancer> getLoadBalancers(ZoneId zone);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
index 84e15deea4c..d989a9dfbe6 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
@@ -18,6 +18,7 @@ import com.yahoo.vespa.hosted.controller.application.ApplicationActivity;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
+import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.application.RotationStatus;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
@@ -56,7 +57,7 @@ public class Application {
private final OptionalInt majorVersion;
private final ApplicationMetrics metrics;
private final Optional<String> pemDeployKey;
- private final Optional<RotationId> rotation;
+ private final List<RotationId> rotations;
private final Map<HostName, RotationStatus> rotationStatus;
/** Creates an empty application */
@@ -65,7 +66,7 @@ public class Application {
new DeploymentJobs(OptionalLong.empty(), Collections.emptyList(), Optional.empty(), false),
Change.empty(), Change.empty(), Optional.empty(), Optional.empty(), OptionalInt.empty(),
new ApplicationMetrics(0, 0),
- Optional.empty(), Optional.empty(), Collections.emptyMap());
+ Optional.empty(), Collections.emptyList(), Collections.emptyMap());
}
/** Used from persistence layer: Do not use */
@@ -73,18 +74,18 @@ public class Application {
List<Deployment> deployments, DeploymentJobs deploymentJobs, Change change,
Change outstandingChange, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Optional<String> pemDeployKey,
- Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) {
+ List<RotationId> rotations, Map<HostName, RotationStatus> rotationStatus) {
this(id, createdAt, deploymentSpec, validationOverrides,
deployments.stream().collect(Collectors.toMap(Deployment::zone, Function.identity())),
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
Application(ApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides,
Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Change change,
Change outstandingChange, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Optional<String> pemDeployKey,
- Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) {
+ List<RotationId> rotations, Map<HostName, RotationStatus> rotationStatus) {
this.id = Objects.requireNonNull(id, "id cannot be null");
this.createdAt = Objects.requireNonNull(createdAt, "instant of creation cannot be null");
this.deploymentSpec = Objects.requireNonNull(deploymentSpec, "deploymentSpec cannot be null");
@@ -98,7 +99,7 @@ public class Application {
this.majorVersion = Objects.requireNonNull(majorVersion, "majorVersion cannot be null");
this.metrics = Objects.requireNonNull(metrics, "metrics cannot be null");
this.pemDeployKey = pemDeployKey;
- this.rotation = Objects.requireNonNull(rotation, "rotation cannot be null");
+ this.rotations = List.copyOf(Objects.requireNonNull(rotations, "rotations cannot be null"));
this.rotationStatus = ImmutableMap.copyOf(Objects.requireNonNull(rotationStatus, "rotationStatus cannot be null"));
}
@@ -195,13 +196,15 @@ public class Application {
}
/** Returns the global rotation id of this, if present */
- public Optional<RotationId> rotation() {
- return rotation;
+ public List<RotationId> rotations() {
+ return rotations;
}
/** Returns the default global endpoints for this in given system */
public EndpointList endpointsIn(SystemName system) {
- if (rotation.isEmpty()) return EndpointList.EMPTY;
+ // TODO: Do we need to change something here? .defaultGlobalId seems like it is
+ // TODO: making some assumptions on naming.
+ if (rotations.isEmpty()) return EndpointList.EMPTY;
return EndpointList.defaultGlobal(id, system);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index 8231de59249..de5d8de1f9e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -327,7 +327,7 @@ public class ApplicationController {
// Include global DNS names
cnames = app.endpointsIn(controller.system()).asList().stream().map(Endpoint::dnsName).collect(Collectors.toSet());
// Include rotation ID to ensure that deployment can respond to health checks with rotation ID as Host header
- app.rotation().map(RotationId::asString).ifPresent(cnames::add);
+ app.rotations().stream().map(RotationId::asString).forEach(cnames::add);
// Update application with information from application package
if ( ! preferOldestVersion
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
index 943ede5197b..e8d69f8a577 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
@@ -27,6 +27,7 @@ import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import java.time.Instant;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -55,7 +56,7 @@ public class LockedApplication {
private final OptionalInt majorVersion;
private final ApplicationMetrics metrics;
private final Optional<String> pemDeployKey;
- private final Optional<RotationId> rotation;
+ private final List<RotationId> rotations;
private final Map<HostName, RotationStatus> rotationStatus;
/**
@@ -70,7 +71,7 @@ public class LockedApplication {
application.deployments(),
application.deploymentJobs(), application.change(), application.outstandingChange(),
application.ownershipIssueId(), application.owner(), application.majorVersion(), application.metrics(),
- application.pemDeployKey(), application.rotation(), application.rotationStatus());
+ application.pemDeployKey(), application.rotations(), application.rotationStatus());
}
private LockedApplication(Lock lock, ApplicationId id, Instant createdAt,
@@ -78,7 +79,7 @@ public class LockedApplication {
Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Change change,
Change outstandingChange, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Optional<String> pemDeployKey,
- Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) {
+ List<RotationId> rotations, Map<HostName, RotationStatus> rotationStatus) {
this.lock = lock;
this.id = id;
this.createdAt = createdAt;
@@ -93,7 +94,7 @@ public class LockedApplication {
this.majorVersion = majorVersion;
this.metrics = metrics;
this.pemDeployKey = pemDeployKey;
- this.rotation = rotation;
+ this.rotations = rotations;
this.rotationStatus = rotationStatus;
}
@@ -101,35 +102,35 @@ public class LockedApplication {
public Application get() {
return new Application(id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change,
outstandingChange, ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withBuiltInternally(boolean builtInternally) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withBuiltInternally(builtInternally), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withProjectId(OptionalLong projectId) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withProjectId(projectId), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withDeploymentIssueId(IssueId issueId) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.with(issueId), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withJobPause(JobType jobType, OptionalLong pausedUntil) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withPause(jobType, pausedUntil), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withJobCompletion(long projectId, JobType jobType, JobStatus.JobRun completion,
@@ -137,14 +138,14 @@ public class LockedApplication {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withCompletion(projectId, jobType, completion, jobError),
change, outstandingChange, ownershipIssueId, owner, majorVersion, metrics,
- pemDeployKey, rotation, rotationStatus);
+ pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withJobTriggering(JobType jobType, JobStatus.JobRun job) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.withTriggering(jobType, job), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication withNewDeployment(ZoneId zone, ApplicationVersion applicationVersion, Version version,
@@ -195,45 +196,45 @@ public class LockedApplication {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs.without(jobType), change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication with(DeploymentSpec deploymentSpec) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange,
ownershipIssueId, owner, majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
public LockedApplication with(ValidationOverrides validationOverrides) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withChange(Change change) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withOutstandingChange(Change outstandingChange) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withOwnershipIssueId(IssueId issueId) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, Optional.ofNullable(issueId), owner,
- majorVersion, metrics, pemDeployKey, rotation, rotationStatus);
+ majorVersion, metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withOwner(User owner) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId,
Optional.ofNullable(owner), majorVersion, metrics, pemDeployKey,
- rotation, rotationStatus);
+ rotations, rotationStatus);
}
/** Set a major version for this, or set to null to remove any major version override */
@@ -241,31 +242,31 @@ public class LockedApplication {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner,
majorVersion == null ? OptionalInt.empty() : OptionalInt.of(majorVersion),
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication with(MetricsService.ApplicationMetrics metrics) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
public LockedApplication withPemDeployKey(String pemDeployKey) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, Optional.ofNullable(pemDeployKey), rotation, rotationStatus);
+ metrics, Optional.ofNullable(pemDeployKey), rotations, rotationStatus);
}
public LockedApplication with(RotationId rotation) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, Optional.of(rotation), rotationStatus);
+ metrics, pemDeployKey, List.of(rotation), rotationStatus);
}
public LockedApplication withRotationStatus(Map<HostName, RotationStatus> rotationStatus) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
/** Don't expose non-leaf sub-objects. */
@@ -278,7 +279,7 @@ public class LockedApplication {
private LockedApplication with(Map<ZoneId, Deployment> deployments) {
return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments,
deploymentJobs, change, outstandingChange, ownershipIssueId, owner, majorVersion,
- metrics, pemDeployKey, rotation, rotationStatus);
+ metrics, pemDeployKey, rotations, rotationStatus);
}
@Override
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index 1b90a43421b..6c9b8dd0784 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -119,7 +119,7 @@ public class Endpoint {
private static String separator(SystemName system, boolean directRouting, boolean tls) {
if (!tls) return ".";
if (directRouting) return ".";
- if (isPublic(system)) return ".";
+ if (system.isPublic()) return ".";
return "--";
}
@@ -141,7 +141,7 @@ public class Endpoint {
}
private static String systemPart(SystemName system, String separator) {
- if (system == SystemName.main || isPublic(system)) return "";
+ if (!system.isCd()) return "";
return system.value() + separator;
}
@@ -160,10 +160,6 @@ public class Endpoint {
}
}
- private static boolean isPublic(SystemName system) { // TODO: Remove and inline once we're down to one
- return system == SystemName.Public || system == SystemName.vaas;
- }
-
/** An endpoint's scope */
public enum Scope {
@@ -277,7 +273,7 @@ public class Endpoint {
} else {
throw new IllegalArgumentException("Must set either cluster or rotation target");
}
- if (isPublic(system) && !directRouting) {
+ if (system.isPublic() && !directRouting) {
throw new IllegalArgumentException("Public system only supports direct routing endpoints");
}
if (directRouting && !port.isDefault()) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java
index 26233c998f8..3eeaf09c10b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/SystemApplication.java
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.application;
+import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.zone.ZoneId;
@@ -9,6 +10,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ServiceConvergence;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -61,11 +63,11 @@ public enum SystemApplication {
}
/** Returns whether config for this application has converged in given zone */
- public boolean configConvergedIn(ZoneId zone, Controller controller) {
+ public boolean configConvergedIn(ZoneId zone, Controller controller, Optional<Version> version) {
if (!hasApplicationPackage()) {
return true;
}
- return controller.configServer().serviceConvergence(new DeploymentId(id(), zone))
+ return controller.configServer().serviceConvergence(new DeploymentId(id(), zone), version)
.map(ServiceConvergence::converged)
.orElse(false);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java
index c467a4a0acd..aefe8ae7b48 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java
@@ -109,6 +109,7 @@ public class AuditLog {
public enum Method {
POST,
PATCH,
+ PUT,
DELETE
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
index 7a42456f2ac..68e84c7d289 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
@@ -268,7 +268,7 @@ public class InternalStepRunner implements StepRunner {
logger.log("Checking installation of " + platform + " and " + application.id() + " ...");
if ( nodesConverged(id.application(), id.type(), platform, logger)
- && servicesConverged(id.application(), id.type(), logger)) {
+ && servicesConverged(id.application(), id.type(), platform, logger)) {
if (endpointsAvailable(id.application(), id.type().zone(controller.system()), logger)) {
logger.log("Installation succeeded!");
return Optional.of(running);
@@ -298,7 +298,7 @@ public class InternalStepRunner implements StepRunner {
Version platform = controller.jobController().run(id).get().versions().targetPlatform();
logger.log("Checking installation of tester container ...");
if ( nodesConverged(id.tester().id(), id.type(), platform, logger)
- && servicesConverged(id.tester().id(), id.type(), logger)) {
+ && servicesConverged(id.tester().id(), id.type(), platform, logger)) {
if (endpointsAvailable(id.tester().id(), id.type().zone(controller.system()), logger)) {
logger.log("Tester container successfully installed!");
return Optional.of(running);
@@ -354,9 +354,10 @@ public class InternalStepRunner implements StepRunner {
&& node.rebootGeneration() >= node.wantedRebootGeneration());
}
- private boolean servicesConverged(ApplicationId id, JobType type, DualLogger logger) {
- Optional<ServiceConvergence> convergence = controller.configServer().serviceConvergence(new DeploymentId(id, type.zone(controller.system())));
- if ( ! convergence.isPresent()) {
+ private boolean servicesConverged(ApplicationId id, JobType type, Version platform, DualLogger logger) {
+ var convergence = controller.configServer().serviceConvergence(new DeploymentId(id, type.zone(controller.system())),
+ Optional.of(platform));
+ if (convergence.isEmpty()) {
logger.log("Config status not currently available -- will retry.");
return false;
}
@@ -519,7 +520,7 @@ public class InternalStepRunner implements StepRunner {
});
}
catch (IllegalStateException e) {
- logger.log(INFO, "Job '" + id.type() + "'no longer supposed to run?:", e);
+ logger.log(INFO, "Job '" + id.type() + "' no longer supposed to run?", e);
return Optional.of(error);
}
return Optional.of(running);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java
index b17ca52d835..3cd3d969731 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java
@@ -43,11 +43,6 @@ public class CreateRecord implements NameServiceRequest {
}
@Override
- public List<Record> affectedRecords() {
- return List.of(record);
- }
-
- @Override
public String toString() {
return "create record " + record;
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java
index ec943676962..9dd02735638 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java
@@ -52,11 +52,6 @@ public class CreateRecords implements NameServiceRequest {
}
@Override
- public List<Record> affectedRecords() {
- return records();
- }
-
- @Override
public String toString() {
return "create records " + records();
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceForwarder.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceForwarder.java
index f4bea8b1083..299ea168c7a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceForwarder.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceForwarder.java
@@ -39,24 +39,24 @@ public class NameServiceForwarder {
}
/** Create or update a CNAME record with given name and data */
- public Record createCname(RecordName name, RecordData canonicalName, NameServiceQueue.Priority priority) {
- return forward(new CreateRecord(new Record(Record.Type.CNAME, name, canonicalName)), priority).affectedRecords().get(0);
+ public void createCname(RecordName name, RecordData canonicalName, NameServiceQueue.Priority priority) {
+ forward(new CreateRecord(new Record(Record.Type.CNAME, name, canonicalName)), priority);
}
/** Create or update an ALIAS record with given name and targets */
- public List<Record> createAlias(RecordName name, Set<AliasTarget> targets, NameServiceQueue.Priority priority) {
+ public void createAlias(RecordName name, Set<AliasTarget> targets, NameServiceQueue.Priority priority) {
var records = targets.stream().map(AliasTarget::asData)
.map(data -> new Record(Record.Type.ALIAS, name, data))
.collect(Collectors.toList());
- return forward(new CreateRecords(records), priority).affectedRecords();
+ forward(new CreateRecords(records), priority);
}
/** Create or update a TXT record with given name and data */
- public List<Record> createTxt(RecordName name, List<RecordData> txtData, NameServiceQueue.Priority priority) {
+ public void createTxt(RecordName name, List<RecordData> txtData, NameServiceQueue.Priority priority) {
var records = txtData.stream()
.map(data -> new Record(Record.Type.TXT, name, data))
.collect(Collectors.toList());
- return forward(new CreateRecords(records), priority).affectedRecords();
+ forward(new CreateRecords(records), priority);
}
/** Remove all records of given type and name */
@@ -69,7 +69,7 @@ public class NameServiceForwarder {
forward(new RemoveRecords(type, data), priority);
}
- private NameServiceRequest forward(NameServiceRequest request, NameServiceQueue.Priority priority) {
+ private void forward(NameServiceRequest request, NameServiceQueue.Priority priority) {
try (Lock lock = db.lockNameServiceQueue()) {
NameServiceQueue queue = db.readNameServiceQueue();
var queued = queue.requests().size();
@@ -80,7 +80,6 @@ public class NameServiceForwarder {
}
db.writeNameServiceQueue(queue.with(request, priority).last(maxQueuedRequests));
}
- return request;
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java
index cc49e589cbb..684fb091d92 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java
@@ -72,7 +72,7 @@ public class NameServiceQueue {
if (requests.isEmpty()) return this;
var queue = new NameServiceQueue(requests);
- for (var i = 0; i < n && !queue.requests.isEmpty(); i++) {
+ for (int i = 0; i < n && !queue.requests.isEmpty(); i++) {
var request = queue.requests.peek();
try {
request.dispatchTo(nameService);
@@ -97,11 +97,13 @@ public class NameServiceQueue {
/** Priority of a request added to this */
public enum Priority {
+
/** Default priority. Request will be delivered in FIFO order */
normal,
- /**Request is queued before others. Useful for code that needs to act on effects of a request */
+ /** Request is queued first. Useful for code that needs to act on effects of a request */
high
+
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java
index a01719ccc88..65076694160 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java
@@ -2,9 +2,6 @@
package com.yahoo.vespa.hosted.controller.dns;
import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
-
-import java.util.List;
/**
* Interface for requests to a {@link NameService}.
@@ -16,7 +13,4 @@ public interface NameServiceRequest {
/** Send this to given name service */
void dispatchTo(NameService nameService);
- /** Returns the records affected by executing this */
- List<Record> affectedRecords();
-
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java
index b721f66e452..ddc4d157afd 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java
@@ -61,11 +61,6 @@ public class RemoveRecords implements NameServiceRequest {
}
@Override
- public List<Record> affectedRecords() {
- return List.of();
- }
-
- @Override
public String toString() {
return "remove records of type " + type + ", by " +
name.map(n -> "name " + n).orElse("") +
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
index 0cf89d798a7..4ad5940f8f2 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.config.provision.HostName;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.ApplicationController;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -20,6 +21,7 @@ import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -39,7 +41,8 @@ public class DeploymentMetricsMaintainer extends Maintainer {
private final ApplicationController applications;
public DeploymentMetricsMaintainer(Controller controller, Duration duration, JobControl jobControl) {
- super(controller, duration, jobControl);
+ super(controller, duration, jobControl, DeploymentMetricsMaintainer.class.getSimpleName(),
+ SystemName.allOf(Predicate.not(SystemName::isPublic)));
this.applications = controller.applications();
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/NameServiceDispatcher.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/NameServiceDispatcher.java
index 8878ac9bd5b..d4dc068c71f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/NameServiceDispatcher.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/NameServiceDispatcher.java
@@ -41,6 +41,7 @@ public class NameServiceDispatcher extends Maintainer {
try (Lock lock = db.lockNameServiceQueue()) {
NameServiceQueue queue = db.readNameServiceQueue();
NameServiceQueue remaining = queue.dispatchTo(nameService, requestCount);
+ if (queue == remaining) return; // Queue unchanged
db.writeNameServiceQueue(remaining);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java
index 62d401cf478..3c2455b314a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java
@@ -42,7 +42,7 @@ public class SystemUpgrader extends InfrastructureUpgrader {
protected boolean convergedOn(Version target, SystemApplication application, ZoneId zone) {
return minVersion(zone, application, Node::currentVersion).map(target::equals)
.orElse(true)
- && application.configConvergedIn(zone, controller());
+ && application.configConvergedIn(zone, controller(), Optional.of(target));
}
@Override
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
index 3c2cbade606..3dd091919aa 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
@@ -70,7 +70,8 @@ public class ApplicationSerializer {
private final String writeQualityField = "writeQuality";
private final String queryQualityField = "queryQuality";
private final String pemDeployKeyField = "pemDeployKey";
- private final String rotationField = "rotation";
+ private final String rotationsField = "endpoints";
+ private final String deprecatedRotationField = "rotation";
private final String rotationStatusField = "rotationStatus";
// Deployment fields
@@ -162,7 +163,8 @@ public class ApplicationSerializer {
root.setDouble(queryQualityField, application.metrics().queryServiceQuality());
root.setDouble(writeQualityField, application.metrics().writeServiceQuality());
application.pemDeployKey().ifPresent(pemDeployKey -> root.setString(pemDeployKeyField, pemDeployKey));
- application.rotation().ifPresent(rotation -> root.setString(rotationField, rotation.asString()));
+ Cursor rotations = root.setArray(rotationsField);
+ application.rotations().forEach(rotation -> rotations.addString(rotation.asString()));
toSlime(application.rotationStatus(), root.setArray(rotationStatusField));
return slime;
}
@@ -329,12 +331,12 @@ public class ApplicationSerializer {
ApplicationMetrics metrics = new ApplicationMetrics(root.field(queryQualityField).asDouble(),
root.field(writeQualityField).asDouble());
Optional<String> pemDeployKey = optionalString(root.field(pemDeployKeyField));
- Optional<RotationId> rotation = rotationFromSlime(root.field(rotationField));
+ List<RotationId> rotations = rotationsFromSlime(root);
Map<HostName, RotationStatus> rotationStatus = rotationStatusFromSlime(root.field(rotationStatusField));
return new Application(id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs,
deploying, outstandingChange, ownershipIssueId, owner, majorVersion, metrics,
- pemDeployKey, rotation, rotationStatus);
+ pemDeployKey, rotations, rotationStatus);
}
private List<Deployment> deploymentsFromSlime(Inspector array) {
@@ -514,7 +516,26 @@ public class ApplicationSerializer {
Instant.ofEpochMilli(object.field(atField).asLong())));
}
- private Optional<RotationId> rotationFromSlime(Inspector field) {
+ private List<RotationId> rotationsFromSlime(Inspector root) {
+ final var rotations = rotationListFromSlime(root.field(rotationsField));
+ final var legacyRotation = legacyRotationFromSlime(root.field(deprecatedRotationField));
+ legacyRotation.ifPresent(rotations::add);
+ return rotations;
+ }
+
+ private List<RotationId> rotationListFromSlime(Inspector field) {
+ final var rotations = new ArrayList<RotationId>();
+
+ field.traverse((ArrayTraverser) (idx, inspector) -> {
+ final var rotation = new RotationId(inspector.asString());
+ rotations.add(rotation);
+ });
+
+ return rotations;
+ }
+
+ // TODO: Remove after June 2019 once the 'rotation' field is gone from storage
+ private Optional<RotationId> legacyRotationFromSlime(Inspector field) {
return field.valid() ? optionalString(field).map(RotationId::new) : Optional.empty();
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializer.java
index 5bcb155efcb..7fee9a7f9b4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializer.java
@@ -60,6 +60,7 @@ public class AuditLogSerializer {
switch (method) {
case POST: return "POST";
case PATCH: return "PATCH";
+ case PUT: return "PUT";
case DELETE: return "DELETE";
default: throw new IllegalArgumentException("No serialization defined for method " + method);
}
@@ -69,6 +70,7 @@ public class AuditLogSerializer {
switch (field.asString()) {
case "POST": return AuditLog.Entry.Method.POST;
case "PATCH": return AuditLog.Entry.Method.PATCH;
+ case "PUT": return AuditLog.Entry.Method.PUT;
case "DELETE": return AuditLog.Entry.Method.DELETE;
default: throw new IllegalArgumentException("Unknown serialized value '" + field.asString() + "'");
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index d231b034e8a..367c9528e8c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -496,7 +496,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
.map(URI::toString)
.forEach(globalRotationsArray::addString);
- application.rotation().ifPresent(rotation -> object.setString("rotationId", rotation.asString()));
+
+ application.rotations().stream().findFirst().ifPresent(rotation -> object.setString("rotationId", rotation.asString()));
// Per-cluster rotations
Set<RoutingPolicy> routingPolicies = controller.applications().routingPolicies().get(application.id());
@@ -515,7 +516,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
for (Deployment deployment : deployments) {
Cursor deploymentObject = instancesArray.addObject();
- if (application.rotation().isPresent() && deployment.zone().environment() == Environment.prod) {
+ if ((! application.rotations().isEmpty()) && deployment.zone().environment() == Environment.prod) {
toSlime(application.rotationStatus(deployment), deploymentObject);
}
@@ -707,7 +708,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
ApplicationId applicationId = ApplicationId.from(tenantName, applicationName, instanceName);
Application application = controller.applications().require(applicationId);
ZoneId zone = ZoneId.from(environment, region);
- if (!application.rotation().isPresent()) {
+ if (application.rotations().isEmpty()) {
throw new NotExistsException("global rotation does not exist for " + application);
}
Deployment deployment = application.deployments().get(zone);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java
index b3953c47c01..d2b16721503 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java
@@ -47,7 +47,7 @@ public class RotationRepository {
/** Get rotation for given application */
public Optional<Rotation> getRotation(Application application) {
- return application.rotation().map(allRotations::get);
+ return application.rotations().stream().map(allRotations::get).findFirst();
}
/**
@@ -60,8 +60,8 @@ public class RotationRepository {
* @param lock Lock which must be acquired by the caller
*/
public Rotation getOrAssignRotation(Application application, RotationLock lock) {
- if (application.rotation().isPresent()) {
- return allRotations.get(application.rotation().get());
+ if (! application.rotations().isEmpty()) {
+ return allRotations.get(application.rotations().get(0));
}
if (application.deploymentSpec().globalServiceId().isEmpty()) {
throw new IllegalArgumentException("global-service-id is not set in deployment spec");
@@ -81,8 +81,8 @@ public class RotationRepository {
*/
public Map<RotationId, Rotation> availableRotations(@SuppressWarnings("unused") RotationLock lock) {
List<RotationId> assignedRotations = applications.asList().stream()
- .filter(application -> application.rotation().isPresent())
- .map(application -> application.rotation().get())
+ .filter(application -> ! application.rotations().isEmpty())
+ .flatMap(application -> application.rotations().stream())
.collect(Collectors.toList());
Map<RotationId, Rotation> unassignedRotations = new LinkedHashMap<>(this.allRotations);
assignedRotations.forEach(unassignedRotations::remove);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java
index e2d4c90f443..879575669cd 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VersionStatus.java
@@ -157,7 +157,7 @@ public class VersionStatus {
ListMap<Version, HostName> versions = new ListMap<>();
for (ZoneId zone : zones) {
for (SystemApplication application : SystemApplication.all()) {
- boolean configConverged = application.configConvergedIn(zone, controller);
+ boolean configConverged = application.configConvergedIn(zone, controller, Optional.empty());
if (!configConverged) {
log.log(LogLevel.WARNING, "Config for " + application.id() + " in " + zone + " has not converged");
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index 7dcb4aba138..b89941b5d2d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -436,7 +436,7 @@ public class ControllerTest {
.build();
tester.deployCompletely(app1, applicationPackage);
app1 = tester.applications().require(app1.id());
- assertEquals("rotation-id-02", app1.rotation().get().asString());
+ assertEquals("rotation-id-02", app1.rotations().get(0).asString());
// Existing DNS records are updated to point to the newly assigned rotation
assertEquals(6, tester.controllerTester().nameService().records().size());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java
index 6470ce3663f..67979571f73 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java
@@ -24,11 +24,10 @@ import static org.junit.Assert.assertTrue;
public class AuditLoggerTest {
private final ControllerTester tester = new ControllerTester();
+ private final Supplier<AuditLog> log = () -> tester.controller().auditLogger().readLog();
@Test
public void test_logging() {
- Supplier<AuditLog> log = () -> tester.controller().auditLogger().readLog();
-
{ // GET request is ignored
HttpRequest request = testRequest(Method.GET, URI.create("http://localhost:8080/os/v1/"), "");
tester.controller().auditLogger().log(request);
@@ -40,11 +39,8 @@ public class AuditLoggerTest {
String data = "{\"cloud\":\"cloud9\",\"version\":\"42.0\"}";
HttpRequest request = testRequest(Method.PATCH, url, data);
tester.controller().auditLogger().log(request);
-
- assertEquals(instant(), log.get().entries().get(0).at());
+ assertEntry(Entry.Method.PATCH, 1, "/os/v1/?foo=bar");
assertEquals("user", log.get().entries().get(0).principal());
- assertEquals(Entry.Method.PATCH, log.get().entries().get(0).method());
- assertEquals("/os/v1/?foo=bar", log.get().entries().get(0).resource());
assertEquals(data, log.get().entries().get(0).data().get());
}
@@ -53,9 +49,31 @@ public class AuditLoggerTest {
HttpRequest request = testRequest(Method.PATCH, URI.create("http://localhost:8080/os/v1/"),
"{\"cloud\":\"cloud9\",\"version\":\"43.0\"}");
tester.controller().auditLogger().log(request);
- assertEquals(2, log.get().entries().size());
- assertEquals(instant(), log.get().entries().get(0).at());
- assertEquals("/os/v1/", log.get().entries().get(0).resource());
+ assertEntry(Entry.Method.PATCH, 2, "/os/v1/");
+ }
+
+ { // PUT is logged
+ tester.clock().advance(Duration.ofDays(1));
+ HttpRequest request = testRequest(Method.PUT, URI.create("http://localhost:8080/zone/v2/prod/us-north-1/nodes/v2/state/dirty/node1/"),
+ "");
+ tester.controller().auditLogger().log(request);
+ assertEntry(Entry.Method.PUT, 3, "/zone/v2/prod/us-north-1/nodes/v2/state/dirty/node1/");
+ }
+
+ { // DELETE is logged
+ tester.clock().advance(Duration.ofDays(1));
+ HttpRequest request = testRequest(Method.DELETE, URI.create("http://localhost:8080/zone/v2/prod/us-north-1/nodes/v2/node/node1"),
+ "");
+ tester.controller().auditLogger().log(request);
+ assertEntry(Entry.Method.DELETE, 4, "/zone/v2/prod/us-north-1/nodes/v2/node/node1");
+ }
+
+ { // POST is logged
+ tester.clock().advance(Duration.ofDays(1));
+ HttpRequest request = testRequest(Method.POST, URI.create("http://localhost:8080/controller/v1/jobs/upgrader/confidence/6.42"),
+ "6.42");
+ tester.controller().auditLogger().log(request);
+ assertEntry(Entry.Method.POST, 5, "/controller/v1/jobs/upgrader/confidence/6.42");
}
{ // 14 days pass and another PATCH request is logged. Older entries are removed due to expiry
@@ -63,8 +81,7 @@ public class AuditLoggerTest {
HttpRequest request = testRequest(Method.PATCH, URI.create("http://localhost:8080/os/v1/"),
"{\"cloud\":\"cloud9\",\"version\":\"44.0\"}");
tester.controller().auditLogger().log(request);
- assertEquals(1, log.get().entries().size());
- assertEquals(instant(), log.get().entries().get(0).at());
+ assertEntry(Entry.Method.PATCH, 1, "/os/v1/");
}
}
@@ -72,6 +89,13 @@ public class AuditLoggerTest {
return tester.clock().instant().truncatedTo(MILLIS);
}
+ private void assertEntry(Entry.Method method, int logSize, String resource) {
+ assertEquals(logSize, log.get().entries().size());
+ assertEquals(instant(), log.get().entries().get(0).at());
+ assertEquals(method, log.get().entries().get(0).method());
+ assertEquals(resource, log.get().entries().get(0).resource());
+ }
+
private static HttpRequest testRequest(Method method, URI url, String data) {
HttpRequest request = HttpRequest.createTestRequest(
url.toString(),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
index 913c9a800c1..31b48bea883 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
@@ -195,7 +195,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
}
@Override
- public Optional<ServiceConvergence> serviceConvergence(DeploymentId deployment) {
+ public Optional<ServiceConvergence> serviceConvergence(DeploymentId deployment, Optional<Version> version) {
return Optional.ofNullable(serviceStatus.get(deployment));
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
index d7c0ac9fe9e..084ac593f4c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
@@ -29,6 +29,7 @@ import com.yahoo.vespa.hosted.controller.application.RotationStatus;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import org.junit.Test;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -115,7 +116,7 @@ public class ApplicationSerializerTest {
OptionalInt.of(7),
new MetricsService.ApplicationMetrics(0.5, 0.9),
Optional.of("-----BEGIN PUBLIC KEY-----\n∠( ᐛ 」∠)_\n-----END PUBLIC KEY-----"),
- Optional.of(new RotationId("my-rotation")),
+ List.of(new RotationId("my-rotation")),
rotationStatus);
Application serialized = applicationSerializer.fromSlime(applicationSerializer.toSlime(original));
@@ -151,7 +152,7 @@ public class ApplicationSerializerTest {
assertEquals(original.change(), serialized.change());
assertEquals(original.pemDeployKey(), serialized.pemDeployKey());
- assertEquals(original.rotation().get(), serialized.rotation().get());
+ assertEquals(original.rotations(), serialized.rotations());
assertEquals(original.rotationStatus(), serialized.rotationStatus());
// Test cluster utilization
@@ -244,4 +245,32 @@ public class ApplicationSerializerTest {
// ok if no error
}
+ /** TODO: Test can be removed after June 2019 - once legacy field for single rotation is retired */
+ @Test
+ public void testParsingLegacyRotationElement() throws IOException {
+ // Use the 'complete-application.json' as a baseline
+ final var applicationJson = Files.readAllBytes(testData.resolve("complete-application.json"));
+ final var slime = SlimeUtils.jsonToSlime(applicationJson);
+
+ // Add the necessary fields to the Slime representation of the application
+ final var cursor = slime.get();
+
+ cursor.setString("rotation", "single-rotation");
+
+ final var rotations = cursor.setArray("endpoints");
+ rotations.addString("multiple-rotation-1");
+ rotations.addString("multiple-rotation-2");
+
+ // Parse and test the output from parsing contains both legacy rotation and multiple rotations
+ final var application = applicationSerializer.fromSlime(slime);
+
+ assertEquals(
+ List.of(
+ new RotationId("multiple-rotation-1"),
+ new RotationId("multiple-rotation-2"),
+ new RotationId("single-rotation")
+ ),
+ application.rotations()
+ );
+ }
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
index 8d1f40260e3..02a82e35f10 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
@@ -14,10 +14,11 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.net.URI;
+import java.util.List;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* @author Oyvind Gronnesby
@@ -64,7 +65,7 @@ public class RotationRepositoryTest {
Rotation expected = new Rotation(new RotationId("foo-1"), "foo-1.com");
application = tester.applications().require(application.id());
- assertEquals(expected.id(), application.rotation().get());
+ assertEquals(List.of(expected.id()), application.rotations());
assertEquals(URI.create("https://app1--tenant1.global.vespa.oath.cloud:4443/"),
application.endpointsIn(SystemName.main).main().get().url());
try (RotationLock lock = repository.lock()) {
@@ -80,7 +81,7 @@ public class RotationRepositoryTest {
.searchDefinition("search foo { }") // Update application package so there is something to deploy
.build();
tester.deployCompletely(application, applicationPackage, 43);
- assertEquals(expected.id(), tester.applications().require(application.id()).rotation().get());
+ assertEquals(List.of(expected.id()), tester.applications().require(application.id()).rotations());
}
@Test
@@ -139,8 +140,7 @@ public class RotationRepositoryTest {
.build();
tester.deployCompletely(application, applicationPackage);
Application app = tester.applications().require(application.id());
- Optional<RotationId> rotation = app.rotation();
- assertFalse(rotation.isPresent());
+ assertTrue(app.rotations().isEmpty());
}
@Test
@@ -153,7 +153,7 @@ public class RotationRepositoryTest {
Application application = tester.createApplication("app2", "tenant2", 22L,
2L);
tester.deployCompletely(application, applicationPackage);
- assertEquals(new RotationId("foo-1"), tester.applications().require(application.id()).rotation().get());
+ assertEquals(List.of(new RotationId("foo-1")), tester.applications().require(application.id()).rotations());
assertEquals("https://cd--app2--tenant2.global.vespa.oath.cloud:4443/", tester.applications().require(application.id())
.endpointsIn(SystemName.cd).main().get().url().toString());
}
diff --git a/document/src/tests/serialization/vespadocumentserializer_test.cpp b/document/src/tests/serialization/vespadocumentserializer_test.cpp
index c573eef6147..4b5c3e6f195 100644
--- a/document/src/tests/serialization/vespadocumentserializer_test.cpp
+++ b/document/src/tests/serialization/vespadocumentserializer_test.cpp
@@ -40,8 +40,7 @@
#include <vespa/eval/tensor/types.h>
#include <vespa/eval/tensor/tensor_builder.h>
#include <vespa/eval/tensor/tensor.h>
-#include <vespa/eval/tensor/default_tensor.h>
-#include <vespa/eval/tensor/tensor_factory.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/testkit/testapp.h>
@@ -53,10 +52,9 @@ using vespalib::Slime;
using vespalib::nbostream;
using vespalib::nbostream_longlivedbuf;
using vespalib::slime::Cursor;
+using vespalib::eval::TensorSpec;
using vespalib::tensor::Tensor;
-using vespalib::tensor::TensorBuilder;
-using vespalib::tensor::TensorCells;
-using vespalib::tensor::TensorDimensions;
+using vespalib::tensor::DefaultTensorEngine;
using vespalib::compression::CompressionConfig;
using namespace document;
using std::string;
@@ -828,13 +826,14 @@ TEST("Require that predicate deserialization matches Java") {
namespace
{
-Tensor::UP
-createTensor(const TensorCells &cells, const TensorDimensions &dimensions) {
- vespalib::tensor::DefaultTensor::builder builder;
- return vespalib::tensor::TensorFactory::create(cells, dimensions, builder);
+Tensor::UP createTensor(const TensorSpec &spec) {
+ auto value = DefaultTensorEngine::ref().from_spec(spec);
+ Tensor *tensor = dynamic_cast<Tensor*>(value.get());
+ ASSERT_TRUE(tensor != nullptr);
+ value.release();
+ return Tensor::UP(tensor);
}
-
}
TEST("Require that tensors can be serialized")
@@ -846,12 +845,12 @@ TEST("Require that tensors can be serialized")
nbostream stream;
serializeAndDeserialize(noTensorValue, stream);
stream.clear();
- emptyTensorValue = createTensor({}, {"x", "y"});
+ emptyTensorValue = createTensor(TensorSpec("tensor(x{},y{})"));
serializeAndDeserialize(emptyTensorValue, stream);
stream.clear();
- twoCellsTwoDimsValue = createTensor({ {{{"y", "3"}}, 3},
- {{{"x", "4"}, {"y", "5"}}, 7} },
- {"x", "y"});
+ twoCellsTwoDimsValue = createTensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", ""}, {"y", "3"}}, 3)
+ .add({{"x", "4"}, {"y", "5"}}, 7));
serializeAndDeserialize(twoCellsTwoDimsValue, stream);
EXPECT_NOT_EQUAL(noTensorValue, emptyTensorValue);
EXPECT_NOT_EQUAL(noTensorValue, twoCellsTwoDimsValue);
@@ -859,6 +858,7 @@ TEST("Require that tensors can be serialized")
}
+
const int tensor_doc_type_id = 321;
const string tensor_field_name = "my_tensor";
@@ -904,15 +904,15 @@ void checkDeserialization(const string &name, std::unique_ptr<Tensor> tensor) {
deserializeAndCheck(data_dir + name + "__java", value);
}
+
TEST("Require that tensor deserialization matches Java") {
checkDeserialization("non_existing_tensor", std::unique_ptr<Tensor>());
- checkDeserialization("empty_tensor", createTensor({}, {"dimX", "dimY"}));
+ checkDeserialization("empty_tensor", createTensor(TensorSpec("tensor(dimX{},dimY{})")));
checkDeserialization("multi_cell_tensor",
- createTensor({ {{{"dimX", "a"}, {"dimY", "bb"}}, 2.0 },
- {{{"dimX", "ccc"},
- {"dimY", "dddd"}}, 3.0},
- {{{"dimX", "e"},{"dimY","ff"}}, 5.0} },
- { "dimX", "dimY" }));
+ createTensor(TensorSpec("tensor(dimX{},dimY{})")
+ .add({{"dimX", "a"}, {"dimY", "bb"}}, 2.0)
+ .add({{"dimX", "ccc"}, {"dimY", "dddd"}}, 3.0)
+ .add({{"dimX", "e"}, {"dimY", "ff"}}, 5.0)));
}
struct TensorDocFixture {
@@ -980,14 +980,14 @@ DeserializedTensorDoc::getTensor() const
TEST("Require that wrong tensor type hides tensor")
{
TensorDocFixture f(tensor_doc_repo,
- createTensor({ {{{"dimX", "a"},{"dimY", "bb"}}, 2.0 },
- {{{"dimX", "ccc"},{"dimY", "dddd"}}, 3.0},
- {{{"dimX", "e"},{"dimY","ff"}}, 5.0} },
- { "dimX", "dimY" }));
+ createTensor(TensorSpec("tensor(dimX{},dimY{})")
+ .add({{"dimX", "a"}, {"dimY", "bb"}}, 2.0)
+ .add({{"dimX", "ccc"}, {"dimY", "dddd"}}, 3.0)
+ .add({{"dimX", "e"}, {"dimY", "ff"}}, 5.0)));
TensorDocFixture f1(tensor_doc_repo1,
- createTensor({ {{{"dimX", "a"}}, 20.0 },
- {{{"dimX", "ccc"}}, 30.0} },
- { "dimX" }));
+ createTensor(TensorSpec("tensor(dimX{})")
+ .add({{"dimX", "a"}}, 20.0)
+ .add({{"dimX", "ccc"}}, 30.0)));
DeserializedTensorDoc doc;
doc.setup(tensor_doc_repo, f._blob);
EXPECT_TRUE(doc.getTensor() != nullptr);
diff --git a/document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp b/document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp
index dc91df0b5a0..9d2da9c983a 100644
--- a/document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp
+++ b/document/src/tests/tensor_fieldvalue/tensor_fieldvalue_test.cpp
@@ -9,8 +9,7 @@ LOG_SETUP("fieldvalue_test");
#include <vespa/document/fieldvalue/tensorfieldvalue.h>
#include <vespa/eval/tensor/tensor.h>
#include <vespa/eval/tensor/types.h>
-#include <vespa/eval/tensor/default_tensor.h>
-#include <vespa/eval/tensor/tensor_factory.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/test/test_utils.h>
#include <vespa/vespalib/testkit/testapp.h>
@@ -19,6 +18,7 @@ using namespace document;
using namespace vespalib::tensor;
using vespalib::eval::TensorSpec;
using vespalib::eval::ValueType;
+using vespalib::tensor::DefaultTensorEngine;
using vespalib::tensor::test::makeTensor;
namespace
@@ -27,10 +27,12 @@ namespace
TensorDataType xSparseTensorDataType(ValueType::from_spec("tensor(x{})"));
TensorDataType xySparseTensorDataType(ValueType::from_spec("tensor(x{},y{})"));
-Tensor::UP
-createTensor(const TensorCells &cells, const TensorDimensions &dimensions) {
- vespalib::tensor::DefaultTensor::builder builder;
- return vespalib::tensor::TensorFactory::create(cells, dimensions, builder);
+Tensor::UP createTensor(const TensorSpec &spec) {
+ auto value = DefaultTensorEngine::ref().from_spec(spec);
+ Tensor *tensor = dynamic_cast<Tensor*>(value.get());
+ ASSERT_TRUE(tensor != nullptr);
+ value.release();
+ return Tensor::UP(tensor);
}
std::unique_ptr<Tensor>
@@ -54,10 +56,10 @@ TEST("require that TensorFieldValue can be assigned tensors and cloned") {
TensorFieldValue noTensorValue(xySparseTensorDataType);
TensorFieldValue emptyTensorValue(xySparseTensorDataType);
TensorFieldValue twoCellsTwoDimsValue(xySparseTensorDataType);
- emptyTensorValue = createTensor({}, {"x", "y"});
- twoCellsTwoDimsValue = createTensor({ {{{"y", "3"}}, 3},
- {{{"x", "4"}, {"y", "5"}}, 7} },
- {"x", "y"});
+ emptyTensorValue = createTensor(TensorSpec("tensor(x{},y{})"));
+ twoCellsTwoDimsValue = createTensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", ""}, {"y", "3"}}, 3)
+ .add({{"x", "4"}, {"y", "5"}}, 7));
EXPECT_NOT_EQUAL(noTensorValue, emptyTensorValue);
EXPECT_NOT_EQUAL(noTensorValue, twoCellsTwoDimsValue);
EXPECT_NOT_EQUAL(emptyTensorValue, noTensorValue);
@@ -74,10 +76,9 @@ TEST("require that TensorFieldValue can be assigned tensors and cloned") {
EXPECT_NOT_EQUAL(*twoClone, *noneClone);
EXPECT_NOT_EQUAL(*twoClone, *emptyClone);
TensorFieldValue twoCellsTwoDimsValue2(xySparseTensorDataType);
- twoCellsTwoDimsValue2 =
- createTensor({ {{{"y", "3"}}, 3},
- {{{"x", "4"}, {"y", "5"}}, 7} },
- {"x", "y"});
+ twoCellsTwoDimsValue2 = createTensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", ""}, {"y", "3"}}, 3)
+ .add({{"x", "4"}, {"y", "5"}}, 7));
EXPECT_NOT_EQUAL(*noneClone, twoCellsTwoDimsValue2);
EXPECT_NOT_EQUAL(*emptyClone, twoCellsTwoDimsValue2);
EXPECT_EQUAL(*twoClone, twoCellsTwoDimsValue2);
@@ -87,8 +88,8 @@ TEST("require that TensorFieldValue::toString works")
{
TensorFieldValue tensorFieldValue(xSparseTensorDataType);
EXPECT_EQUAL("{TensorFieldValue: null}", tensorFieldValue.toString());
- tensorFieldValue = createTensor({{{{"x","a"}}, 3}}, {"x"});
- EXPECT_EQUAL("{TensorFieldValue: {\"dimensions\":[\"x\"],\"cells\":[{\"address\":{\"x\":\"a\"},\"value\":3}]}}", tensorFieldValue.toString());
+ tensorFieldValue = createTensor(TensorSpec("tensor(x{})").add({{"x", "a"}}, 3));
+ EXPECT_EQUAL("{TensorFieldValue: spec(tensor(x{})) {\n [a]: 3\n}}", tensorFieldValue.toString());
}
TEST("require that wrong tensor type for special case assign throws exception")
diff --git a/document/src/vespa/document/fieldvalue/tensorfieldvalue.cpp b/document/src/vespa/document/fieldvalue/tensorfieldvalue.cpp
index f209358c901..9b318a39f0a 100644
--- a/document/src/vespa/document/fieldvalue/tensorfieldvalue.cpp
+++ b/document/src/vespa/document/fieldvalue/tensorfieldvalue.cpp
@@ -4,16 +4,16 @@
#include <vespa/document/base/exceptions.h>
#include <vespa/document/datatype/tensor_data_type.h>
#include <vespa/vespalib/util/xmlstream.h>
+#include <vespa/eval/eval/tensor_spec.h>
#include <vespa/eval/tensor/tensor.h>
-#include <vespa/eval/tensor/serialization/slime_binary_format.h>
-#include <vespa/vespalib/data/slime/slime.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <ostream>
#include <cassert>
-using vespalib::slime::JsonFormat;
using vespalib::tensor::Tensor;
-using vespalib::tensor::SlimeBinaryFormat;
+using vespalib::eval::TensorSpec;
using vespalib::eval::ValueType;
+using Engine = vespalib::tensor::DefaultTensorEngine;
using namespace vespalib::xml;
namespace document {
@@ -148,11 +148,7 @@ TensorFieldValue::print(std::ostream& out, bool verbose,
(void) indent;
out << "{TensorFieldValue: ";
if (_tensor) {
- auto slime = SlimeBinaryFormat::serialize(*_tensor);
- vespalib::SimpleBuffer buf;
- JsonFormat::encode(*slime, buf, true);
- auto json = buf.get().make_string();
- out << json;
+ out << Engine::ref().to_spec(*_tensor).to_string();
} else {
out << "null";
}
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index d15f1c37d2d..4468ac46f61 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -44,7 +44,6 @@ vespa_define_module(
src/tests/tensor/tensor_performance
src/tests/tensor/tensor_remove_operation
src/tests/tensor/tensor_serialization
- src/tests/tensor/tensor_slime_serialization
src/tests/tensor/typed_cells
src/tests/tensor/vector_from_doubles_function
diff --git a/eval/src/tests/tensor/tensor_serialization/tensor_serialization_test.cpp b/eval/src/tests/tensor/tensor_serialization/tensor_serialization_test.cpp
index b7aa988775d..2c113e814e9 100644
--- a/eval/src/tests/tensor/tensor_serialization/tensor_serialization_test.cpp
+++ b/eval/src/tests/tensor/tensor_serialization/tensor_serialization_test.cpp
@@ -4,8 +4,7 @@
#include <vespa/eval/tensor/sparse/sparse_tensor.h>
#include <vespa/eval/tensor/sparse/sparse_tensor_builder.h>
#include <vespa/eval/tensor/types.h>
-#include <vespa/eval/tensor/default_tensor.h>
-#include <vespa/eval/tensor/tensor_factory.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/serialization/typed_binary_format.h>
#include <vespa/eval/tensor/serialization/sparse_binary_format.h>
#include <vespa/vespalib/objects/nbostream.h>
@@ -14,6 +13,7 @@
#include <vespa/eval/tensor/dense/dense_tensor_view.h>
using namespace vespalib::tensor;
+using vespalib::eval::TensorSpec;
using vespalib::nbostream;
using ExpBuffer = std::vector<uint8_t>;
@@ -33,257 +33,181 @@ std::ostream &operator<<(std::ostream &out, const std::vector<uint8_t> &rhs)
}
-namespace vespalib::tensor {
-
-static bool operator==(const Tensor &lhs, const Tensor &rhs)
-{
- return lhs.equals(rhs);
-}
-
-}
-
-template <class BuilderType>
-void
-checkDeserialize(vespalib::nbostream &stream, const Tensor &rhs)
-{
- (void) stream;
- (void) rhs;
-}
-
-template <>
-void
-checkDeserialize<DefaultTensor::builder>(nbostream &stream, const Tensor &rhs)
-{
- nbostream wrapStream(stream.peek(), stream.size());
- auto chk = TypedBinaryFormat::deserialize(wrapStream);
- EXPECT_EQUAL(0u, wrapStream.size());
- EXPECT_EQUAL(*chk, rhs);
-}
-
-template <typename BuilderType>
-struct Fixture
-{
- BuilderType _builder;
- Fixture() : _builder() {}
-
- Tensor::UP createTensor(const TensorCells &cells) {
- return TensorFactory::create(cells, _builder);
- }
- Tensor::UP createTensor(const TensorCells &cells, const TensorDimensions &dimensions) {
- return TensorFactory::create(cells, dimensions, _builder);
+//-----------------------------------------------------------------------------
+
+template <typename T>
+void verify_cells_only(const ExpBuffer &exp, const TensorSpec &spec) {
+ nbostream input(&exp[0], exp.size());
+ std::vector<T> cells;
+ TypedBinaryFormat::deserializeCellsOnlyFromDenseTensors(input, cells);
+ ASSERT_EQUAL(cells.size(), spec.cells().size());
+ size_t i = 0;
+ for (const auto &cell: spec.cells()) {
+ EXPECT_EQUAL(cells[i++], cell.second.value);
}
+ ASSERT_EQUAL(i, cells.size());
+}
- void serialize(nbostream &stream, const Tensor &tensor) {
- TypedBinaryFormat::serialize(stream, tensor);
- }
- Tensor::UP deserialize(nbostream &stream) {
- BuilderType builder;
- nbostream wrapStream(stream.peek(), stream.size());
- auto formatId = wrapStream.getInt1_4Bytes();
- ASSERT_EQUAL(formatId, 1u); // sparse format
- SparseBinaryFormat::deserialize(wrapStream, builder);
- EXPECT_TRUE(wrapStream.empty());
- auto ret = builder.build();
- checkDeserialize<BuilderType>(stream, *ret);
- stream.adjustReadPos(stream.size());
- return ret;
- }
- void assertSerialized(const ExpBuffer &exp, const TensorCells &rhs,
- const TensorDimensions &rhsDimensions) {
- Tensor::UP rhsTensor(createTensor(rhs, rhsDimensions));
- nbostream rhsStream;
- serialize(rhsStream, *rhsTensor);
- EXPECT_EQUAL(exp, rhsStream);
- auto rhs2 = deserialize(rhsStream);
- EXPECT_EQUAL(*rhs2, *rhsTensor);
+void verify_serialized(const ExpBuffer &exp, const TensorSpec &spec) {
+ auto &engine = DefaultTensorEngine::ref();
+ auto value = engine.from_spec(spec);
+ auto value_spec = engine.to_spec(*value);
+ nbostream actual;
+ engine.encode(*value, actual);
+ EXPECT_EQUAL(exp, actual);
+ auto decoded = engine.decode(actual);
+ auto decoded_spec = engine.to_spec(*decoded);
+ EXPECT_EQUAL(0u, actual.size());
+ EXPECT_EQUAL(value_spec, decoded_spec);
+ if (value->type().is_dense()) {
+ TEST_DO(verify_cells_only<float>(exp, value_spec));
+ TEST_DO(verify_cells_only<double>(exp, value_spec));
}
-};
-
-using SparseFixture = Fixture<SparseTensorBuilder>;
-
+}
-template <typename FixtureType>
-void
-testTensorSerialization(FixtureType &f)
-{
- TEST_DO(f.assertSerialized({ 0x01, 0x00, 0x00 }, {}, {}));
- TEST_DO(f.assertSerialized({ 0x01, 0x01, 0x01, 0x78, 0x00 },
- {}, { "x" }));
- TEST_DO(f.assertSerialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x00 },
- {}, { "x", "y" }));
- TEST_DO(f.assertSerialized({ 0x01, 0x01, 0x01, 0x78, 0x01, 0x01, 0x31, 0x40,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- { {{{"x","1"}}, 3} }, { "x" }));
- TEST_DO(f.assertSerialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x00,
+//-----------------------------------------------------------------------------
+
+TEST("test tensor serialization for SparseTensor") {
+ TEST_DO(verify_serialized({ 0x01, 0x01, 0x01, 0x78, 0x00 },
+ TensorSpec("tensor(x{})")));
+ TEST_DO(verify_serialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x00 },
+ TensorSpec("tensor(x{},y{})")));
+ TEST_DO(verify_serialized({ 0x01, 0x01, 0x01, 0x78, 0x01, 0x01, 0x31, 0x40,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ TensorSpec("tensor(x{})")
+ .add({{"x", "1"}}, 3)));
+ TEST_DO(verify_serialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x00,
0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00 },
- { {{}, 3} }, { "x", "y"}));
- TEST_DO(f.assertSerialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x01,
- 0x31, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00 },
- { {{{"x","1"}}, 3} }, { "x", "y" }));
- TEST_DO(f.assertSerialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x00,
- 0x01, 0x33, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00 },
- { {{{"y","3"}}, 3} }, { "x", "y" }));
- TEST_DO(f.assertSerialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x01,
+ TensorSpec("tensor(x{},y{})")
+ .add({{"x", ""}, {"y", ""}}, 3)));
+ TEST_DO(verify_serialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x01,
+ 0x31, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00 },
+ TensorSpec("tensor(x{},y{})")
+ .add({{"x", "1"}, {"y", ""}}, 3)));
+ TEST_DO(verify_serialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x00,
+ 0x01, 0x33, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00 },
+ TensorSpec("tensor(x{},y{})")
+ .add({{"x", ""}, {"y", "3"}}, 3)));
+ TEST_DO(verify_serialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79, 0x01, 0x01,
0x32, 0x01, 0x34, 0x40, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00 },
- { {{{"x","2"}, {"y", "4"}}, 3} }, { "x", "y" }));
- TEST_DO(f.assertSerialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79,
+ TensorSpec("tensor(x{},y{})")
+ .add({{"x", "2"}, {"y", "4"}}, 3)));
+ TEST_DO(verify_serialized({ 0x01, 0x02, 0x01, 0x78, 0x01, 0x79,
0x01, 0x01, 0x31, 0x00, 0x40, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- { {{{"x","1"}}, 3} }, {"x", "y"}));
+ TensorSpec("tensor(x{},y{})")
+ .add({{"x", "1"}, {"y", ""}}, 3)));
}
-TEST_F("test tensor serialization for SparseTensor", SparseFixture)
-{
- testTensorSerialization(f);
+TEST("test tensor serialization for DenseTensor") {
+ TEST_DO(verify_serialized({0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("double")));
+ TEST_DO(verify_serialized({0x02, 0x01, 0x01, 0x78, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("tensor(x[1])")
+ .add({{"x", 0}}, 0)));
+ TEST_DO(verify_serialized({0x02, 0x02, 0x01, 0x78, 0x01,
+ 0x01, 0x79, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("tensor(x[1],y[1])")
+ .add({{"x", 0}, {"y", 0}}, 0)));
+ TEST_DO(verify_serialized({0x02, 0x01, 0x01, 0x78, 0x02,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("tensor(x[2])")
+ .add({{"x", 1}}, 3)));
+ TEST_DO(verify_serialized({0x02, 0x02, 0x01, 0x78, 0x01,
+ 0x01, 0x79, 0x01,
+ 0x40, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("tensor(x[1],y[1])")
+ .add({{"x", 0}, {"y", 0}}, 3)));
+ TEST_DO(verify_serialized({0x02, 0x02, 0x01, 0x78, 0x02,
+ 0x01, 0x79, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("tensor(x[2],y[1])")
+ .add({{"x", 1}, {"y", 0}}, 3)));
+ TEST_DO(verify_serialized({0x02, 0x02, 0x01, 0x78, 0x01,
+ 0x01, 0x79, 0x04,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("tensor(x[1],y[4])")
+ .add({{"x", 0}, {"y", 3}}, 3)));
+ TEST_DO(verify_serialized({0x02, 0x02, 0x01, 0x78, 0x03,
+ 0x01, 0x79, 0x05,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00},
+ TensorSpec("tensor(x[3],y[5])")
+ .add({{"x", 2}, {"y", 4}}, 3)));
}
-
-struct DenseFixture
-{
- Tensor::UP createTensor(const DenseTensorCells &cells) {
- return TensorFactory::createDense(cells);
- }
-
- void serialize(nbostream &stream, const Tensor &tensor) {
- TypedBinaryFormat::serialize(stream, tensor);
- }
-
- Tensor::UP deserialize(nbostream &stream) {
- nbostream wrapStream(stream.peek(), stream.size());
- auto ret = TypedBinaryFormat::deserialize(wrapStream);
- EXPECT_TRUE(wrapStream.size() == 0);
- stream.adjustReadPos(stream.size());
- return ret;
- }
- void assertSerialized(const ExpBuffer &exp, const DenseTensorCells &rhs) {
- assertSerialized(exp, SerializeFormat::DOUBLE, rhs);
- }
- template <typename T>
- void assertCellsOnly(const ExpBuffer &exp, const DenseTensorView & rhs) {
- nbostream a(&exp[0], exp.size());
- std::vector<T> v;
- TypedBinaryFormat::deserializeCellsOnlyFromDenseTensors(a, v);
- EXPECT_EQUAL(v.size(), rhs.cellsRef().size());
- for (size_t i(0); i < v.size(); i++) {
- EXPECT_EQUAL(v[i], rhs.cellsRef()[i]);
- }
- }
- void assertSerialized(const ExpBuffer &exp, SerializeFormat cellType, const DenseTensorCells &rhs) {
- Tensor::UP rhsTensor(createTensor(rhs));
- nbostream rhsStream;
- TypedBinaryFormat::serialize(rhsStream, *rhsTensor, cellType);
- EXPECT_EQUAL(exp, rhsStream);
- auto rhs2 = deserialize(rhsStream);
- EXPECT_EQUAL(*rhs2, *rhsTensor);
-
- assertCellsOnly<float>(exp, dynamic_cast<const DenseTensorView &>(*rhs2));
- assertCellsOnly<double>(exp, dynamic_cast<const DenseTensorView &>(*rhs2));
- }
-};
-
-
-TEST_F("test tensor serialization for DenseTensor", DenseFixture) {
- TEST_DO(f.assertSerialized({0x02, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {}));
- TEST_DO(f.assertSerialized({0x02, 0x01, 0x01, 0x78, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {{{{"x", 0}}, 0}}));
- TEST_DO(f.assertSerialized({0x02, 0x02, 0x01, 0x78, 0x01,
- 0x01, 0x79, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {{{{"x", 0}, {"y", 0}}, 0}}));
- TEST_DO(f.assertSerialized({0x02, 0x01, 0x01, 0x78, 0x02,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x40, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {{{{"x", 1}}, 3}}));
- TEST_DO(f.assertSerialized({0x02, 0x02, 0x01, 0x78, 0x01,
- 0x01, 0x79, 0x01,
- 0x40, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {{{{"x", 0}, {"y", 0}}, 3}}));
- TEST_DO(f.assertSerialized({0x02, 0x02, 0x01, 0x78, 0x02,
- 0x01, 0x79, 0x01,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x40, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {{{{"x", 1}, {"y", 0}}, 3}}));
- TEST_DO(f.assertSerialized({0x02, 0x02, 0x01, 0x78, 0x01,
- 0x01, 0x79, 0x04,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x40, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {{{{"x", 0}, {"y", 3}}, 3}}));
- TEST_DO(f.assertSerialized({0x02, 0x02, 0x01, 0x78, 0x03,
- 0x01, 0x79, 0x05,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x40, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00},
- {{{{"x", 2}, {"y", 4}}, 3}}));
-}
-
-TEST_F("test 'float' cells", DenseFixture) {
- TEST_DO(f.assertSerialized({0x06, 0x01, 0x02, 0x01, 0x78, 0x03,
- 0x01, 0x79, 0x05,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x40, 0x40, 0x00, 0x00 },
- SerializeFormat::FLOAT, { {{{"x",2}, {"y",4}}, 3} }));
+TEST("test 'float' cells") {
+ TEST_DO(verify_serialized({0x06, 0x01, 0x02, 0x01, 0x78, 0x03,
+ 0x01, 0x79, 0x05,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x40, 0x00, 0x00 },
+ TensorSpec("tensor<float>(x[3],y[5])")
+ .add({{"x", 2}, {"y", 4}}, 3)));
}
-
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/tests/tensor/tensor_slime_serialization/.gitignore b/eval/src/tests/tensor/tensor_slime_serialization/.gitignore
deleted file mode 100644
index 9cb3b664d58..00000000000
--- a/eval/src/tests/tensor/tensor_slime_serialization/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-vespalib_tensor_slime_serialization_test_app
diff --git a/eval/src/tests/tensor/tensor_slime_serialization/CMakeLists.txt b/eval/src/tests/tensor/tensor_slime_serialization/CMakeLists.txt
deleted file mode 100644
index 9a9b8b7a436..00000000000
--- a/eval/src/tests/tensor/tensor_slime_serialization/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_executable(eval_tensor_slime_serialization_test_app TEST
- SOURCES
- tensor_slime_serialization_test.cpp
- DEPENDS
- vespaeval
-)
-vespa_add_test(NAME eval_tensor_slime_serialization_test_app COMMAND eval_tensor_slime_serialization_test_app)
diff --git a/eval/src/tests/tensor/tensor_slime_serialization/tensor_slime_serialization_test.cpp b/eval/src/tests/tensor/tensor_slime_serialization/tensor_slime_serialization_test.cpp
deleted file mode 100644
index e6e6c7de686..00000000000
--- a/eval/src/tests/tensor/tensor_slime_serialization/tensor_slime_serialization_test.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include <vespa/vespalib/testkit/test_kit.h>
-#include <vespa/eval/tensor/sparse/sparse_tensor.h>
-#include <vespa/eval/tensor/sparse/sparse_tensor_builder.h>
-#include <vespa/eval/tensor/types.h>
-#include <vespa/eval/tensor/default_tensor.h>
-#include <vespa/eval/tensor/tensor_factory.h>
-#include <vespa/eval/tensor/serialization/typed_binary_format.h>
-#include <vespa/eval/tensor/serialization/slime_binary_format.h>
-#include <vespa/vespalib/data/slime/slime.h>
-#include <iostream>
-
-using namespace vespalib::tensor;
-
-template <typename BuilderType>
-struct Fixture
-{
- BuilderType _builder;
- Fixture() : _builder() {}
-
- Tensor::UP createTensor(const TensorCells &cells) {
- return vespalib::tensor::TensorFactory::create(cells, _builder);
- }
- Tensor::UP createTensor(const TensorCells &cells, const TensorDimensions &dimensions) {
- return TensorFactory::create(cells, dimensions, _builder);
- }
-
- static inline uint32_t getTensorTypeId();
-
- void assertSerialized(const vespalib::string &exp, const TensorCells &rhs,
- const TensorDimensions &rhsDimensions) {
- Tensor::UP rhsTensor(createTensor(rhs, rhsDimensions));
- auto slime = SlimeBinaryFormat::serialize(*rhsTensor);
- vespalib::Memory memory_exp(exp);
- vespalib::Slime expSlime;
- size_t used = vespalib::slime::JsonFormat::decode(memory_exp, expSlime);
- EXPECT_TRUE(used > 0);
- EXPECT_EQUAL(expSlime, *slime);
- }
-};
-
-template <>
-uint32_t
-Fixture<SparseTensorBuilder>::getTensorTypeId() { return 2u; }
-
-
-using SparseFixture = Fixture<SparseTensorBuilder>;
-
-
-namespace {
-vespalib::string twoCellsJson[3] =
-{
- "{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { y:'3'}, value: 4.0 },"
- "{ address: { x:'1'}, value: 3.0 }"
- "] }",
- "{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x:'1'}, value: 3.0 },"
- "{ address: { y:'3'}, value: 4.0 }"
- "] }",
- "{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x:'1'}, value: 3.0 },"
- "{ address: { y:'3'}, value: 4.0 }"
- "] }",
-};
-}
-
-
-template <typename FixtureType>
-void
-testTensorSlimeSerialization(FixtureType &f)
-{
- TEST_DO(f.assertSerialized("{ dimensions: [], cells: [] }", {}, {}));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x' ], cells: [] }",
- {}, { "x" }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ], cells: [] }",
- {}, { "x", "y" }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x' ],"
- "cells: ["
- "{ address: { x: '1' }, value: 3.0 }"
- "] }",
- { {{{"x","1"}}, 3} }, { "x" }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { }, value: 3.0 }"
- "] }",
- { {{}, 3} }, { "x", "y"}));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x: '1' }, value: 3.0 }"
- "] }",
- { {{{"x","1"}}, 3} }, { "x", "y" }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { y: '3' }, value: 3.0 }"
- "] }",
- { {{{"y","3"}}, 3} }, { "x", "y" }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x:'2', y:'4'}, value: 3.0 }"
- "] }",
- { {{{"x","2"}, {"y", "4"}}, 3} }, { "x", "y" }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x:'1'}, value: 3.0 }"
- "] }",
- { {{{"x","1"}}, 3} }, {"x", "y"}));
- TEST_DO(f.assertSerialized(twoCellsJson[FixtureType::getTensorTypeId()],
- { {{{"x","1"}}, 3}, {{{"y","3"}}, 4} },
- {"x", "y"}));
-}
-
-TEST_F("test tensor slime serialization for SparseTensor", SparseFixture)
-{
- testTensorSlimeSerialization(f);
-}
-
-
-struct DenseFixture
-{
- DenseFixture() {}
-
- Tensor::UP createTensor(const DenseTensorCells &cells) {
- return vespalib::tensor::TensorFactory::createDense(cells);
- }
-
- void assertSerialized(const vespalib::string &exp,
- const DenseTensorCells &rhs) {
- Tensor::UP rhsTensor(createTensor(rhs));
- auto slime = SlimeBinaryFormat::serialize(*rhsTensor);
- vespalib::Memory memory_exp(exp);
- vespalib::Slime expSlime;
- size_t used = vespalib::slime::JsonFormat::decode(memory_exp, expSlime);
- EXPECT_TRUE(used > 0);
- EXPECT_EQUAL(expSlime, *slime);
- }
-};
-
-
-TEST_F("test tensor slime serialization for DenseTensor", DenseFixture)
-{
- TEST_DO(f.assertSerialized("{ dimensions: [], cells: ["
- "{ address: { }, value: 0.0 }"
- "] }", {}));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x' ], cells: ["
- "{ address: { x: '0' }, value: 0.0 }"
- "] }",
- { {{{"x",0}}, 0} }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ], cells: ["
- "{ address: { x: '0', y: '0' }, value: 0.0 }"
- "] }",
- { {{{"x",0},{"y",0}}, 0} }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x' ],"
- "cells: ["
- "{ address: { x: '0' }, value: 0.0 },"
- "{ address: { x: '1' }, value: 3.0 }"
- "] }",
- { {{{"x",1}}, 3} }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x: '0', y: '0' }, value: 3.0 }"
- "] }",
- { {{{"x",0},{"y",0}}, 3} }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x: '0', y: '0' }, value: 0.0 },"
- "{ address: { x: '1', y: '0' }, value: 3.0 }"
- "] }",
- { {{{"x",1},{"y", 0}}, 3} }));
- TEST_DO(f.assertSerialized("{ dimensions: [ 'x', 'y' ],"
- " cells: ["
- "{ address: { x: '0', y: '0' }, value: 0.0 },"
- "{ address: { x: '0', y: '1' }, value: 0.0 },"
- "{ address: { x: '0', y: '2' }, value: 0.0 },"
- "{ address: { x: '0', y: '3' }, value: 3.0 }"
- "] }",
- { {{{"x",0},{"y",3}}, 3} }));
-}
-
-
-TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/vespa/eval/tensor/CMakeLists.txt b/eval/src/vespa/eval/tensor/CMakeLists.txt
index ca4d275ea44..4e9940d3c0a 100644
--- a/eval/src/vespa/eval/tensor/CMakeLists.txt
+++ b/eval/src/vespa/eval/tensor/CMakeLists.txt
@@ -5,7 +5,6 @@ vespa_add_library(eval_tensor OBJECT
tensor.cpp
tensor_address.cpp
tensor_apply.cpp
- tensor_factory.cpp
tensor_mapper.cpp
wrapped_simple_tensor.cpp
)
diff --git a/eval/src/vespa/eval/tensor/serialization/CMakeLists.txt b/eval/src/vespa/eval/tensor/serialization/CMakeLists.txt
index cd3cd455e55..fc9ac64ea68 100644
--- a/eval/src/vespa/eval/tensor/serialization/CMakeLists.txt
+++ b/eval/src/vespa/eval/tensor/serialization/CMakeLists.txt
@@ -3,6 +3,5 @@ vespa_add_library(eval_tensor_serialization OBJECT
SOURCES
sparse_binary_format.cpp
dense_binary_format.cpp
- slime_binary_format.cpp
typed_binary_format.cpp
)
diff --git a/eval/src/vespa/eval/tensor/serialization/slime_binary_format.cpp b/eval/src/vespa/eval/tensor/serialization/slime_binary_format.cpp
deleted file mode 100644
index ece3c2e4a07..00000000000
--- a/eval/src/vespa/eval/tensor/serialization/slime_binary_format.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "slime_binary_format.h"
-#include <vespa/eval/tensor/types.h>
-#include <vespa/eval/tensor/tensor.h>
-#include <vespa/eval/tensor/tensor_builder.h>
-#include <vespa/eval/tensor/tensor_visitor.h>
-#include <vespa/vespalib/data/slime/inserter.h>
-#include <vespa/vespalib/data/slime/cursor.h>
-#include <vespa/vespalib/data/slime/slime.h>
-#include <vespa/vespalib/data/memory.h>
-
-namespace vespalib::tensor {
-
-
-using slime::Inserter;
-using slime::SlimeInserter;
-using slime::Cursor;
-using slime::ObjectInserter;
-
-namespace {
-
-Memory memory_address("address");
-Memory memory_cells("cells");
-Memory memory_dimensions("dimensions");
-Memory memory_value("value");
-
-void writeTensorAddress(Cursor &cursor, const TensorAddress &value) {
- ObjectInserter addressInserter(cursor, memory_address);
- Cursor &addressCursor = addressInserter.insertObject();
- for (const auto &elem : value.elements()) {
- Memory dimension(elem.dimension());
- Memory label(elem.label());
- addressCursor.setString(dimension, label);
- }
-}
-
-}
-
-class SlimeBinaryFormatSerializer : public TensorVisitor
-{
- Cursor &_tensor; // cursor for whole tensor
- Cursor &_dimensions; // cursor for dimensions array
- Cursor &_cells; // cursor for cells array
-public:
- SlimeBinaryFormatSerializer(Inserter &inserter);
- virtual ~SlimeBinaryFormatSerializer() override;
- virtual void visit(const TensorAddress &address, double value) override;
- void serialize(const Tensor &tensor);
-};
-
-SlimeBinaryFormatSerializer::SlimeBinaryFormatSerializer(Inserter &inserter)
- : _tensor(inserter.insertObject()),
- _dimensions(_tensor.setArray(memory_dimensions)),
- _cells(_tensor.setArray(memory_cells))
-{
-}
-
-
-SlimeBinaryFormatSerializer::~SlimeBinaryFormatSerializer() = default;
-
-void
-SlimeBinaryFormatSerializer::visit(const TensorAddress &address, double value)
-{
- Cursor &cellCursor = _cells.addObject();
- writeTensorAddress(cellCursor, address);
- cellCursor.setDouble(memory_value, value);
-}
-
-
-void
-SlimeBinaryFormatSerializer::serialize(const Tensor &tensor)
-{
- eval::ValueType type(tensor.type());
- for (const auto & dimension : type.dimensions()) {
- _dimensions.addString(Memory(dimension.name));
- }
- tensor.accept(*this);
-}
-
-
-void
-SlimeBinaryFormat::serialize(Inserter &inserter, const Tensor &tensor)
-{
- SlimeBinaryFormatSerializer serializer(inserter);
- serializer.serialize(tensor);
-}
-
-
-std::unique_ptr<Slime>
-SlimeBinaryFormat::serialize(const Tensor &tensor)
-{
- auto slime = std::make_unique<Slime>();
- SlimeInserter inserter(*slime);
- serialize(inserter, tensor);
- return slime;
-}
-
-
-}
diff --git a/eval/src/vespa/eval/tensor/serialization/slime_binary_format.h b/eval/src/vespa/eval/tensor/serialization/slime_binary_format.h
deleted file mode 100644
index c9e9ff2c3e9..00000000000
--- a/eval/src/vespa/eval/tensor/serialization/slime_binary_format.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include <memory>
-
-namespace vespalib { class Slime; }
-
-namespace vespalib::slime { struct Inserter; }
-
-namespace vespalib::tensor {
-
-class Tensor;
-class TensorBuilder;
-
-/**
- * Class for serializing a tensor into a slime object.
- */
-class SlimeBinaryFormat
-{
-public:
- static void serialize(slime::Inserter &inserter, const Tensor &tensor);
- static std::unique_ptr<Slime> serialize(const Tensor &tensor);
-};
-
-}
diff --git a/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp b/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
index 4ca037e82a4..7be4db46372 100644
--- a/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
+++ b/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
@@ -25,9 +25,9 @@ namespace {
constexpr uint32_t SPARSE_BINARY_FORMAT_TYPE = 1u;
constexpr uint32_t DENSE_BINARY_FORMAT_TYPE = 2u;
constexpr uint32_t MIXED_BINARY_FORMAT_TYPE = 3u;
-constexpr uint32_t SPARSE_BINARY_FORMAT_WITH_CELLTYPE = 5u; //Future
+constexpr uint32_t SPARSE_BINARY_FORMAT_WITH_CELLTYPE = 5u;
constexpr uint32_t DENSE_BINARY_FORMAT_WITH_CELLTYPE = 6u;
-constexpr uint32_t MIXED_BINARY_FORMAT_WITH_CELLTYPE = 7u; //Future
+constexpr uint32_t MIXED_BINARY_FORMAT_WITH_CELLTYPE = 7u;
constexpr uint32_t DOUBLE_VALUE_TYPE = 0;
constexpr uint32_t FLOAT_VALUE_TYPE = 1;
@@ -91,10 +91,11 @@ TypedBinaryFormat::deserialize(nbostream &stream)
if (formatId == DENSE_BINARY_FORMAT_TYPE) {
return DenseBinaryFormat(SerializeFormat::DOUBLE).deserialize(stream);
}
- if (formatId == DENSE_BINARY_FORMAT_WITH_CELLTYPE) {
- return DenseBinaryFormat(encoding2Format(stream.getInt1_4Bytes())).deserialize(stream);
- }
- if (formatId == MIXED_BINARY_FORMAT_TYPE) {
+ if ((formatId == SPARSE_BINARY_FORMAT_WITH_CELLTYPE) ||
+ (formatId == DENSE_BINARY_FORMAT_WITH_CELLTYPE) ||
+ (formatId == MIXED_BINARY_FORMAT_TYPE) ||
+ (formatId == MIXED_BINARY_FORMAT_WITH_CELLTYPE))
+ {
stream.adjustReadPos(read_pos - stream.rp());
return std::make_unique<WrappedSimpleTensor>(eval::SimpleTensor::decode(stream));
}
diff --git a/eval/src/vespa/eval/tensor/tensor_factory.cpp b/eval/src/vespa/eval/tensor/tensor_factory.cpp
deleted file mode 100644
index 0b7fa3b9c2e..00000000000
--- a/eval/src/vespa/eval/tensor/tensor_factory.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "tensor.h"
-#include "tensor_factory.h"
-#include "tensor_builder.h"
-#include <vespa/eval/tensor/dense/dense_tensor_builder.h>
-
-namespace vespalib::tensor {
-
-std::unique_ptr<Tensor>
-TensorFactory::create(const TensorCells &cells, TensorBuilder &builder) {
- for (const auto &cell : cells) {
- for (const auto &addressElem : cell.first) {
- const auto &dimension = addressElem.first;
- builder.define_dimension(dimension);
- }
- }
- for (const auto &cell : cells) {
- for (const auto &addressElem : cell.first) {
- const auto &dimension = addressElem.first;
- const auto &label = addressElem.second;
- builder.add_label(builder.define_dimension(dimension), label);
- }
- builder.add_cell(cell.second);
- }
- return builder.build();
-}
-
-
-std::unique_ptr<Tensor>
-TensorFactory::create(const TensorCells &cells, const TensorDimensions &dimensions, TensorBuilder &builder) {
- for (const auto &dimension : dimensions) {
- builder.define_dimension(dimension);
- }
- return create(cells, builder);
-}
-
-
-std::unique_ptr<Tensor>
-TensorFactory::createDense(const DenseTensorCells &cells)
-{
- std::map<std::string, size_t> dimensionSizes;
- DenseTensorBuilder builder;
- for (const auto &cell : cells) {
- for (const auto &addressElem : cell.first) {
- dimensionSizes[addressElem.first] = std::max(dimensionSizes[addressElem.first], (addressElem.second + 1));
- }
- }
- std::map<std::string, typename DenseTensorBuilder::Dimension> dimensionEnums;
- for (const auto &dimensionElem : dimensionSizes) {
- dimensionEnums[dimensionElem.first] = builder.defineDimension(dimensionElem.first, dimensionElem.second);
- }
- for (const auto &cell : cells) {
- for (const auto &addressElem : cell.first) {
- const auto &dimension = addressElem.first;
- size_t label = addressElem.second;
- builder.addLabel(dimensionEnums[dimension], label);
- }
- builder.addCell(cell.second);
- }
- return builder.build();
-}
-
-
-}
diff --git a/eval/src/vespa/eval/tensor/tensor_factory.h b/eval/src/vespa/eval/tensor/tensor_factory.h
deleted file mode 100644
index 5364c28c8ff..00000000000
--- a/eval/src/vespa/eval/tensor/tensor_factory.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include "types.h"
-
-namespace vespalib::tensor {
-
-
-class Tensor;
-class TensorBuilder;
-
-/**
- * A factory for creating tensors based on stl structures (TensorCells and
- * TensorDimensions) in unit tests.
- */
-class TensorFactory {
-public:
- static std::unique_ptr<Tensor>
- create(const TensorCells &cells, TensorBuilder &builder);
- static std::unique_ptr<Tensor>
- create(const TensorCells &cells, const TensorDimensions &dimensions, TensorBuilder &builder);
- static std::unique_ptr<Tensor>
- createDense(const DenseTensorCells &cells);
-};
-
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index c790fc5659e..9cf508444f5 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -158,6 +158,11 @@ public class Flags {
"Takes effect only at bootstrap of config server/controller",
HOSTNAME);
+ public static final UnboundStringFlag CONFIGSERVER_RPC_AUTHORIZER = defineStringFlag(
+ "configserver-rpc-authorizer", "log-only",
+ "Configserver RPC authorizer. Allowed values: ['disable', 'log-only', 'enforce']",
+ "Takes effect on restart of configserver");
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, String description,
diff --git a/metrics-proxy/pom.xml b/metrics-proxy/pom.xml
index 66ca25dadf0..3cfe1d2f568 100644
--- a/metrics-proxy/pom.xml
+++ b/metrics-proxy/pom.xml
@@ -60,6 +60,12 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>container-di</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
@@ -104,6 +110,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>jdisc_http_service</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
@@ -120,6 +132,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
@@ -158,6 +175,16 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!-- Illegal reflective access by guice -->
+ <argLine>
+ --add-opens=java.base/java.lang=ALL-UNNAMED
+ </argLine>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java
new file mode 100644
index 00000000000..488f26b7af7
--- /dev/null
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package ai.vespa.metricsproxy.http;
+
+import ai.vespa.metricsproxy.core.MetricsManager;
+import ai.vespa.metricsproxy.metric.model.MetricsPacket;
+import ai.vespa.metricsproxy.metric.model.json.GenericJsonModel;
+import ai.vespa.metricsproxy.metric.model.json.GenericJsonUtil;
+import ai.vespa.metricsproxy.service.VespaServices;
+import com.google.inject.Inject;
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
+import com.yahoo.yolean.Exceptions;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.time.Instant;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Handler exposing the generic metrics format via http.
+ *
+ * @author gjoranv
+ */
+public class GenericMetricsHandler extends ThreadedHttpRequestHandler {
+
+ private final MetricsManager metricsManager;
+ private final VespaServices vespaServices;
+
+ @Inject
+ public GenericMetricsHandler(Executor executor, MetricsManager metricsManager, VespaServices vespaServices) {
+ super(executor);
+ this.metricsManager = metricsManager;
+ this.vespaServices = vespaServices;
+ }
+
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ try {
+ List<MetricsPacket> metrics = metricsManager.getMetrics(vespaServices.getVespaServices(), Instant.now());
+ return new Response(200, GenericJsonUtil.toGenericJsonModel(metrics).serialize());
+ } catch (GenericJsonModel.JsonMetricsRenderingException e) {
+ return new ErrorResponse(500, Exceptions.toMessageString(e));
+ }
+ }
+
+ private static class Response extends HttpResponse {
+ private final byte[] data;
+
+ Response(int code, String data) {
+ super(code);
+ this.data = data.getBytes(Charset.forName(DEFAULT_CHARACTER_ENCODING));
+ }
+
+ @Override
+ public String getContentType() {
+ return "application/json";
+ }
+
+ @Override
+ public void render(OutputStream outputStream) throws IOException {
+ outputStream.write(data);
+ }
+ }
+
+ private static class ErrorResponse extends Response {
+ ErrorResponse(int code, String data) {
+ super(code, "{\"error\":\"" + data + "\"}");
+ }
+ }
+
+}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java
index dd8858ff99e..14f085ff388 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java
@@ -38,8 +38,13 @@ public class GenericJsonModel {
return mapper.writeValueAsString(this);
} catch (IOException e) {
log.log(Level.WARNING, "Got exception when rendering metrics:", e);
- throw new RuntimeException("Could not render metrics. Check the log for details.");
+ throw new JsonMetricsRenderingException("Could not render metrics. Check the log for details.");
}
}
+ public static class JsonMetricsRenderingException extends RuntimeException {
+ JsonMetricsRenderingException(String message) {
+ super(message);
+ }
+ }
}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JacksonUtil.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JacksonUtil.java
index 5c04f933c4a..d9df3e0c0e9 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JacksonUtil.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JacksonUtil.java
@@ -20,12 +20,12 @@ import java.util.Locale;
* @author smorgrav
* @author gjoranv
*/
-class JacksonUtil {
+public class JacksonUtil {
/**
* Returns an object mapper with a custom floating point serializer to avoid scientific notation
*/
- static ObjectMapper createObjectMapper() {
+ public static ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("DoubleSerializer",
new Version(1, 0, 0, "", null, null));
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java
index d3f674176b3..c59daf65cc2 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/VespaService.java
@@ -33,6 +33,7 @@ public class VespaService implements Comparable<VespaService> {
private volatile int pid = -1;
private volatile String state = "UNKNOWN";
+ private volatile boolean isAlive;
// Used to keep the last polled system metrics for service
private Metrics systemMetrics;
@@ -42,7 +43,6 @@ public class VespaService implements Comparable<VespaService> {
private final RemoteHealthMetricFetcher remoteHealthMetricFetcher;
private final RemoteMetricsFetcher remoteMetricsFetcher;
- private boolean isAlive;
// Used to keep track of log level when health or metrics requests fail
private AtomicInteger metricsFetchCount = new AtomicInteger(0);
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java
index e18bd38f97a..6f86be3aa30 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/TestUtil.java
@@ -4,12 +4,22 @@
package ai.vespa.metricsproxy;
+import ai.vespa.metricsproxy.core.MetricsConsumers;
+import ai.vespa.metricsproxy.core.MetricsManager;
+import ai.vespa.metricsproxy.core.VespaMetrics;
+import ai.vespa.metricsproxy.metric.ExternalMetrics;
+import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
+import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
+import ai.vespa.metricsproxy.service.VespaService;
+import ai.vespa.metricsproxy.service.VespaServices;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.List;
import java.util.stream.Collectors;
/**
@@ -17,6 +27,15 @@ import java.util.stream.Collectors;
*/
public class TestUtil {
+ public static MetricsManager createMetricsManager(VespaServices vespaServices,
+ MetricsConsumers consumers,
+ ApplicationDimensions applicationDimensions,
+ NodeDimensions nodeDimensions) {
+ VespaMetrics metrics = new VespaMetrics(consumers, vespaServices);
+ return new MetricsManager(vespaServices, metrics, new ExternalMetrics(consumers),
+ applicationDimensions, nodeDimensions);
+ }
+
public static String getFileContents(String filename) {
InputStream in = TestUtil.class.getClassLoader().getResourceAsStream(filename);
if (in == null) {
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java
index 81634323269..1635ccab197 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/core/MetricsManagerTest.java
@@ -4,6 +4,7 @@
package ai.vespa.metricsproxy.core;
+import ai.vespa.metricsproxy.TestUtil;
import ai.vespa.metricsproxy.core.ConsumersConfig.Consumer;
import ai.vespa.metricsproxy.metric.ExternalMetrics;
import ai.vespa.metricsproxy.metric.Metric;
@@ -56,7 +57,8 @@ public class MetricsManagerTest {
@Before
public void setupMetricsManager() {
- metricsManager = getMetricsManager();
+ metricsManager = TestUtil.createMetricsManager(new VespaServices(testServices), getMetricsConsumers(),
+ getApplicationDimensions(), getNodeDimensions());
}
@Test
@@ -201,15 +203,6 @@ public class MetricsManagerTest {
return MetricsManager.adjustTimestamp(builder, startTime).getTimestamp();
}
- private MetricsManager getMetricsManager() {
- VespaServices vespaServices = new VespaServices(testServices);
- MetricsConsumers consumers = getMetricsConsumers();
- VespaMetrics metrics = new VespaMetrics(consumers, vespaServices);
-
- return new MetricsManager(vespaServices, metrics, new ExternalMetrics(consumers),
- getApplicationDimensions(),getNodeDimensions());
- }
-
private static MetricsConsumers getMetricsConsumers() {
Consumer.Metric.Dimension.Builder metricDimension = new Consumer.Metric.Dimension.Builder()
.key("dim0").value("metric-dim");
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java
new file mode 100644
index 00000000000..744c96d7744
--- /dev/null
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package ai.vespa.metricsproxy.http;
+
+import ai.vespa.metricsproxy.TestUtil;
+import ai.vespa.metricsproxy.core.ConsumersConfig;
+import ai.vespa.metricsproxy.core.MetricsConsumers;
+import ai.vespa.metricsproxy.core.MetricsManager;
+import ai.vespa.metricsproxy.metric.Metric;
+import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
+import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig;
+import ai.vespa.metricsproxy.metric.dimensions.NodeDimensions;
+import ai.vespa.metricsproxy.metric.dimensions.NodeDimensionsConfig;
+import ai.vespa.metricsproxy.metric.model.MetricsPacket;
+import ai.vespa.metricsproxy.metric.model.json.GenericJsonModel;
+import ai.vespa.metricsproxy.metric.model.json.GenericMetrics;
+import ai.vespa.metricsproxy.metric.model.json.GenericService;
+import ai.vespa.metricsproxy.service.DummyService;
+import ai.vespa.metricsproxy.service.VespaService;
+import ai.vespa.metricsproxy.service.VespaServices;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableList;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.concurrent.Executors;
+
+import static ai.vespa.metricsproxy.core.VespaMetrics.INSTANCE_DIMENSION_ID;
+import static ai.vespa.metricsproxy.core.VespaMetrics.VESPA_CONSUMER_ID;
+import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
+import static ai.vespa.metricsproxy.metric.model.json.JacksonUtil.createObjectMapper;
+import static ai.vespa.metricsproxy.service.DummyService.METRIC_1;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author gjoranv
+ */
+@SuppressWarnings("UnstableApiUsage")
+public class GenericMetricsHandlerTest {
+
+ private static final List<VespaService> testServices = ImmutableList.of(
+ new DummyService(0, "dummy/id/0"),
+ new DummyService(1, "dummy/id/0"));
+
+ private static final String CPU_METRIC = "cpu";
+
+ private static final String URI = "http://localhost/metrics/v1/values";
+
+ private static final VespaServices vespaServices = new VespaServices(testServices);
+
+ private static RequestHandlerTestDriver testDriver;
+
+ @BeforeClass
+ public static void setupMetricsManager() {
+ MetricsManager metricsManager = TestUtil.createMetricsManager(vespaServices, getMetricsConsumers(), getApplicationDimensions(), getNodeDimensions());
+ metricsManager.setExtraMetrics(ImmutableList.of(
+ new MetricsPacket.Builder(toServiceId("foo"))
+ .timestamp(Instant.now().getEpochSecond())
+ .putMetrics(ImmutableList.of(new Metric(CPU_METRIC, 12.345)))));
+ GenericMetricsHandler handler = new GenericMetricsHandler(Executors.newSingleThreadExecutor(), metricsManager, vespaServices);
+ testDriver = new RequestHandlerTestDriver(handler);
+ }
+
+ @Ignore
+ @Test
+ public void visually_inspect_response() throws Exception{
+ String response = testDriver.sendRequest(URI).readAll();
+ ObjectMapper mapper = createObjectMapper();
+ var jsonModel = mapper.readValue(response, GenericJsonModel.class);
+ System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonModel));
+ }
+
+ @Test
+ public void response_contains_node_metrics() throws Exception {
+ String response = testDriver.sendRequest(URI).readAll();
+ var jsonModel = createObjectMapper().readValue(response, GenericJsonModel.class);
+
+ assertNotNull(jsonModel.node);
+ assertEquals(1, jsonModel.node.metrics.size());
+ assertEquals(12.345, jsonModel.node.metrics.get(0).values.get(CPU_METRIC), 0.0001d);
+ }
+
+ @Test
+ public void response_contains_service_metrics() throws Exception {
+ String response = testDriver.sendRequest(URI).readAll();
+ var jsonModel = createObjectMapper().readValue(response, GenericJsonModel.class);
+
+ assertEquals(1, jsonModel.services.size());
+ GenericService dummyService = jsonModel.services.get(0);
+ assertEquals(2, dummyService.metrics.size());
+
+ GenericMetrics dummy0Metrics = getMetricsForInstance("dummy0", dummyService);
+ assertEquals(1L, dummy0Metrics.values.get(METRIC_1).longValue());
+ assertEquals("metric-dim", dummy0Metrics.dimensions.get("dim0"));
+
+ GenericMetrics dummy1Metrics = getMetricsForInstance("dummy1", dummyService);
+ assertEquals(6L, dummy1Metrics.values.get(METRIC_1).longValue());
+ assertEquals("metric-dim", dummy1Metrics.dimensions.get("dim0"));
+ }
+
+ @Test
+ public void all_timestamps_are_equal_and_non_zero() throws Exception {
+ String response = testDriver.sendRequest(URI).readAll();
+ var jsonModel = createObjectMapper().readValue(response, GenericJsonModel.class);
+
+ Long nodeTimestamp = jsonModel.node.timestamp;
+ assertNotEquals(0L, (long) nodeTimestamp);
+ for (var service : jsonModel.services)
+ assertEquals(nodeTimestamp, service.timestamp);
+ }
+
+ private static GenericMetrics getMetricsForInstance(String instance, GenericService service) {
+ for (var metrics : service.metrics) {
+ if (metrics.dimensions.get(INSTANCE_DIMENSION_ID.id).equals(instance))
+ return metrics;
+ }
+ throw new RuntimeException("Could not find metrics for service instance " + instance);
+ }
+
+ private static MetricsConsumers getMetricsConsumers() {
+ ConsumersConfig.Consumer.Metric.Dimension.Builder metricDimension = new ConsumersConfig.Consumer.Metric.Dimension.Builder()
+ .key("dim0").value("metric-dim");
+
+ return new MetricsConsumers(new ConsumersConfig.Builder()
+ .consumer(new ConsumersConfig.Consumer.Builder()
+ .name(VESPA_CONSUMER_ID.id)
+ .metric(new ConsumersConfig.Consumer.Metric.Builder()
+ .name(CPU_METRIC)
+ .outputname(CPU_METRIC))
+ .metric(new ConsumersConfig.Consumer.Metric.Builder()
+ .name(METRIC_1)
+ .outputname(METRIC_1)
+ .dimension(metricDimension)))
+ .build());
+ }
+
+ private static ApplicationDimensions getApplicationDimensions() {
+ return new ApplicationDimensions(new ApplicationDimensionsConfig.Builder().build());
+ }
+
+ private static NodeDimensions getNodeDimensions() {
+ return new NodeDimensions(new NodeDimensionsConfig.Builder().build());
+ }
+
+}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModelTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModelTest.java
index 44fe383839f..dc3eb12ff2c 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModelTest.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModelTest.java
@@ -64,7 +64,7 @@ public class GenericJsonModelTest {
assertNotNull(jsonModel.node);
assertEquals(1, jsonModel.node.metrics.size());
GenericMetrics nodeMetrics = jsonModel.node.metrics.get(0);
- assertEquals(1.234, jsonModel.node.metrics.get(0).values.get("node-metric"), 0.001d);
+ assertEquals(1.234, nodeMetrics.values.get("node-metric"), 0.001d);
assertEquals("node-dim-value", nodeMetrics.dimensions.get("node-dim"));
assertEquals(1, jsonModel.services.size());
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
index 6dfcf3e9aec..2f5d6ae60d8 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
@@ -294,6 +294,7 @@ public class DockerOperationsImpl implements DockerOperations {
context.pathInNodeUnderVespaHome("var/jdisc_container"),
context.pathInNodeUnderVespaHome("var/jdisc_core"),
context.pathInNodeUnderVespaHome("var/maven"),
+ context.pathInNodeUnderVespaHome("var/mediasearch"), // TODO: Remove when vespa-routing is no more
context.pathInNodeUnderVespaHome("var/run"),
context.pathInNodeUnderVespaHome("var/scoreboards"),
context.pathInNodeUnderVespaHome("var/service"),
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java
index 4b086e1ea09..684f6dbcd50 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java
@@ -63,15 +63,15 @@ public class FailedExpirer extends Maintainer {
this.nodeRepository = nodeRepository;
this.zone = zone;
this.clock = clock;
- if (zone.system() == SystemName.main) {
+ if (zone.system().isCd()) {
+ defaultExpiry = containerExpiry = Duration.ofMinutes(30);
+ } else {
if (zone.environment() == Environment.staging || zone.environment() == Environment.test) {
defaultExpiry = Duration.ofHours(1);
} else {
defaultExpiry = Duration.ofDays(4);
}
containerExpiry = Duration.ofHours(1);
- } else {
- defaultExpiry = containerExpiry = Duration.ofMinutes(30);
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java
index 50a371458ce..9ace6ad55ef 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java
@@ -184,7 +184,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent {
hostProvisionerInterval = Duration.ofMinutes(5);
hostDeprovisionerInterval = Duration.ofMinutes(5);
- if (zone.environment().equals(Environment.prod) && zone.system() != SystemName.cd) {
+ if (zone.environment().equals(Environment.prod) && ! zone.system().isCd()) {
inactiveExpiry = Duration.ofHours(4); // enough time for the application owner to discover and redeploy
retiredInterval = Duration.ofMinutes(10);
dirtyExpiry = Duration.ofHours(2); // enough time to clean the node
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java
index 9d8bc047862..feccfb430e6 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java
@@ -70,8 +70,8 @@ public class DockerHostCapacity {
.filter(n -> n.type().equals(NodeType.host))
.filter(n -> speed == NodeResources.DiskSpeed.any || n.flavor().resources().diskSpeed() == speed)
.map(n -> freeCapacityOf(n, false))
- .reduce(new NodeResources(0, 0, 0, speed), NodeResources::add)
- .withDiskSpeed(speed); // Set speed to 'any' if necessary
+ .map(resources -> resources.withDiskSpeed(speed)) // Set speed to 'any' if necessary
+ .reduce(new NodeResources(0, 0, 0, speed), NodeResources::add);
}
/** Return total capacity for a given disk speed (or for any disk speed) */
@@ -80,8 +80,8 @@ public class DockerHostCapacity {
.filter(n -> n.type().equals(NodeType.host))
.filter(n -> speed == NodeResources.DiskSpeed.any || n.flavor().resources().diskSpeed() == speed)
.map(host -> host.flavor().resources())
- .reduce(new NodeResources(0, 0, 0, speed), NodeResources::add)
- .withDiskSpeed(speed); // Set speed to 'any' if necessary
+ .map(resources -> resources.withDiskSpeed(speed)) // Set speed to 'any' if necessary
+ .reduce(new NodeResources(0, 0, 0, speed), NodeResources::add);
}
public int freeCapacityInFlavorEquivalence(Flavor flavor) {
diff --git a/searchcore/src/tests/proton/attribute/attribute_test.cpp b/searchcore/src/tests/proton/attribute/attribute_test.cpp
index 992a9c56dfb..3734d2fe1dc 100644
--- a/searchcore/src/tests/proton/attribute/attribute_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_test.cpp
@@ -7,9 +7,8 @@
#include <vespa/document/update/arithmeticvalueupdate.h>
#include <vespa/document/update/assignvalueupdate.h>
#include <vespa/document/update/documentupdate.h>
-#include <vespa/eval/tensor/default_tensor.h>
#include <vespa/eval/tensor/tensor.h>
-#include <vespa/eval/tensor/tensor_factory.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/types.h>
#include <vespa/fastos/file.h>
#include <vespa/searchcommon/attribute/attributecontent.h>
@@ -75,9 +74,9 @@ using search::tensor::TensorAttribute;
using search::test::DirectoryHandler;
using std::string;
using vespalib::eval::ValueType;
+using vespalib::eval::TensorSpec;
using vespalib::tensor::Tensor;
-using vespalib::tensor::TensorCells;
-using vespalib::tensor::TensorDimensions;
+using vespalib::tensor::DefaultTensorEngine;
using AVConfig = search::attribute::Config;
using AVBasicType = search::attribute::BasicType;
@@ -612,13 +611,11 @@ TEST_F("require that filter attribute manager can return flushed serial number",
namespace {
-Tensor::UP
-createTensor(const TensorCells &cells, const TensorDimensions &dimensions) {
- vespalib::tensor::DefaultTensor::builder builder;
- return vespalib::tensor::TensorFactory::create(cells, dimensions, builder);
+Tensor::UP make_tensor(const TensorSpec &spec) {
+ auto tensor = DefaultTensorEngine::ref().from_spec(spec);
+ return Tensor::UP(dynamic_cast<Tensor*>(tensor.release()));
}
-
AttributeVector::SP
createTensorAttribute(Fixture &f) {
AVConfig cfg(AVBasicType::TENSOR);
@@ -650,8 +647,8 @@ TEST_F("Test that we can use attribute writer to write to tensor attribute",
AttributeVector::SP a1 = createTensorAttribute(f);
Schema s = createTensorSchema();
DocBuilder builder(s);
- auto tensor = createTensor({ {{{"x", "4"}, {"y", "5"}}, 7} },
- {"x", "y"});
+ auto tensor = make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "4"}, {"y", "5"}}, 7));
Document::UP doc = createTensorPutDoc(builder, *tensor);
f.put(1, *doc, 1);
EXPECT_EQUAL(2u, a1->getNumDocs());
@@ -668,8 +665,8 @@ TEST_F("require that attribute writer handles tensor assign update", Fixture)
AttributeVector::SP a1 = createTensorAttribute(f);
Schema s = createTensorSchema();
DocBuilder builder(s);
- auto tensor = createTensor({ {{{"x", "6"}, {"y", "7"}}, 9} },
- {"x", "y"});
+ auto tensor = make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "6"}, {"y", "7"}}, 9));
Document::UP doc = createTensorPutDoc(builder, *tensor);
f.put(1, *doc, 1);
EXPECT_EQUAL(2u, a1->getNumDocs());
@@ -682,8 +679,8 @@ TEST_F("require that attribute writer handles tensor assign update", Fixture)
const document::DocumentType &dt(builder.getDocumentType());
DocumentUpdate upd(*builder.getDocumentTypeRepo(), dt, DocumentId("doc::1"));
- auto new_tensor = createTensor({ {{{"x", "8"}, {"y", "9"}}, 11} },
- {"x", "y"});
+ auto new_tensor = make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "8"}, {"y", "9"}}, 11));
TensorDataType xySparseTensorDataType(vespalib::eval::ValueType::from_spec("tensor(x{},y{})"));
TensorFieldValue new_value(xySparseTensorDataType);
new_value = new_tensor->clone();
diff --git a/searchcore/src/tests/proton/docsummary/docsummary.cpp b/searchcore/src/tests/proton/docsummary/docsummary.cpp
index 38b85594a62..e8152161faa 100644
--- a/searchcore/src/tests/proton/docsummary/docsummary.cpp
+++ b/searchcore/src/tests/proton/docsummary/docsummary.cpp
@@ -2,9 +2,9 @@
#include <tests/proton/common/dummydbowner.h>
#include <vespa/config/helper/configgetter.hpp>
-#include <vespa/eval/tensor/default_tensor.h>
+#include <vespa/eval/tensor/tensor.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/serialization/typed_binary_format.h>
-#include <vespa/eval/tensor/tensor_factory.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/document/test/make_bucket_space.h>
#include <vespa/searchcore/proton/attribute/attribute_writer.h>
@@ -55,10 +55,9 @@ using search::index::schema::CollectionType;
using storage::spi::Timestamp;
using vespa::config::search::core::ProtonConfig;
using vespa::config::content::core::BucketspacesConfig;
+using vespalib::eval::TensorSpec;
using vespalib::tensor::Tensor;
-using vespalib::tensor::TensorCells;
-using vespalib::tensor::TensorDimensions;
-using vespalib::tensor::TensorFactory;
+using vespalib::tensor::DefaultTensorEngine;
using namespace vespalib::slime;
typedef std::unique_ptr<GeneralResult> GeneralResultPtr;
@@ -137,10 +136,9 @@ getDocTypeName()
return "searchdocument";
}
-Tensor::UP createTensor(const TensorCells &cells,
- const TensorDimensions &dimensions) {
- vespalib::tensor::DefaultTensor::builder builder;
- return TensorFactory::create(cells, dimensions, builder);
+Tensor::UP make_tensor(const TensorSpec &spec) {
+ auto tensor = DefaultTensorEngine::ref().from_spec(spec);
+ return Tensor::UP(dynamic_cast<Tensor*>(tensor.release()));
}
vespalib::string asVstring(vespalib::Memory str) {
@@ -750,7 +748,8 @@ Test::requireThatAttributesAreUsed()
endElement().
endField().
startAttributeField("bj").
- addTensor(createTensor({ {{{"x","f"},{"y","g"}}, 3} }, { "x", "y"})).
+ addTensor(make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "f"}, {"y", "g"}}, 3))).
endField().
endDocument(),
2);
@@ -776,7 +775,8 @@ Test::requireThatAttributesAreUsed()
"bh:[{item:40.4,weight:4},{item:50.5,weight:5}],"
"bi:[{item:'quux',weight:7},{item:'qux',weight:6}],"
"bj:'0x01020178017901016601674008000000000000'}", *rep, 0, true));
- TEST_DO(assertTensor(createTensor({ {{{"x","f"},{"y","g"}}, 3} }, { "x", "y"}),
+ TEST_DO(assertTensor(make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "f"}, {"y", "g"}}, 3)),
"bj", *rep, 0, rclass));
// empty doc
@@ -790,13 +790,15 @@ Test::requireThatAttributesAreUsed()
attributeFieldWriter.execute(attributeFieldWriter.getExecutorId(bjAttr->getNamePrefix()),
[&]() {
- bjTensorAttr->setTensor(3, *createTensor({ {{{"x", "a"},{"y", "b"}}, 4} }, { "x"}));
+ bjTensorAttr->setTensor(3, *make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "a"}, {"y", "b"}}, 4)));
bjTensorAttr->commit();
});
attributeFieldWriter.sync();
DocsumReply::UP rep2 = dc._ddb->getDocsums(req);
- TEST_DO(assertTensor(createTensor({ {{{"x","a"},{"y","b"}}, 4} }, { "x", "y"}),
+ TEST_DO(assertTensor(make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "a"}, {"y", "b"}}, 4)),
"bj", *rep2, 1, rclass));
DocsumRequest req3;
diff --git a/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp b/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp
index b82fec85d47..cc6eef14fd6 100644
--- a/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp
+++ b/searchcore/src/tests/proton/docsummary/summaryfieldconverter_test.cpp
@@ -47,8 +47,7 @@
#include <vespa/searchlib/util/slime_output_raw_buf_adapter.h>
#include <vespa/eval/tensor/tensor.h>
#include <vespa/eval/tensor/types.h>
-#include <vespa/eval/tensor/default_tensor.h>
-#include <vespa/eval/tensor/tensor_factory.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/vespalib/data/slime/slime.h>
using document::Annotation;
@@ -95,12 +94,12 @@ using vespa::config::search::SummarymapConfig;
using vespa::config::search::SummarymapConfigBuilder;
using vespalib::Slime;
using vespalib::eval::ValueType;
+using vespalib::eval::TensorSpec;
using vespalib::geo::ZCurve;
using vespalib::slime::Cursor;
using vespalib::string;
using vespalib::tensor::Tensor;
-using vespalib::tensor::TensorCells;
-using vespalib::tensor::TensorDimensions;
+using vespalib::tensor::DefaultTensorEngine;
using namespace search::docsummary;
@@ -676,11 +675,9 @@ Test::requireThatPredicateIsPrinted()
SFC::convertSummaryField(false, *doc.getValue("predicate")).get());
}
-
-Tensor::UP
-createTensor(const TensorCells &cells, const TensorDimensions &dimensions) {
- vespalib::tensor::DefaultTensor::builder builder;
- return vespalib::tensor::TensorFactory::create(cells, dimensions, builder);
+Tensor::UP make_tensor(const TensorSpec &spec) {
+ auto tensor = DefaultTensorEngine::ref().from_spec(spec);
+ return Tensor::UP(dynamic_cast<Tensor*>(tensor.release()));
}
void
@@ -688,14 +685,14 @@ Test::requireThatTensorIsNotConverted()
{
TensorDataType tensorDataType(ValueType::from_spec("tensor(x{},y{})"));
TensorFieldValue tensorFieldValue(tensorDataType);
- tensorFieldValue = createTensor({ {{{"x", "4"}, {"y", "5"}}, 7} },
- {"x", "y"});
+ tensorFieldValue = make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "4"}, {"y", "5"}}, 7));
Document doc(getDocType(), DocumentId("doc:scheme:"));
doc.setRepo(*_documentRepo);
doc.setValue("tensor", tensorFieldValue);
- TEST_CALL(checkTensor(createTensor({ {{{"x", "4"}, {"y", "5"}}, 7} },
- {"x", "y"}),
+ TEST_CALL(checkTensor(make_tensor(TensorSpec("tensor(x{},y{})")
+ .add({{"x", "4"}, {"y", "5"}}, 7)),
SFC::convertSummaryField(false,
*doc.getValue("tensor")).get()));
doc.setValue("tensor", TensorFieldValue());
diff --git a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
index af6a9d38385..10b1c829674 100644
--- a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
@@ -191,7 +191,7 @@ MySubDb::MySubDb(const std::shared_ptr<const DocumentTypeRepo> &repo, std::share
_metaStore(*_metaStoreSP),
_realRetriever(std::make_shared<MyDocumentRetriever>(repo)),
_retriever(_realRetriever),
- _subDb(_metaStoreSP, _retriever, subDbId), _docs(),
+ _subDb("my_sub_db", subDbId, _metaStoreSP, _retriever, IFeedView::SP()), _docs(),
_bucketDBHandler(*bucketDB)
{
_bucketDBHandler.addDocumentMetaStore(_metaStoreSP.get(), 0);
@@ -238,7 +238,11 @@ struct MoveFixture
void setupForBucket(const BucketId &bucket,
uint32_t sourceSubDbId,
uint32_t targetSubDbId) {
- _source._subDb._subDbId = sourceSubDbId;
+ _source._subDb = MaintenanceDocumentSubDB(_source._subDb.name(),
+ sourceSubDbId,
+ _source._subDb.meta_store(),
+ _source._subDb.retriever(),
+ _source._subDb.feed_view());
_mover.setupForBucket(bucket, &_source._subDb, targetSubDbId, _handler, _bucketDb);
}
void moveDocuments(size_t maxDocsToMove) {
diff --git a/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_compaction_test.cpp b/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_compaction_test.cpp
index 82566025a30..5305ff15003 100644
--- a/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_compaction_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/lid_space_compaction/lid_space_compaction_test.cpp
@@ -160,51 +160,59 @@ struct MyFrozenBucketHandler : public IFrozenBucketHandler
virtual void removeListener(IBucketFreezeListener *) override { }
};
-struct MyFeedView : public test::DummyFeedView
-{
- MyFeedView(const std::shared_ptr<const DocumentTypeRepo> &repo)
- : test::DummyFeedView(repo)
+struct MyFeedView : public test::DummyFeedView {
+ MyFeedView(std::shared_ptr<const DocumentTypeRepo> repo)
+ : test::DummyFeedView(std::move(repo))
{
}
};
-struct MyDocumentStore : public test::DummyDocumentStore
-{
+struct MyDocumentStore : public test::DummyDocumentStore {
Document::SP _readDoc;
mutable uint32_t _readLid;
MyDocumentStore() : _readDoc(), _readLid(0) {}
- virtual document::Document::UP
- read(search::DocumentIdT lid, const document::DocumentTypeRepo &) const override {
+ ~MyDocumentStore();
+ document::Document::UP read(search::DocumentIdT lid, const document::DocumentTypeRepo &) const override {
_readLid = lid;
return Document::UP(_readDoc->clone());
}
};
-struct MySummaryManager : public test::DummySummaryManager
-{
- MyDocumentStore _store;
- MySummaryManager() : _store() {}
- virtual search::IDocumentStore &getBackingStore() override { return _store; }
-};
+MyDocumentStore::~MyDocumentStore() = default;
-struct MySubDb : public test::DummyDocumentSubDb
-{
- std::shared_ptr<const DocumentTypeRepo> _repo;
- MySubDb(const std::shared_ptr<const DocumentTypeRepo> &repo, std::shared_ptr<BucketDBOwner> bucketDB);
- ~MySubDb();
- virtual IFeedView::SP getFeedView() const override {
- return IFeedView::SP(new MyFeedView(_repo));
+struct MyDocumentRetriever : public DocumentRetrieverBaseForTest {
+ std::shared_ptr<const DocumentTypeRepo> repo;
+ const MyDocumentStore& store;
+ MyDocumentRetriever(std::shared_ptr<const DocumentTypeRepo> repo_in, const MyDocumentStore& store_in)
+ : repo(repo_in),
+ store(store_in)
+ {
+ }
+ const document::DocumentTypeRepo& getDocumentTypeRepo() const override { return *repo; }
+ void getBucketMetaData(const storage::spi::Bucket&, DocumentMetaData::Vector& ) const override { abort(); }
+ DocumentMetaData getDocumentMetaData(const DocumentId& ) const override { abort(); }
+ Document::UP getDocument(DocumentIdT lid) const override {
+ return store.read(lid, *repo);
}
+ CachedSelect::SP parseSelect(const vespalib::string &) const override { abort(); }
};
+struct MySubDb {
+ test::DummyDocumentSubDb sub_db;
+ MaintenanceDocumentSubDB maintenance_sub_db;
+ MySubDb(std::shared_ptr<BucketDBOwner> bucket_db, const MyDocumentStore& store, std::shared_ptr<const DocumentTypeRepo> repo);
+ ~MySubDb();
+};
-MySubDb::MySubDb(const std::shared_ptr<const DocumentTypeRepo> &repo, std::shared_ptr<BucketDBOwner> bucketDB)
- : test::DummyDocumentSubDb(bucketDB, SUBDB_ID),
- _repo(repo)
+MySubDb::MySubDb(std::shared_ptr<BucketDBOwner> bucket_db, const MyDocumentStore& store, std::shared_ptr<const DocumentTypeRepo> repo)
+ : sub_db(std::move(bucket_db), SUBDB_ID),
+ maintenance_sub_db(sub_db.getName(), sub_db.getSubDbId(), sub_db.getDocumentMetaStoreContext().getSP(),
+ std::make_shared<MyDocumentRetriever>(repo, store),
+ std::make_shared<MyFeedView>(repo))
{
- _summaryManager.reset(new MySummaryManager());
}
-MySubDb::~MySubDb() {}
+
+MySubDb::~MySubDb() = default;
struct MyDirectJobRunner : public IMaintenanceJobRunner {
IMaintenanceJob &_job;
@@ -350,17 +358,15 @@ struct HandlerFixture
{
DocBuilder _docBuilder;
std::shared_ptr<BucketDBOwner> _bucketDB;
+ MyDocumentStore _docStore;
MySubDb _subDb;
- MySummaryManager &_summaryMgr;
- MyDocumentStore &_docStore;
LidSpaceCompactionHandler _handler;
HandlerFixture()
: _docBuilder(Schema()),
_bucketDB(std::make_shared<BucketDBOwner>()),
- _subDb(_docBuilder.getDocumentTypeRepo(), _bucketDB),
- _summaryMgr(static_cast<MySummaryManager &>(*_subDb.getSummaryManager())),
- _docStore(_summaryMgr._store),
- _handler(_subDb, "test")
+ _docStore(),
+ _subDb(_bucketDB, _docStore, _docBuilder.getDocumentTypeRepo()),
+ _handler(_subDb.maintenance_sub_db, "test")
{
_docStore._readDoc = _docBuilder.startDocument(DOC_ID).endDocument();
}
diff --git a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
index c3974368e54..a4f2aeb767d 100644
--- a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
@@ -523,9 +523,10 @@ MyDocumentSubDB::getSubDB()
{
IDocumentRetriever::SP retriever(new MyDocumentRetriever(*this));
- return MaintenanceDocumentSubDB(_metaStoreSP,
+ return MaintenanceDocumentSubDB("my_sub_db", _subDBId,
+ _metaStoreSP,
retriever,
- _subDBId);
+ IFeedView::SP());
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp b/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp
index cb929e0a6c7..4848c5a5d47 100644
--- a/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp
@@ -88,9 +88,9 @@ BucketMoveJob::checkBucket(const BucketId &bucket,
const MaintenanceDocumentSubDB &source(wantReady ? _notReady : _ready);
const MaintenanceDocumentSubDB &target(wantReady ? _ready : _notReady);
LOG(debug, "checkBucket(): mover.setupForBucket(%s, source:%u, target:%u)",
- bucket.toString().c_str(), source._subDbId, target._subDbId);
- mover.setupForBucket(bucket, &source, target._subDbId,
- _moveHandler, _ready._metaStore->getBucketDB());
+ bucket.toString().c_str(), source.sub_db_id(), target.sub_db_id());
+ mover.setupForBucket(bucket, &source, target.sub_db_id(),
+ _moveHandler, _ready.meta_store()->getBucketDB());
}
BucketMoveJob::ScanResult
@@ -98,7 +98,7 @@ BucketMoveJob::scanBuckets(size_t maxBucketsToScan, IFrozenBucketHandler::Exclus
{
size_t bucketsScanned = 0;
bool passDone = false;
- ScanIterator itr(_ready._metaStore->getBucketDB().takeGuard(),
+ ScanIterator itr(_ready.meta_store()->getBucketDB().takeGuard(),
_scanPass, _scanPos._lastBucket, _endPos._lastBucket);
BucketId bucket;
for (; itr.valid() &&
@@ -250,7 +250,7 @@ BucketMoveJob::deactivateBucket(BucketId bucket)
void
BucketMoveJob::activateBucket(BucketId bucket)
{
- BucketDBOwner::Guard notReadyBdb(_notReady._metaStore->getBucketDB().takeGuard());
+ BucketDBOwner::Guard notReadyBdb(_notReady.meta_store()->getBucketDB().takeGuard());
if (notReadyBdb->get(bucket).getDocumentCount() == 0) {
return; // notready bucket already empty. This is the normal case.
}
@@ -291,7 +291,7 @@ BucketMoveJob::scanAndMove(size_t maxBucketsToScan,
while (!_delayedBuckets.empty() && _delayedMover.bucketDone()) {
const BucketId bucket = *_delayedBuckets.begin();
_delayedBuckets.erase(_delayedBuckets.begin());
- ScanIterator itr(_ready._metaStore->getBucketDB().takeGuard(), bucket);
+ ScanIterator itr(_ready.meta_store()->getBucketDB().takeGuard(), bucket);
if (itr.getBucket() == bucket) {
checkBucket(bucket, itr, _delayedMover, bucketGuard);
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp b/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp
index 3c73532fbba..d6d8ba03a67 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp
@@ -24,12 +24,12 @@ DocumentBucketMover::moveDocument(DocumentIdT lid,
const document::GlobalId &gid,
Timestamp timestamp)
{
- Document::SP doc(_source->_retriever->getDocument(lid).release());
+ Document::SP doc(_source->retriever()->getDocument(lid).release());
if (!doc || doc->getId().getGlobalId() != gid)
return; // Failed to retrieve document, removed or changed identity
// TODO(geirst): what if doc is NULL?
BucketId bucketId = _bucket.stripUnused();
- MoveOperation op(bucketId, timestamp, doc, DbDocumentId(_source->_subDbId, lid), _targetSubDbId);
+ MoveOperation op(bucketId, timestamp, doc, DbDocumentId(_source->sub_db_id(), lid), _targetSubDbId);
// We cache the bucket for the document we are going to move to avoid getting
// inconsistent bucket info (getBucketInfo()) while moving between ready and not-ready
@@ -103,16 +103,16 @@ DocumentBucketMover::moveDocuments(size_t maxDocsToMove)
if (_bucketDone) {
return;
}
- Iterator itr = (_lastGidValid ? _source->_metaStore->upperBound(_lastGid)
- : _source->_metaStore->lowerBound(_bucket));
- const Iterator end = _source->_metaStore->upperBound(_bucket);
+ Iterator itr = (_lastGidValid ? _source->meta_store()->upperBound(_lastGid)
+ : _source->meta_store()->lowerBound(_bucket));
+ const Iterator end = _source->meta_store()->upperBound(_bucket);
size_t docsMoved = 0;
size_t docsSkipped = 0; // In absence of a proper cost metric
typedef std::vector<MoveKey> MoveVec;
MoveVec toMove;
for (; itr != end && docsMoved < maxDocsToMove; ++itr) {
DocumentIdT lid = itr.getKey();
- const RawDocumentMetaData &metaData = _source->_metaStore->getRawMetaData(lid);
+ const RawDocumentMetaData &metaData = _source->meta_store()->getRawMetaData(lid);
if (metaData.getBucketUsedBits() != _bucket.getUsedBits()) {
++docsSkipped;
if (docsSkipped >= 50) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
index 16e8d7ae90a..5aacfa2678c 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
@@ -1,5 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "bootstrapconfig.h"
#include "combiningfeedview.h"
#include "commit_and_wait_document_retriever.h"
#include "document_meta_store_read_guards.h"
@@ -7,29 +8,29 @@
#include "documentdb.h"
#include "documentdbconfigscout.h"
#include "idocumentdbowner.h"
+#include "idocumentsubdb.h"
#include "lid_space_compaction_handler.h"
#include "maintenance_jobs_injector.h"
#include "reconfig_params.h"
-#include "bootstrapconfig.h"
#include <vespa/document/repo/documenttyperepo.h>
-#include <vespa/searchcore/proton/metrics/executor_threading_service_stats.h>
+#include <vespa/searchcommon/common/schemaconfigurer.h>
#include <vespa/searchcore/proton/attribute/attribute_writer.h>
#include <vespa/searchcore/proton/attribute/imported_attributes_repo.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/searchcore/proton/common/statusreport.h>
+#include <vespa/searchcore/proton/docsummary/isummarymanager.h>
#include <vespa/searchcore/proton/feedoperation/noopoperation.h>
#include <vespa/searchcore/proton/index/index_writer.h>
#include <vespa/searchcore/proton/initializer/task_runner.h>
+#include <vespa/searchcore/proton/metrics/executor_threading_service_stats.h>
#include <vespa/searchcore/proton/metrics/metricswireservice.h>
-#include <vespa/searchcore/proton/reference/i_document_db_reference_resolver.h>
-#include <vespa/searchcore/proton/reference/i_document_db_reference_registry.h>
#include <vespa/searchcore/proton/reference/document_db_reference_resolver.h>
-#include <vespa/searchcore/proton/docsummary/isummarymanager.h>
+#include <vespa/searchcore/proton/reference/i_document_db_reference_registry.h>
+#include <vespa/searchcore/proton/reference/i_document_db_reference_resolver.h>
#include <vespa/searchlib/attribute/attributefactory.h>
#include <vespa/searchlib/attribute/configconverter.h>
#include <vespa/searchlib/engine/docsumreply.h>
#include <vespa/searchlib/engine/searchreply.h>
-#include <vespa/searchcommon/common/schemaconfigurer.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/closuretask.h>
#include <vespa/vespalib/util/exceptions.h>
@@ -205,9 +206,11 @@ DocumentDB::DocumentDB(const vespalib::string &baseDir,
_clusterStateHandler.addClusterStateChangedHandler(this);
// Forward changes of cluster state to bucket handler
_clusterStateHandler.addClusterStateChangedHandler(&_bucketHandler);
- for (auto subDb : _subDBs) {
- _lidSpaceCompactionHandlers.push_back(std::make_unique<LidSpaceCompactionHandler>(*subDb, _docTypeName.getName()));
- }
+
+ _lidSpaceCompactionHandlers.push_back(std::make_unique<LidSpaceCompactionHandler>(_maintenanceController.getReadySubDB(), _docTypeName.getName()));
+ _lidSpaceCompactionHandlers.push_back(std::make_unique<LidSpaceCompactionHandler>(_maintenanceController.getRemSubDB(), _docTypeName.getName()));
+ _lidSpaceCompactionHandlers.push_back(std::make_unique<LidSpaceCompactionHandler>(_maintenanceController.getNotReadySubDB(), _docTypeName.getName()));
+
_writeFilter.setConfig(loaded_config->getMaintenanceConfigSP()->getAttributeUsageFilterConfig());
fastos::TimeStamp visibilityDelay = loaded_config->getMaintenanceConfigSP()->getVisibilityDelay();
_visibility.setVisibilityDelay(visibilityDelay);
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp b/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp
index 70e548e8c05..682719436f1 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp
@@ -139,18 +139,21 @@ wrapRetriever(const IDocumentRetriever::SP &retriever, ICommitable &commit)
void DocumentSubDBCollection::maintenanceSync(MaintenanceController &mc, ICommitable &commit) {
RetrieversSP retrievers = getRetrievers();
- MaintenanceDocumentSubDB readySubDB(
- getReadySubDB()->getDocumentMetaStoreContext().getSP(),
- wrapRetriever((*retrievers)[_readySubDbId], commit),
- _readySubDbId);
- MaintenanceDocumentSubDB remSubDB(
- getRemSubDB()->getDocumentMetaStoreContext().getSP(),
- (*retrievers)[_remSubDbId],
- _remSubDbId);
- MaintenanceDocumentSubDB notReadySubDB(
- getNotReadySubDB()->getDocumentMetaStoreContext().getSP(),
- wrapRetriever((*retrievers)[_notReadySubDbId], commit),
- _notReadySubDbId);
+ MaintenanceDocumentSubDB readySubDB(getReadySubDB()->getName(),
+ _readySubDbId,
+ getReadySubDB()->getDocumentMetaStoreContext().getSP(),
+ wrapRetriever((*retrievers)[_readySubDbId], commit),
+ getReadySubDB()->getFeedView());
+ MaintenanceDocumentSubDB remSubDB(getRemSubDB()->getName(),
+ _remSubDbId,
+ getRemSubDB()->getDocumentMetaStoreContext().getSP(),
+ (*retrievers)[_remSubDbId],
+ getRemSubDB()->getFeedView());
+ MaintenanceDocumentSubDB notReadySubDB(getNotReadySubDB()->getName(),
+ _notReadySubDbId,
+ getNotReadySubDB()->getDocumentMetaStoreContext().getSP(),
+ wrapRetriever((*retrievers)[_notReadySubDbId], commit),
+ getNotReadySubDB()->getFeedView());
mc.syncSubDBs(readySubDB, remSubDB, notReadySubDB);
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.cpp b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.cpp
index a77aa302fea..c4dc26a0875 100644
--- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.cpp
@@ -16,8 +16,8 @@ using storage::spi::Timestamp;
namespace proton {
-LidSpaceCompactionHandler::LidSpaceCompactionHandler(IDocumentSubDB &subDb,
- const vespalib::string &docTypeName)
+LidSpaceCompactionHandler::LidSpaceCompactionHandler(const MaintenanceDocumentSubDB& subDb,
+ const vespalib::string& docTypeName)
: _subDb(subDb),
_docTypeName(docTypeName)
{
@@ -26,27 +26,24 @@ LidSpaceCompactionHandler::LidSpaceCompactionHandler(IDocumentSubDB &subDb,
LidUsageStats
LidSpaceCompactionHandler::getLidStatus() const
{
- return _subDb.getDocumentMetaStoreContext().get().getLidUsageStats();
+ return _subDb.meta_store()->getLidUsageStats();
}
IDocumentScanIterator::UP
LidSpaceCompactionHandler::getIterator() const
{
- return IDocumentScanIterator::UP(new DocumentScanIterator(
- _subDb.getDocumentMetaStoreContext().get()));
+ return std::make_unique<DocumentScanIterator>(*_subDb.meta_store());
}
MoveOperation::UP
LidSpaceCompactionHandler::createMoveOperation(const search::DocumentMetaData &document, uint32_t moveToLid) const
{
- IFeedView::SP feedView = _subDb.getFeedView();
- const ISummaryManager::SP &summaryMan = _subDb.getSummaryManager();
const uint32_t moveFromLid = document.lid;
- Document::UP doc = summaryMan->getBackingStore().read(moveFromLid, *feedView->getDocumentTypeRepo());
- MoveOperation::UP op(new MoveOperation(document.bucketId, document.timestamp,
- Document::SP(doc.release()),
- DbDocumentId(_subDb.getSubDbId(), moveFromLid),
- _subDb.getSubDbId()));
+ auto doc = _subDb.retriever()->getDocument(moveFromLid);
+ auto op = std::make_unique<MoveOperation>(document.bucketId, document.timestamp,
+ Document::SP(doc.release()),
+ DbDocumentId(_subDb.sub_db_id(), moveFromLid),
+ _subDb.sub_db_id());
op->setTargetLid(moveToLid);
return op;
}
@@ -54,14 +51,14 @@ LidSpaceCompactionHandler::createMoveOperation(const search::DocumentMetaData &d
void
LidSpaceCompactionHandler::handleMove(const MoveOperation& op, IDestructorCallback::SP doneCtx)
{
- _subDb.getFeedView()->handleMove(op, std::move(doneCtx));
+ _subDb.feed_view()->handleMove(op, std::move(doneCtx));
}
void
LidSpaceCompactionHandler::handleCompactLidSpace(const CompactLidSpaceOperation &op)
{
- assert(_subDb.getSubDbId() == op.getSubDbId());
- _subDb.getFeedView()->handleCompactLidSpace(op);
+ assert(_subDb.sub_db_id() == op.getSubDbId());
+ _subDb.feed_view()->handleCompactLidSpace(op);
}
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.h b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.h
index 0861513a909..21d20001923 100644
--- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.h
+++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_handler.h
@@ -2,7 +2,7 @@
#pragma once
#include "i_lid_space_compaction_handler.h"
-#include "idocumentsubdb.h"
+#include "maintenancedocumentsubdb.h"
namespace proton {
@@ -12,18 +12,18 @@ namespace proton {
class LidSpaceCompactionHandler : public ILidSpaceCompactionHandler
{
private:
- IDocumentSubDB &_subDb;
+ const MaintenanceDocumentSubDB& _subDb;
vespalib::string _docTypeName;
public:
- LidSpaceCompactionHandler(IDocumentSubDB &subDb,
- const vespalib::string &docTypeName);
+ LidSpaceCompactionHandler(const MaintenanceDocumentSubDB& subDb,
+ const vespalib::string& docTypeName);
// Implements ILidSpaceCompactionHandler
virtual vespalib::string getName() const override {
- return _docTypeName + "." + _subDb.getName();
+ return _docTypeName + "." + _subDb.name();
}
- virtual uint32_t getSubDbId() const override { return _subDb.getSubDbId(); }
+ virtual uint32_t getSubDbId() const override { return _subDb.sub_db_id(); }
virtual search::LidUsageStats getLidStatus() const override;
virtual IDocumentScanIterator::UP getIterator() const override;
virtual MoveOperation::UP createMoveOperation(const search::DocumentMetaData &document, uint32_t moveToLid) const override;
diff --git a/searchcore/src/vespa/searchcore/proton/server/maintenance_jobs_injector.cpp b/searchcore/src/vespa/searchcore/proton/server/maintenance_jobs_injector.cpp
index 744ce2aa7f4..6b130dfa144 100644
--- a/searchcore/src/vespa/searchcore/proton/server/maintenance_jobs_injector.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/maintenance_jobs_injector.cpp
@@ -111,8 +111,8 @@ MaintenanceJobsInjector::injectJobs(MaintenanceController &controller,
controller.registerJobInMasterThread(MUP(new DocumentDBCommitJob(commit, config.getVisibilityDelay())));
}
const MaintenanceDocumentSubDB &mRemSubDB(controller.getRemSubDB());
- MUP pruneRDjob(new PruneRemovedDocumentsJob(config.getPruneRemovedDocumentsConfig(), *mRemSubDB._metaStore,
- mRemSubDB._subDbId, docTypeName, prdHandler, fbHandler));
+ MUP pruneRDjob(new PruneRemovedDocumentsJob(config.getPruneRemovedDocumentsConfig(), *mRemSubDB.meta_store(),
+ mRemSubDB.sub_db_id(), docTypeName, prdHandler, fbHandler));
controller.registerJobInMasterThread(
trackJob(jobTrackers.getRemovedDocumentsPrune(), std::move(pruneRDjob)));
if (!config.getLidSpaceCompactionConfig().isDisabled()) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/maintenancecontroller.cpp b/searchcore/src/vespa/searchcore/proton/server/maintenancecontroller.cpp
index 79135d27e5c..e6afb8a19b2 100644
--- a/searchcore/src/vespa/searchcore/proton/server/maintenancecontroller.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/maintenancecontroller.cpp
@@ -197,6 +197,19 @@ MaintenanceController::newConfig(const DocumentDBMaintenanceConfig::SP &config)
restart();
}
+namespace {
+
+void
+assert_equal_meta_store_instances(const MaintenanceDocumentSubDB& old_db,
+ const MaintenanceDocumentSubDB& new_db)
+{
+ if (old_db.valid() && new_db.valid()) {
+ assert(old_db.meta_store().get() == new_db.meta_store().get());
+ }
+}
+
+}
+
void
MaintenanceController::syncSubDBs(const MaintenanceDocumentSubDB &readySubDB,
const MaintenanceDocumentSubDB &remSubDB,
@@ -206,11 +219,16 @@ MaintenanceController::syncSubDBs(const MaintenanceDocumentSubDB &readySubDB,
bool oldValid = _readySubDB.valid();
assert(readySubDB.valid());
assert(remSubDB.valid());
+ // Document meta store instances should not change. Maintenance jobs depend on this fact.
+ assert_equal_meta_store_instances(_readySubDB, readySubDB);
+ assert_equal_meta_store_instances(_remSubDB, remSubDB);
+ assert_equal_meta_store_instances(_notReadySubDB, notReadySubDB);
_readySubDB = readySubDB;
_remSubDB = remSubDB;
_notReadySubDB = notReadySubDB;
- if (!oldValid && _started)
+ if (!oldValid && _started) {
restart();
+ }
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.cpp b/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.cpp
index ffaea1ff576..6b781eb8e0f 100644
--- a/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.cpp
@@ -5,26 +5,37 @@
namespace proton {
MaintenanceDocumentSubDB::MaintenanceDocumentSubDB()
- : _metaStore(),
- _retriever(),
- _subDbId(0u)
-{ }
+ : _name(""),
+ _sub_db_id(0),
+ _meta_store(nullptr),
+ _retriever(nullptr),
+ _feed_view(nullptr)
+{
+}
-MaintenanceDocumentSubDB::~MaintenanceDocumentSubDB() { }
+MaintenanceDocumentSubDB::~MaintenanceDocumentSubDB() = default;
-MaintenanceDocumentSubDB::MaintenanceDocumentSubDB(const IDocumentMetaStore::SP & metaStore,
- const IDocumentRetriever::SP & retriever,
- uint32_t subDbId)
- : _metaStore(metaStore),
- _retriever(retriever),
- _subDbId(subDbId)
-{ }
+MaintenanceDocumentSubDB::MaintenanceDocumentSubDB(const vespalib::string& name,
+ uint32_t sub_db_id,
+ IDocumentMetaStore::SP meta_store,
+ IDocumentRetriever::SP retriever,
+ IFeedView::SP feed_view)
+ : _name(name),
+ _sub_db_id(sub_db_id),
+ _meta_store(std::move(meta_store)),
+ _retriever(std::move(retriever)),
+ _feed_view(std::move(feed_view))
+{
+}
void
-MaintenanceDocumentSubDB::clear() {
- _metaStore.reset();
+MaintenanceDocumentSubDB::clear()
+{
+ _name = "";
+ _sub_db_id = 0;
+ _meta_store.reset();
_retriever.reset();
- _subDbId = 0u;
-}
+ _feed_view.reset();
+}
-} // namespace proton
+}
diff --git a/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.h b/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.h
index 41c43d910d7..bfe73f361b6 100644
--- a/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.h
+++ b/searchcore/src/vespa/searchcore/proton/server/maintenancedocumentsubdb.h
@@ -2,6 +2,7 @@
#pragma once
+#include "ifeedview.h"
#include <vespa/searchcore/proton/documentmetastore/i_document_meta_store.h>
#include <vespa/searchcore/proton/persistenceengine/i_document_retriever.h>
@@ -13,19 +14,30 @@ namespace proton {
*/
class MaintenanceDocumentSubDB
{
-public:
- IDocumentMetaStore::SP _metaStore;
- IDocumentRetriever::SP _retriever;
- uint32_t _subDbId;
+private:
+ vespalib::string _name;
+ uint32_t _sub_db_id;
+ IDocumentMetaStore::SP _meta_store;
+ IDocumentRetriever::SP _retriever;
+ IFeedView::SP _feed_view;
+public:
MaintenanceDocumentSubDB();
~MaintenanceDocumentSubDB();
- MaintenanceDocumentSubDB(const IDocumentMetaStore::SP & metaStore,
- const IDocumentRetriever::SP & retriever,
- uint32_t subDbId);
+ MaintenanceDocumentSubDB(const vespalib::string& name,
+ uint32_t sub_db_id,
+ IDocumentMetaStore::SP meta_store,
+ IDocumentRetriever::SP retriever,
+ IFeedView::SP feed_view);
+
+ const vespalib::string& name() const { return _name; }
+ uint32_t sub_db_id() const { return _sub_db_id; }
+ const IDocumentMetaStore::SP& meta_store() const { return _meta_store; }
+ const IDocumentRetriever::SP& retriever() const { return _retriever; }
+ const IFeedView::SP& feed_view() const { return _feed_view; }
- bool valid() const { return bool(_metaStore); }
+ bool valid() const { return _meta_store.get() != nullptr; }
void clear();
};
diff --git a/searchlib/CMakeLists.txt b/searchlib/CMakeLists.txt
index bae47872d6c..f032bbe9c30 100644
--- a/searchlib/CMakeLists.txt
+++ b/searchlib/CMakeLists.txt
@@ -163,6 +163,7 @@ vespa_define_module(
src/tests/hitcollector
src/tests/index/docbuilder
src/tests/index/doctypebuilder
+ src/tests/index/field_length_calculator
src/tests/indexmetainfo
src/tests/ld-library-path
src/tests/memoryindex/compact_words_store
diff --git a/searchlib/src/tests/attribute/imported_attribute_vector/imported_attribute_vector_test.cpp b/searchlib/src/tests/attribute/imported_attribute_vector/imported_attribute_vector_test.cpp
index 1e24a784fd9..cfa05b7a765 100644
--- a/searchlib/src/tests/attribute/imported_attribute_vector/imported_attribute_vector_test.cpp
+++ b/searchlib/src/tests/attribute/imported_attribute_vector/imported_attribute_vector_test.cpp
@@ -1,8 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/eval/tensor/default_tensor.h>
#include <vespa/eval/tensor/tensor.h>
-#include <vespa/eval/tensor/tensor_factory.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/searchcommon/attribute/search_context_params.h>
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/searchlib/tensor/i_tensor_attribute.h>
@@ -13,11 +12,17 @@ using search::attribute::IAttributeVector;
using search::tensor::ITensorAttribute;
using search::tensor::TensorAttribute;
using vespalib::eval::ValueType;
-using vespalib::tensor::DenseTensorCells;
+using vespalib::eval::TensorSpec;
using vespalib::tensor::Tensor;
-using vespalib::tensor::TensorCells;
-using vespalib::tensor::TensorDimensions;
-using vespalib::tensor::TensorFactory;
+using vespalib::tensor::DefaultTensorEngine;
+
+Tensor::UP createTensor(const TensorSpec &spec) {
+ auto value = DefaultTensorEngine::ref().from_spec(spec);
+ Tensor *tensor = dynamic_cast<Tensor*>(value.get());
+ ASSERT_TRUE(tensor != nullptr);
+ value.release();
+ return Tensor::UP(tensor);
+}
namespace search::attribute {
@@ -482,32 +487,23 @@ TEST("onSerializeForDescendingSort() is forwarded with remapped LID to target ve
}
struct TensorAttrFixture : Fixture {
- vespalib::tensor::DefaultTensor::builder builder;
std::shared_ptr<Tensor> tensor1;
std::shared_ptr<Tensor> tensor2;
TensorAttrFixture(bool dense)
: Fixture(),
- builder(),
tensor1(),
tensor2()
{
setup(dense);
}
- Tensor::UP createTensor(const TensorCells &cells,
- const TensorDimensions &dimensions) {
- return TensorFactory::create(cells, dimensions, builder);
- }
- Tensor::UP createDenseTensor(const DenseTensorCells &cells) const {
- return TensorFactory::createDense(cells);
- }
void setup(bool dense) {
if (dense) {
- tensor1 = createDenseTensor({ {{{"x",1}}, 11} });
- tensor2 = createDenseTensor({ {{{"x",0}}, 12}, {{{"x", 1}}, 0} });
+ tensor1 = createTensor(TensorSpec("tensor(x[2])").add({{"x", 1}}, 11));
+ tensor2 = createTensor(TensorSpec("tensor(x[2])").add({{"x", 0}}, 12).add({{"x", 1}}, 0));
} else {
- tensor1 = createTensor({ {{{"x","1"}}, 11} }, { "x" });
- tensor2 = createTensor({ {{{"x","0"}}, 12} }, { "x" });
+ tensor1 = createTensor(TensorSpec("tensor(x{})").add({{"x", "1"}}, 11));
+ tensor2 = createTensor(TensorSpec("tensor(x{})").add({{"x", "0"}}, 12));
}
const std::vector<ImportedAttributeFixture::LidToLidMapping<std::shared_ptr<Tensor>>> mappings =
{ {DocId(2), dummy_gid(3), DocId(3), tensor1 },
diff --git a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
index cbbaa518b16..e0ec4a6fd06 100644
--- a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
+++ b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
@@ -6,8 +6,9 @@
#include <vespa/searchlib/tensor/generic_tensor_attribute.h>
#include <vespa/searchlib/tensor/dense_tensor_attribute.h>
#include <vespa/searchlib/attribute/attributeguard.h>
-#include <vespa/eval/tensor/tensor_factory.h>
-#include <vespa/eval/tensor/default_tensor.h>
+#include <vespa/eval/tensor/tensor.h>
+#include <vespa/eval/tensor/dense/dense_tensor.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/data/fileheader.h>
#include <vespa/fastos/file.h>
@@ -21,11 +22,10 @@ using search::tensor::GenericTensorAttribute;
using search::AttributeGuard;
using search::AttributeVector;
using vespalib::eval::ValueType;
+using vespalib::eval::TensorSpec;
using vespalib::tensor::Tensor;
-using vespalib::tensor::TensorCells;
-using vespalib::tensor::DenseTensorCells;
-using vespalib::tensor::TensorDimensions;
-using vespalib::tensor::TensorFactory;
+using vespalib::tensor::DenseTensor;
+using vespalib::tensor::DefaultTensorEngine;
namespace vespalib {
namespace tensor {
@@ -41,6 +41,17 @@ static bool operator==(const Tensor &lhs, const Tensor &rhs)
vespalib::string sparseSpec("tensor(x{},y{})");
vespalib::string denseSpec("tensor(x[2],y[3])");
+Tensor::UP createTensor(const TensorSpec &spec) {
+ auto value = DefaultTensorEngine::ref().from_spec(spec);
+ if (value->is_double()) {
+ return Tensor::UP(new DenseTensor(ValueType::double_type(), {value->as_double()}));
+ }
+ Tensor *tensor = dynamic_cast<Tensor*>(value.get());
+ ASSERT_TRUE(tensor != nullptr);
+ value.release();
+ return Tensor::UP(tensor);
+}
+
struct Fixture
{
using BasicType = search::attribute::BasicType;
@@ -52,7 +63,6 @@ struct Fixture
vespalib::string _typeSpec;
std::shared_ptr<TensorAttribute> _tensorAttr;
std::shared_ptr<AttributeVector> _attr;
- vespalib::tensor::DefaultTensor::builder _builder;
bool _denseTensors;
bool _useDenseTensorAttribute;
@@ -63,7 +73,6 @@ struct Fixture
_typeSpec(typeSpec),
_tensorAttr(),
_attr(),
- _builder(),
_denseTensors(false),
_useDenseTensorAttribute(useDenseTensorAttribute)
{
@@ -85,17 +94,6 @@ struct Fixture
}
}
- Tensor::UP createTensor(const TensorCells &cells) {
- return TensorFactory::create(cells, _builder);
- }
- Tensor::UP createTensor(const TensorCells &cells,
- const TensorDimensions &dimensions) {
- return TensorFactory::create(cells, dimensions, _builder);
- }
- Tensor::UP createDenseTensor(const DenseTensorCells &cells) const {
- return TensorFactory::createDense(cells);
- }
-
void ensureSpace(uint32_t docId) {
while (_attr->getNumDocs() <= docId) {
uint32_t newDocId = 0u;
@@ -138,19 +136,9 @@ struct Fixture
}
void
- assertGetTensor(const TensorCells &expCells,
- const TensorDimensions &expDimensions,
- uint32_t docId)
- {
- Tensor::UP expTensor = createTensor(expCells, expDimensions);
- assertGetTensor(*expTensor, docId);
- }
-
- void
- assertGetDenseTensor(const DenseTensorCells &expCells,
- uint32_t docId)
+ assertGetTensor(const TensorSpec &expSpec, uint32_t docId)
{
- Tensor::UP expTensor = createDenseTensor(expCells);
+ Tensor::UP expTensor = createTensor(expSpec);
assertGetTensor(*expTensor, docId);
}
@@ -166,72 +154,27 @@ struct Fixture
EXPECT_TRUE(loadok);
}
- bool isUnbound(const vespalib::string &dimensionName) const
- {
- ValueType type = _cfg.tensorType();
- for (const auto &dim : type.dimensions()) {
- if (dim.name == dimensionName && !dim.is_bound()) {
- return true;
- }
- }
- return false;
- }
-
Tensor::UP expDenseTensor3() const
{
- if (isUnbound("x")) {
- if (isUnbound("y")) {
- return createDenseTensor({ {{{"x",0},{"y",1}}, 11} });
- }
- return createDenseTensor({ {{{"x",0},{"y",1}}, 11},
- {{{"x",0},{"y",2}}, 0} });
- } else if (isUnbound("y")) {
- return createDenseTensor({ {{{"x",0},{"y",1}}, 11},
- {{{"x",1},{"y",0}}, 0} });
- }
- return createDenseTensor({ {{{"x",0},{"y",1}}, 11},
- {{{"x",1},{"y",2}}, 0} });
+ return createTensor(TensorSpec(denseSpec)
+ .add({{"x", 0}, {"y", 1}}, 11)
+ .add({{"x", 1}, {"y", 2}}, 0));
}
Tensor::UP expDenseFillTensor() const
{
- if (isUnbound("x")) {
- if (isUnbound("y")) {
- return createDenseTensor({ {{{"x",0},{"y",0}}, 5} });
- }
- return createDenseTensor({ {{{"x",0},{"y",0}}, 5},
- {{{"x",0},{"y",2}}, 0} });
- } else if (isUnbound("y")) {
- return createDenseTensor({ {{{"x",0},{"y",0}}, 5},
- {{{"x",1},{"y",0}}, 0} });
- }
- return createDenseTensor({ {{{"x",0},{"y",0}}, 5},
- {{{"x",1},{"y",2}}, 0} });
+ return createTensor(TensorSpec(denseSpec)
+ .add({{"x", 0}, {"y", 0}}, 5)
+ .add({{"x", 1}, {"y", 2}}, 0));
}
Tensor::UP expEmptyDenseTensor() const
{
- if (isUnbound("x")) {
- if (isUnbound("y")) {
- return createDenseTensor({ {{{"x",0},{"y",0}}, 0} });
- }
- return createDenseTensor({ {{{"x",0},{"y",2}}, 0} });
- } else if (isUnbound("y")) {
- return createDenseTensor({ {{{"x",1},{"y",0}}, 0} });
- }
- return createDenseTensor({ {{{"x",1},{"y",2}}, 0} });
+ return createTensor(TensorSpec(denseSpec));
}
vespalib::string expEmptyDenseTensorSpec() const {
- if (isUnbound("x")) {
- if (isUnbound("y")) {
- return "tensor(x[1],y[1])";
- }
- return "tensor(x[1],y[3])";
- } else if (isUnbound("y")) {
- return "tensor(x[2],y[1])";
- }
- return "tensor(x[2],y[3])";
+ return denseSpec;
}
void testEmptyAttribute();
@@ -257,7 +200,7 @@ Fixture::testSetTensorValue()
EXPECT_EQUAL(5u, _attr->getNumDocs());
EXPECT_EQUAL(5u, _attr->getCommittedDocIdLimit());
TEST_DO(assertGetNoTensor(4));
- EXPECT_EXCEPTION(setTensor(4, *createTensor({}, {})),
+ EXPECT_EXCEPTION(setTensor(4, *createTensor(TensorSpec("double"))),
WrongTensorTypeException,
"but other tensor type is 'double'");
TEST_DO(assertGetNoTensor(4));
@@ -267,9 +210,11 @@ Fixture::testSetTensorValue()
setTensor(3, *expDenseTensor3());
TEST_DO(assertGetTensor(*expDenseTensor3(), 3));
} else {
- TEST_DO(assertGetTensor({}, {"x", "y"}, 4));
- setTensor(3, *createTensor({ {{}, 11} }, { "x", "y"}));
- TEST_DO(assertGetTensor({ {{}, 11} }, { "x", "y"}, 3));
+ TEST_DO(assertGetTensor(TensorSpec(sparseSpec), 4));
+ setTensor(3, *createTensor(TensorSpec(sparseSpec)
+ .add({{"x", ""}, {"y", ""}}, 11)));
+ TEST_DO(assertGetTensor(TensorSpec(sparseSpec)
+ .add({{"x", ""}, {"y", ""}}, 11), 3));
}
TEST_DO(assertGetNoTensor(2));
TEST_DO(clearTensor(3));
@@ -284,7 +229,8 @@ Fixture::testSaveLoad()
if (_denseTensors) {
setTensor(3, *expDenseTensor3());
} else {
- setTensor(3, *createTensor({ {{{"y","1"}}, 11} }, { "x", "y"}));
+ setTensor(3, *createTensor(TensorSpec(sparseSpec)
+ .add({{"x", ""}, {"y", "1"}}, 11)));
}
TEST_DO(save());
TEST_DO(load());
@@ -294,8 +240,9 @@ Fixture::testSaveLoad()
TEST_DO(assertGetTensor(*expDenseTensor3(), 3));
TEST_DO(assertGetTensor(*expEmptyDenseTensor(), 4));
} else {
- TEST_DO(assertGetTensor({ {{{"y","1"}}, 11} }, { "x", "y"}, 3));
- TEST_DO(assertGetTensor({}, {"x", "y"}, 4));
+ TEST_DO(assertGetTensor(TensorSpec(sparseSpec)
+ .add({{"x", ""}, {"y", "1"}}, 11), 3));
+ TEST_DO(assertGetTensor(TensorSpec(sparseSpec), 4));
}
TEST_DO(assertGetNoTensor(2));
}
@@ -310,9 +257,11 @@ Fixture::testCompaction()
}
ensureSpace(4);
Tensor::UP emptytensor = _tensorAttr->getEmptyTensor();
- Tensor::UP emptyxytensor = createTensor({}, {"x", "y"});
- Tensor::UP simpletensor = createTensor({ {{{"y","1"}}, 11} }, { "x", "y"});
- Tensor::UP filltensor = createTensor({ {{}, 5} }, { "x", "y"});
+ Tensor::UP emptyxytensor = createTensor(TensorSpec(sparseSpec));
+ Tensor::UP simpletensor = createTensor(TensorSpec(sparseSpec)
+ .add({{"x", ""}, {"y", "1"}}, 11));
+ Tensor::UP filltensor = createTensor(TensorSpec(sparseSpec)
+ .add({{"x", ""}, {"y", ""}}, 5));
if (_denseTensors) {
emptyxytensor = expEmptyDenseTensor();
simpletensor = expDenseTensor3();
diff --git a/searchlib/src/tests/features/constant/constant_test.cpp b/searchlib/src/tests/features/constant/constant_test.cpp
index f9d1736ba9e..e558839d2cf 100644
--- a/searchlib/src/tests/features/constant/constant_test.cpp
+++ b/searchlib/src/tests/features/constant/constant_test.cpp
@@ -7,9 +7,8 @@
#include <vespa/searchlib/fef/test/indexenvironment.h>
#include <vespa/eval/eval/function.h>
#include <vespa/eval/eval/tensor_spec.h>
-#include <vespa/eval/tensor/default_tensor.h>
+#include <vespa/eval/tensor/tensor.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
-#include <vespa/eval/tensor/tensor_factory.h>
using search::feature_t;
using namespace search::fef;
@@ -22,21 +21,11 @@ using vespalib::eval::DoubleValue;
using vespalib::eval::TensorSpec;
using vespalib::eval::ValueType;
using vespalib::tensor::DefaultTensorEngine;
-using vespalib::tensor::DenseTensorCells;
using vespalib::tensor::Tensor;
-using vespalib::tensor::TensorCells;
-using vespalib::tensor::TensorDimensions;
-using vespalib::tensor::TensorFactory;
namespace
{
-Tensor::UP createTensor(const TensorCells &cells,
- const TensorDimensions &dimensions) {
- vespalib::tensor::DefaultTensor::builder builder;
- return TensorFactory::create(cells, dimensions, builder);
-}
-
Tensor::UP make_tensor(const TensorSpec &spec) {
auto tensor = DefaultTensorEngine::ref().from_spec(spec);
return Tensor::UP(dynamic_cast<Tensor*>(tensor.release()));
@@ -72,10 +61,9 @@ struct ExecFixture
return extractDouble(docId);
}
void addTensor(const vespalib::string &name,
- const TensorCells &cells,
- const TensorDimensions &dimensions)
+ const TensorSpec &spec)
{
- Tensor::UP tensor = createTensor(cells, dimensions);
+ Tensor::UP tensor = make_tensor(spec);
ValueType type(tensor->type());
test.getIndexEnv().addConstantValue(name,
std::move(type),
@@ -100,10 +88,10 @@ TEST_F("require that existing tensor constant is detected",
ExecFixture("constant(foo)"))
{
f.addTensor("foo",
- { {{{"x", "a"}}, 3},
- {{{"x", "b"}}, 5},
- {{{"x", "c"}}, 7} },
- { "x" });
+ TensorSpec("tensor(x{})")
+ .add({{"x","a"}}, 3)
+ .add({{"x","b"}}, 5)
+ .add({{"x","c"}}, 7));
EXPECT_TRUE(f.setup());
auto expect = make_tensor(TensorSpec("tensor(x{})")
.add({{"x","b"}}, 5)
diff --git a/searchlib/src/tests/features/tensor/tensor_test.cpp b/searchlib/src/tests/features/tensor/tensor_test.cpp
index abe54906791..3aa2671fd25 100644
--- a/searchlib/src/tests/features/tensor/tensor_test.cpp
+++ b/searchlib/src/tests/features/tensor/tensor_test.cpp
@@ -15,7 +15,6 @@
#include <vespa/eval/tensor/default_tensor.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/serialization/typed_binary_format.h>
-#include <vespa/eval/tensor/tensor_factory.h>
#include <vespa/eval/tensor/test/test_utils.h>
#include <vespa/vespalib/objects/nbostream.h>
@@ -36,7 +35,6 @@ using vespalib::tensor::DenseTensorCells;
using vespalib::tensor::Tensor;
using vespalib::tensor::TensorCells;
using vespalib::tensor::TensorDimensions;
-using vespalib::tensor::TensorFactory;
using vespalib::tensor::test::makeTensor;
using AVC = search::attribute::Config;
diff --git a/searchlib/src/tests/index/field_length_calculator/CMakeLists.txt b/searchlib/src/tests/index/field_length_calculator/CMakeLists.txt
new file mode 100644
index 00000000000..df09d0abaa7
--- /dev/null
+++ b/searchlib/src/tests/index/field_length_calculator/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchlib_field_length_calculator_test_app TEST
+ SOURCES
+ field_length_calculator_test.cpp
+ DEPENDS
+ searchlib
+ gtest
+)
+vespa_add_test(NAME searchlib_field_length_calculator_test_app COMMAND searchlib_field_length_calculator_test_app)
diff --git a/searchlib/src/tests/index/field_length_calculator/field_length_calculator_test.cpp b/searchlib/src/tests/index/field_length_calculator/field_length_calculator_test.cpp
new file mode 100644
index 00000000000..c99d241cbc0
--- /dev/null
+++ b/searchlib/src/tests/index/field_length_calculator/field_length_calculator_test.cpp
@@ -0,0 +1,68 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchlib/index/field_length_calculator.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using search::index::FieldLengthCalculator;
+
+namespace search::index {
+
+namespace {
+
+// Arithmetic average of arithmetic sequence 1, 2, ... , samples
+double arith_avg(uint32_t samples) {
+ return static_cast<double>(samples + 1) / 2;
+}
+
+}
+
+TEST(FieldLengthCalculatorTest, empty_is_zero)
+{
+ FieldLengthCalculator calc;
+ EXPECT_EQ(0.0, calc.get_average_field_length());
+ EXPECT_EQ(0, calc.get_num_samples());
+}
+
+TEST(FieldLengthCalculatorTest, startup_is_average)
+{
+ FieldLengthCalculator calc;
+ calc.add_field_length(3);
+ EXPECT_DOUBLE_EQ(3.0, calc.get_average_field_length());
+ EXPECT_EQ(1, calc.get_num_samples());
+ calc.add_field_length(4);
+ EXPECT_DOUBLE_EQ(3.5, calc.get_average_field_length());
+ EXPECT_EQ(2, calc.get_num_samples());
+ calc.add_field_length(7);
+ EXPECT_DOUBLE_EQ((3 + 4 + 7)/3.0, calc.get_average_field_length());
+ EXPECT_EQ(3, calc.get_num_samples());
+ calc.add_field_length(9);
+ EXPECT_DOUBLE_EQ((3 + 4 + 7 + 9)/4.0, calc.get_average_field_length());
+ EXPECT_EQ(4, calc.get_num_samples());
+}
+
+TEST(FieldLengthCalculatorTest, average_until_max_num_samples)
+{
+ const uint32_t max_num_samples = 5;
+ FieldLengthCalculator calc(0.0, 0, max_num_samples);
+ static constexpr double epsilon = 0.000000001; // Allowed difference
+ for (uint32_t i = 0; i + 1 < max_num_samples; ++i) {
+ calc.add_field_length(i + 1);
+ }
+ // Arithmetic average
+ EXPECT_NEAR(arith_avg(max_num_samples - 1), calc.get_average_field_length(), epsilon);
+ EXPECT_EQ(max_num_samples - 1, calc.get_num_samples());
+ calc.add_field_length(max_num_samples);
+ // Arithmetic average
+ EXPECT_NEAR(arith_avg(max_num_samples), calc.get_average_field_length(), epsilon);
+ EXPECT_EQ(max_num_samples, calc.get_num_samples());
+ calc.add_field_length(max_num_samples + 1);
+ // No longer arithmetic average
+ EXPECT_LT(arith_avg(max_num_samples + 1), calc.get_average_field_length());
+ // Switched to exponential decay
+ EXPECT_NEAR((arith_avg(max_num_samples) * (max_num_samples - 1) + max_num_samples + 1) / max_num_samples, calc.get_average_field_length(), epsilon);
+ EXPECT_EQ(max_num_samples, calc.get_num_samples());
+}
+
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/memoryindex/field_inverter/field_inverter_test.cpp b/searchlib/src/tests/memoryindex/field_inverter/field_inverter_test.cpp
index f08e61b0da2..a818bb75bf2 100644
--- a/searchlib/src/tests/memoryindex/field_inverter/field_inverter_test.cpp
+++ b/searchlib/src/tests/memoryindex/field_inverter/field_inverter_test.cpp
@@ -98,6 +98,16 @@ makeDoc16(DocBuilder &b)
return b.endDocument();
}
+Document::UP
+makeDoc17(DocBuilder &b)
+{
+ b.startDocument("doc::17");
+ b.startIndexField("f1").addStr("foo0").addStr("bar0").endField();
+ b.startIndexField("f2").startElement(1).addStr("foo").addStr("bar").endElement().startElement(1).addStr("bar").endElement().endField();
+ b.startIndexField("f3").startElement(3).addStr("foo2").addStr("bar2").endElement().startElement(4).addStr("bar2").endElement().endField();
+ return b.endDocument();
+}
+
}
struct Fixture
@@ -323,6 +333,23 @@ TEST_F("require that multiple words at same position works", Fixture)
f._inserter.toStr());
}
+TEST_F("require that cheap features are calculated", Fixture)
+{
+ f.invertDocument(17, *makeDoc17(f._b));
+ f._inserter.setVerbose();
+ f._inserter.set_show_cheap_features();
+ f.pushDocuments();
+ EXPECT_EQUAL("f=1,"
+ "w=bar0,a=17(fl=2,occs=1,e=0,w=1,l=2[1]),"
+ "w=foo0,a=17(fl=2,occs=1,e=0,w=1,l=2[0]),"
+ "f=2,"
+ "w=bar,a=17(fl=3,occs=2,e=0,w=1,l=2[1],e=1,w=1,l=1[0]),"
+ "w=foo,a=17(fl=3,occs=1,e=0,w=1,l=2[0]),"
+ "f=3,"
+ "w=bar2,a=17(fl=3,occs=2,e=0,w=3,l=2[1],e=1,w=4,l=1[0]),"
+ "w=foo2,a=17(fl=3,occs=1,e=0,w=3,l=2[0])",
+ f._inserter.toStr());
+}
} // namespace memoryindex
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/index/field_length_calculator.h b/searchlib/src/vespa/searchlib/index/field_length_calculator.h
new file mode 100644
index 00000000000..50d47ced063
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/index/field_length_calculator.h
@@ -0,0 +1,42 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <atomic>
+#include <algorithm>
+
+namespace search::index {
+
+/**
+ * Class used to calculate average field length, with a bias towards
+ * the latest field lengths when max_num_samples samples have been reached.
+ */
+class FieldLengthCalculator {
+ std::atomic<double> _average_field_length;
+ uint32_t _num_samples; // Capped by _max_num_samples
+ uint32_t _max_num_samples;
+
+public:
+ FieldLengthCalculator()
+ : FieldLengthCalculator(0.0, 0)
+ {
+ }
+
+ FieldLengthCalculator(double average_field_length, uint32_t num_samples, uint32_t max_num_samples = 100000)
+ : _average_field_length(average_field_length),
+ _num_samples(std::min(num_samples, max_num_samples)),
+ _max_num_samples(max_num_samples)
+ {
+ }
+
+ double get_average_field_length() const { return _average_field_length.load(std::memory_order_relaxed); }
+ uint32_t get_num_samples() const { return _num_samples; }
+ uint32_t get_max_num_samples() { return _max_num_samples; }
+
+ void add_field_length(uint32_t field_length) {
+ if (_num_samples < _max_num_samples) {
+ ++_num_samples;
+ }
+ _average_field_length.store((_average_field_length.load(std::memory_order_relaxed) * (_num_samples - 1) + field_length) / _num_samples, std::memory_order_relaxed);
+ }
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp b/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp
index d19f05a98ee..bfa0143d395 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp
+++ b/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp
@@ -279,6 +279,29 @@ FieldInverter::remove(const vespalib::stringref word, uint32_t docId)
}
void
+FieldInverter::endDoc()
+{
+ uint32_t field_length = 0;
+ if (_elem > 0) {
+ auto itr = _elems.end() - _elem;
+ while (itr != _elems.end()) {
+ field_length += itr->_len;
+ ++itr;
+ }
+ itr = _elems.end() - _elem;
+ while (itr != _elems.end()) {
+ itr->set_field_length(field_length);
+ ++itr;
+ }
+ }
+ uint32_t newPosSize = static_cast<uint32_t>(_positions.size());
+ _pendingDocs.insert({ _docId,
+ { _oldPosSize, newPosSize - _oldPosSize } });
+ _docId = 0;
+ _oldPosSize = newPosSize;
+}
+
+void
FieldInverter::processNormalDocTextField(const StringFieldValue &field)
{
startElement(1);
@@ -500,6 +523,7 @@ FieldInverter::pushDocuments(IOrderedFieldIndexInserter &inserter)
(void) numWordIds;
if (lastWordNum != i._wordNum || lastDocId != i._docId) {
if (!emptyFeatures) {
+ _features.set_num_occs(_features.word_positions().size());
inserter.add(lastDocId, _features);
emptyFeatures = true;
}
@@ -520,6 +544,8 @@ FieldInverter::pushDocuments(IOrderedFieldIndexInserter &inserter)
_features.clear(lastDocId);
lastElemId = NO_ELEMENT_ID;
lastWordPos = NO_WORD_POS;
+ const ElemInfo &elem = _elems[i._elemRef];
+ _features.set_field_length(elem.get_field_length());
} else {
continue; // ignore dup remove
}
@@ -539,6 +565,7 @@ FieldInverter::pushDocuments(IOrderedFieldIndexInserter &inserter)
}
if (!emptyFeatures) {
+ _features.set_num_occs(_features.word_positions().size());
inserter.add(lastDocId, _features);
}
inserter.flush();
diff --git a/searchlib/src/vespa/searchlib/memoryindex/field_inverter.h b/searchlib/src/vespa/searchlib/memoryindex/field_inverter.h
index ba6a0e96698..e547dbe98c6 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/field_inverter.h
+++ b/searchlib/src/vespa/searchlib/memoryindex/field_inverter.h
@@ -99,14 +99,18 @@ private:
public:
int32_t _weight;
uint32_t _len;
+ uint32_t _field_length;
ElemInfo(int32_t weight)
: _weight(weight),
- _len(0u)
+ _len(0u),
+ _field_length(0u)
{
}
void setLen(uint32_t len) { _len = len; }
+ uint32_t get_field_length() const { return _field_length; }
+ void set_field_length(uint32_t field_length) { _field_length = field_length; }
};
using ElemInfoVec = std::vector<ElemInfo>;
@@ -317,13 +321,7 @@ public:
_wpos = 0;
}
- void endDoc() {
- uint32_t newPosSize = static_cast<uint32_t>(_positions.size());
- _pendingDocs.insert({ _docId,
- { _oldPosSize, newPosSize - _oldPosSize } });
- _docId = 0;
- _oldPosSize = newPosSize;
- }
+ void endDoc();
void addWord(const vespalib::stringref word) {
uint32_t wordRef = saveWord(word);
diff --git a/searchlib/src/vespa/searchlib/test/memoryindex/ordered_field_index_inserter.h b/searchlib/src/vespa/searchlib/test/memoryindex/ordered_field_index_inserter.h
index a341e36045e..f984bd8fcbd 100644
--- a/searchlib/src/vespa/searchlib/test/memoryindex/ordered_field_index_inserter.h
+++ b/searchlib/src/vespa/searchlib/test/memoryindex/ordered_field_index_inserter.h
@@ -11,6 +11,7 @@ class OrderedFieldIndexInserter : public IOrderedFieldIndexInserter {
std::stringstream _ss;
bool _first;
bool _verbose;
+ bool _show_cheap_features;
uint32_t _fieldId;
void
@@ -27,6 +28,7 @@ public:
: _ss(),
_first(true),
_verbose(false),
+ _show_cheap_features(false),
_fieldId(0)
{
}
@@ -55,6 +57,11 @@ public:
_ss << "(";
auto wpi = features.word_positions().begin();
bool firstElement = true;
+ if (_show_cheap_features) {
+ _ss << "fl=" << features.field_length() <<
+ ",occs=" << features.num_occs();
+ firstElement = false;
+ }
for (auto &el : features.elements()) {
if (!firstElement) {
_ss << ",";
@@ -70,6 +77,7 @@ public:
}
firstWordPos = false;
_ss << wpi->getWordPos();
+ ++wpi;
}
_ss << "]";
}
@@ -105,6 +113,7 @@ public:
}
void setVerbose() { _verbose = true; }
+ void set_show_cheap_features() { _show_cheap_features = true; }
};
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp b/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp
index 72cedb05f7c..1cac789ad21 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp
@@ -31,7 +31,6 @@
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/data/slime/slime.h>
-#include <vespa/eval/tensor/serialization/slime_binary_format.h>
#include <vespa/eval/tensor/serialization/typed_binary_format.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/searchlib/util/slime_output_raw_buf_adapter.h>
diff --git a/vespajlib/abi-spec.json b/vespajlib/abi-spec.json
index c31eed32830..04e68e60178 100644
--- a/vespajlib/abi-spec.json
+++ b/vespajlib/abi-spec.json
@@ -788,7 +788,11 @@
],
"methods": [
"public static com.yahoo.tensor.IndexedTensor$Builder of(com.yahoo.tensor.TensorType)",
+ "public static com.yahoo.tensor.IndexedTensor$Builder of(com.yahoo.tensor.TensorType, float[])",
+ "public static com.yahoo.tensor.IndexedTensor$Builder of(com.yahoo.tensor.TensorType, double[])",
"public static com.yahoo.tensor.IndexedTensor$Builder of(com.yahoo.tensor.TensorType, com.yahoo.tensor.DimensionSizes)",
+ "public static com.yahoo.tensor.IndexedTensor$Builder of(com.yahoo.tensor.TensorType, com.yahoo.tensor.DimensionSizes, float[])",
+ "public static com.yahoo.tensor.IndexedTensor$Builder of(com.yahoo.tensor.TensorType, com.yahoo.tensor.DimensionSizes, double[])",
"public varargs abstract com.yahoo.tensor.IndexedTensor$Builder cell(double, long[])",
"public varargs abstract com.yahoo.tensor.IndexedTensor$Builder cell(float, long[])",
"public com.yahoo.tensor.TensorType type()",
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/IndexedDoubleTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/IndexedDoubleTensor.java
index 285837a1bc6..e644244178d 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/IndexedDoubleTensor.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/IndexedDoubleTensor.java
@@ -43,8 +43,12 @@ class IndexedDoubleTensor extends IndexedTensor {
private double[] values;
BoundDoubleBuilder(TensorType type, DimensionSizes sizes) {
+ this(type, sizes, new double[(int)sizes.totalSize()]);
+ }
+
+ BoundDoubleBuilder(TensorType type, DimensionSizes sizes, double[] values) {
super(type, sizes);
- values = new double[(int)sizes.totalSize()];
+ this.values = values;
}
@Override
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/IndexedFloatTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/IndexedFloatTensor.java
index 8f8c24c8421..30157d9791a 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/IndexedFloatTensor.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/IndexedFloatTensor.java
@@ -43,8 +43,16 @@ class IndexedFloatTensor extends IndexedTensor {
private float[] values;
BoundFloatBuilder(TensorType type, DimensionSizes sizes) {
+ this(type, sizes, new float[(int)sizes.totalSize()]);
+ }
+
+ BoundFloatBuilder(TensorType type, DimensionSizes sizes, float[] values) {
super(type, sizes);
- values = new float[(int)sizes.totalSize()];
+ if (sizes.totalSize() != values.length) {
+ throw new IllegalArgumentException("Invalid size(" + values.length + ") of supplied value vector." +
+ " Type specifies that size should be " + sizes.totalSize());
+ }
+ this.values = values;
}
@Override
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java
index 19edfc0269e..a03131f3ec9 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java
@@ -236,29 +236,106 @@ public abstract class IndexedTensor implements Tensor {
}
/**
+ * Creates a builder initialized with the given values
+ *
+ * @param type the type of the tensor to build
+ * @param values the initial values of the tensor. This <b>transfers ownership</b> of the value array - it
+ * must not be further mutated by the caller
+ */
+ public static Builder of(TensorType type, float[] values) {
+ if (type.dimensions().stream().allMatch(d -> d instanceof TensorType.IndexedBoundDimension))
+ return of(type, BoundBuilder.dimensionSizesOf(type), values);
+ else
+ return new UnboundBuilder(type);
+ }
+
+ /**
+ * Creates a builder initialized with the given values
+ *
+ * @param type the type of the tensor to build
+ * @param values the initial values of the tensor. This <b>transfers ownership</b> of the value array - it
+ * must not be further mutated by the caller
+ */
+ public static Builder of(TensorType type, double[] values) {
+ if (type.dimensions().stream().allMatch(d -> d instanceof TensorType.IndexedBoundDimension))
+ return of(type, BoundBuilder.dimensionSizesOf(type), values);
+ else
+ return new UnboundBuilder(type);
+ }
+
+ /**
* Create a builder with dimension size information for this instance. Must be one size entry per dimension,
* and, agree with the type size information when specified in the type.
* If sizes are completely specified in the type this size information is redundant.
*/
public static Builder of(TensorType type, DimensionSizes sizes) {
+ validate(type, sizes);
+
+ if (type.valueType() == TensorType.Value.FLOAT)
+ return new IndexedFloatTensor.BoundFloatBuilder(type, sizes);
+ else if (type.valueType() == TensorType.Value.DOUBLE)
+ return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes);
+ else
+ return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes); // Default
+ }
+
+ /**
+ * Creates a builder initialized with the given values
+ *
+ * @param type the type of the tensor to build
+ * @param values the initial values of the tensor. This <b>transfers ownership</b> of the value array - it
+ * must not be further mutated by the caller
+ */
+ public static Builder of(TensorType type, DimensionSizes sizes, float[] values) {
+ validate(type, sizes);
+ validateSizes(sizes, values.length);
+
+ if (type.valueType() == TensorType.Value.FLOAT)
+ return new IndexedFloatTensor.BoundFloatBuilder(type, sizes, values);
+ else if (type.valueType() == TensorType.Value.DOUBLE)
+ return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes).fill(values);
+ else
+ return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes).fill(values); // Default
+ }
+
+ /**
+ * Creates a builder initialized with the given values
+ *
+ * @param type the type of the tensor to build
+ * @param values the initial values of the tensor. This <b>transfers ownership</b> of the value array - it
+ * must not be further mutated by the caller
+ */
+ public static Builder of(TensorType type, DimensionSizes sizes, double[] values) {
+ validate(type, sizes);
+ validateSizes(sizes, values.length);
+
+ if (type.valueType() == TensorType.Value.FLOAT)
+ return new IndexedFloatTensor.BoundFloatBuilder(type, sizes).fill(values);
+ else if (type.valueType() == TensorType.Value.DOUBLE)
+ return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes, values);
+ else
+ return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes, values); // Default
+ }
+
+ private static void validateSizes(DimensionSizes sizes, int length) {
+ if (sizes.totalSize() != length) {
+ throw new IllegalArgumentException("Invalid size(" + length + ") of supplied value vector." +
+ " Type specifies that size should be " + sizes.totalSize());
+ }
+ }
+
+ private static void validate(TensorType type, DimensionSizes sizes) {
// validate
if (sizes.dimensions() != type.dimensions().size())
throw new IllegalArgumentException(sizes.dimensions() +
- " is the wrong number of dimensions for " + type);
+ " is the wrong number of dimensions for " + type);
for (int i = 0; i < sizes.dimensions(); i++ ) {
Optional<Long> size = type.dimensions().get(i).size();
if (size.isPresent() && size.get() < sizes.size(i))
throw new IllegalArgumentException("Size of dimension " + type.dimensions().get(i).name() + " is " +
- sizes.size(i) +
- " but cannot be larger than " + size.get() + " in " + type);
+ sizes.size(i) +
+ " but cannot be larger than " + size.get() + " in " + type);
}
-
- if (type.valueType() == TensorType.Value.FLOAT)
- return new IndexedFloatTensor.BoundFloatBuilder(type, sizes);
- else if (type.valueType() == TensorType.Value.DOUBLE)
- return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes);
- else
- return new IndexedDoubleTensor.BoundDoubleBuilder(type, sizes); // Default
}
public abstract Builder cell(double value, long ... indexes);
@@ -290,6 +367,20 @@ public abstract class IndexedTensor implements Tensor {
throw new IllegalArgumentException("Must have a dimension size entry for each dimension in " + type);
this.sizes = sizes;
}
+ BoundBuilder fill(float [] values) {
+ long index = 0;
+ for (float value : values) {
+ cellByDirectIndex(index++, value);
+ }
+ return this;
+ }
+ BoundBuilder fill(double [] values) {
+ long index = 0;
+ for (double value : values) {
+ cellByDirectIndex(index++, value);
+ }
+ return this;
+ }
DimensionSizes sizes() { return sizes; }
diff --git a/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java
index a5fc3d5a5d8..4bfdb53e321 100644
--- a/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java
@@ -10,6 +10,7 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* @author bratseth
@@ -41,6 +42,37 @@ public class IndexedTensorTestCase {
assertTrue(singleValueFromString instanceof IndexedTensor);
assertEquals(singleValue, singleValueFromString);
}
+
+ private void verifyFloat(String spec) {
+ float [] floats = {1.0f, 2.0f, 3.0f};
+ Tensor tensor = IndexedTensor.Builder.of(TensorType.fromSpec(spec), floats).build();
+ int index = 0;
+ for (Double cell : tensor.cells().values()) {
+ assertEquals(cell, Double.valueOf(floats[index++]));
+ }
+ }
+ private void verifyDouble(String spec) {
+ double [] values = {1.0, 2.0, 3.0};
+ Tensor tensor = IndexedTensor.Builder.of(TensorType.fromSpec(spec), values).build();
+ int index = 0;
+ for (Double cell : tensor.cells().values()) {
+ assertEquals(cell, Double.valueOf(values[index++]));
+ }
+ }
+
+ @Test
+ public void testBoundHandoverBuilding() {
+ verifyFloat("tensor<float>(x[3])");
+ verifyDouble("tensor<float>(x[3])");
+ verifyFloat("tensor<double>(x[3])");
+ verifyDouble("tensor<double>(x[3])");
+ try {
+ verifyDouble("tensor<double>(x[4])");
+ fail("Expect IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Invalid size(3) of supplied value vector. Type specifies that size should be 4", e.getMessage());
+ }
+ }
@Test
public void testBoundBuilding() {
diff --git a/vespalib/src/tests/util/rcuvector/CMakeLists.txt b/vespalib/src/tests/util/rcuvector/CMakeLists.txt
index 77900b8422d..8aa07763bef 100644
--- a/vespalib/src/tests/util/rcuvector/CMakeLists.txt
+++ b/vespalib/src/tests/util/rcuvector/CMakeLists.txt
@@ -3,6 +3,6 @@ vespa_add_executable(vespalib_rcuvector_test_app TEST
SOURCES
rcuvector_test.cpp
DEPENDS
- searchlib
+ vespalib
)
vespa_add_test(NAME vespalib_rcuvector_test_app COMMAND vespalib_rcuvector_test_app)