aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/DispatchSpec.java82
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomDispatchBuilder.java53
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/DispatchGroupBuilder.java39
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java21
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/MultilevelDispatchValidator.java89
-rw-r--r--config-model/src/main/resources/schema/content.rnc1
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java4
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java20
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java35
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java35
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ExpeditedChangeApplicationMaintainer.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java24
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java16
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java19
-rw-r--r--searchcore/src/vespa/searchcore/grouping/groupingcontext.cpp89
-rw-r--r--searchcore/src/vespa/searchcore/grouping/groupingcontext.h38
-rw-r--r--searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp28
-rw-r--r--searchcore/src/vespa/searchcore/grouping/groupingsession.cpp9
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/result_processor.h1
-rw-r--r--searchlib/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/attribute/sort_blob_writers/CMakeLists.txt9
-rw-r--r--searchlib/src/tests/attribute/sort_blob_writers/sort_blob_writers_test.cpp344
-rw-r--r--searchlib/src/tests/groupingengine/groupingengine_benchmark.cpp11
-rw-r--r--searchlib/src/tests/sort/CMakeLists.txt2
-rw-r--r--searchlib/src/tests/sort/sort_test.cpp121
-rw-r--r--searchlib/src/vespa/searchlib/aggregation/group.cpp20
-rw-r--r--searchlib/src/vespa/searchlib/aggregation/grouping.cpp64
-rw-r--r--searchlib/src/vespa/searchlib/aggregation/grouping.h14
-rw-r--r--searchlib/src/vespa/searchlib/aggregation/groupinglevel.h1
-rw-r--r--searchlib/src/vespa/searchlib/attribute/CMakeLists.txt2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.cpp77
-rw-r--r--searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.h25
-rw-r--r--searchlib/src/vespa/searchlib/attribute/sort_blob_writer.h21
-rw-r--r--searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.cpp72
-rw-r--r--searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.h31
-rw-r--r--searchlib/src/vespa/searchlib/expression/aggregationrefnode.h24
-rw-r--r--searchlib/src/vespa/searchlib/expression/expressiontree.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/expression/expressiontree.h3
-rw-r--r--vespalib/src/vespa/vespalib/util/sort.h1
51 files changed, 854 insertions, 674 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
index 9621e8ab9eb..5bb73643de5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
@@ -304,6 +304,7 @@ public class NodesSpecification {
default -> 0; // No constraint on other types
};
if (resources.diskGb() < minDiskGb) {
+ // TODO(mpolden): Consider enforcing this on Vespa 9
deployLogger.logApplicationPackage(Level.WARNING, "Requested disk (" + resources.diskGb() +
"Gb) in " + clusterId + " is not large enough to fit " +
"core/heap dumps. Minimum recommended disk resources " +
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchSpec.java b/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchSpec.java
deleted file mode 100644
index 69557e1a974..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/DispatchSpec.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.content;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents the dispatch setup for a content cluster.
- * This EITHER has a number of dispatch groups OR a an explicit list of groups.
- *
- * @author geirst
- */
-public class DispatchSpec {
-
- private final Integer numDispatchGroups;
- private final List<Group> groups;
-
- private DispatchSpec(Builder builder) {
- numDispatchGroups = builder.numDispatchGroups;
- groups = builder.groups;
- }
-
- public Integer getNumDispatchGroups() { return numDispatchGroups; }
-
- public List<Group> getGroups() {
- return groups;
- }
-
- public boolean valid() {
- return numDispatchGroups != null || groups != null;
- }
-
- /**
- * Reference to a node which is contained in a dispatch group.
- */
- public static class Node {
- private final int distributionKey;
- public Node(int distributionKey) {
- this.distributionKey = distributionKey;
- }
- public int getDistributionKey() {
- return distributionKey;
- }
- }
-
- /**
- * A dispatch group with a list of nodes contained in that group.
- */
- public static class Group {
- private final List<Node> nodes = new ArrayList<>();
- public Group() {
-
- }
- public Group addNode(Node node) {
- nodes.add(node);
- return this;
- }
- public List<Node> getNodes() {
- return nodes;
- }
- }
-
- public static class Builder {
-
- private Integer numDispatchGroups;
- private List<Group> groups;
-
- public DispatchSpec build() {
- return new DispatchSpec(this);
- }
-
- public Builder setNumDispatchGroups(Integer numDispatchGroups) {
- this.numDispatchGroups = numDispatchGroups;
- return this;
- }
-
- public Builder setGroups(List<Group> groups) {
- this.groups = groups;
- return this;
- }
- }
-}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
index f792ac3a591..f2d55b4d49f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
@@ -64,6 +64,8 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
+import static java.util.logging.Level.WARNING;
+
/**
* A content cluster.
*
@@ -190,7 +192,9 @@ public class ContentCluster extends TreeConfigProducer<AnyConfigProducer> implem
index.setQueryTimeout(queryTimeout);
}
index.setSearchCoverage(DomSearchCoverageBuilder.build(element));
- index.setDispatchSpec(DomDispatchBuilder.build(element));
+ if (element.child("dispatch") != null)
+ logger.logApplicationPackage(WARNING, "The <dispatch> element is deprecated and ignored and will be removed in the next major release. "
+ + " See https://docs.vespa.ai/en/reference/services-content.html#dispatch for details.");
if (index.getTuning() == null)
index.setTuning(new Tuning(index));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomDispatchBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomDispatchBuilder.java
deleted file mode 100644
index 57da49fa351..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomDispatchBuilder.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.content.cluster;
-
-
-import com.yahoo.vespa.model.content.DispatchSpec;
-import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Builder for the dispatch setup for a content cluster.
- *
- * @author geirst
- */
-public class DomDispatchBuilder {
-
- public static DispatchSpec build(ModelElement contentXml) {
- DispatchSpec.Builder builder = new DispatchSpec.Builder();
- ModelElement dispatchElement = contentXml.child("dispatch");
- if (dispatchElement == null) {
- return builder.build();
- }
- builder.setNumDispatchGroups(dispatchElement.childAsInteger("num-dispatch-groups"));
-
- List<ModelElement> groupsElement = dispatchElement.subElements("group");
- if (groupsElement != null) {
- builder.setGroups(buildGroups(groupsElement));
- }
- return builder.build();
- }
-
- private static List<DispatchSpec.Group> buildGroups(List<ModelElement> groupsElement) {
- List<DispatchSpec.Group> groups = new ArrayList<>();
- for (ModelElement groupElement : groupsElement) {
- groups.add(buildGroup(groupElement));
- }
- return groups;
- }
-
- private static DispatchSpec.Group buildGroup(ModelElement groupElement) {
- List<ModelElement> nodes = groupElement.subElements("node");
- DispatchSpec.Group group = new DispatchSpec.Group();
- for (ModelElement nodeElement : nodes) {
- group.addNode(buildNode(nodeElement));
- }
- return group;
- }
-
- private static DispatchSpec.Node buildNode(ModelElement nodeElement) {
- return new DispatchSpec.Node(nodeElement.integerAttribute("distribution-key"));
- }
-}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/DispatchGroupBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/search/DispatchGroupBuilder.java
deleted file mode 100644
index f1676da2491..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/DispatchGroupBuilder.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.search;
-
-import com.yahoo.vespa.model.content.DispatchSpec;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class used to build the mid-level dispatch groups in an indexed content cluster.
- *
- * @author geirst
- */
-public class DispatchGroupBuilder {
-
- public static List<DispatchSpec.Group> createDispatchGroups(List<SearchNode> searchNodes,
- int numDispatchGroups) {
- if (numDispatchGroups > searchNodes.size())
- numDispatchGroups = searchNodes.size();
-
- List<DispatchSpec.Group> groupsSpec = new ArrayList<>();
- int numNodesPerGroup = searchNodes.size() / numDispatchGroups;
- if (searchNodes.size() % numDispatchGroups != 0) {
- numNodesPerGroup += 1;
- }
- int searchNodeIdx = 0;
- for (int i = 0; i < numDispatchGroups; ++i) {
- DispatchSpec.Group groupSpec = new DispatchSpec.Group();
- for (int j = 0; j < numNodesPerGroup && searchNodeIdx < searchNodes.size(); ++j) {
- groupSpec.addNode(new DispatchSpec.Node(searchNodes.get(searchNodeIdx++).getDistributionKey()));
- }
- groupsSpec.add(groupSpec);
- }
- assert(searchNodeIdx == searchNodes.size());
- return groupsSpec;
- }
-
-}
-
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
index 960850def1f..a71e72c0ef8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
@@ -7,11 +7,11 @@ import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.producer.AnyConfigProducer;
import com.yahoo.config.model.producer.TreeConfigProducer;
import com.yahoo.prelude.fastsearch.DocumentdbInfoConfig;
-import com.yahoo.search.config.IndexInfoConfig;
-import com.yahoo.search.config.SchemaInfoConfig;
import com.yahoo.schema.DocumentOnlySchema;
import com.yahoo.schema.derived.DerivedConfiguration;
import com.yahoo.schema.derived.SchemaInfo;
+import com.yahoo.search.config.IndexInfoConfig;
+import com.yahoo.search.config.SchemaInfoConfig;
import com.yahoo.vespa.config.search.AttributesConfig;
import com.yahoo.vespa.config.search.DispatchConfig;
import com.yahoo.vespa.config.search.DispatchConfig.DistributionPolicy;
@@ -19,8 +19,6 @@ import com.yahoo.vespa.config.search.DispatchNodesConfig;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import com.yahoo.vespa.configdefinition.IlscriptsConfig;
-import com.yahoo.vespa.model.container.docproc.DocprocChain;
-import com.yahoo.vespa.model.content.DispatchSpec;
import com.yahoo.vespa.model.content.DispatchTuning;
import com.yahoo.vespa.model.content.SearchCoverage;
@@ -56,7 +54,6 @@ public class IndexedSearchCluster extends SearchCluster
private int redundancy = 1;
private final DispatchGroup rootDispatch;
- private DispatchSpec dispatchSpec;
private final List<SearchNode> searchNodes = new ArrayList<>();
private final DispatchTuning.DispatchPolicy defaultDispatchPolicy;
private final double dispatchWarmup;
@@ -226,20 +223,6 @@ public class IndexedSearchCluster extends SearchCluster
this.redundancy = redundancy;
}
- public void setDispatchSpec(DispatchSpec dispatchSpec) {
- if (dispatchSpec.getNumDispatchGroups() != null) {
- this.dispatchSpec = new DispatchSpec.Builder().setGroups
- (DispatchGroupBuilder.createDispatchGroups(getSearchNodes(),
- dispatchSpec.getNumDispatchGroups())).build();
- } else {
- this.dispatchSpec = dispatchSpec;
- }
- }
-
- public DispatchSpec getDispatchSpec() {
- return dispatchSpec;
- }
-
private static DistributionPolicy.Enum toDistributionPolicy(DispatchTuning.DispatchPolicy tuning) {
return switch (tuning) {
case ADAPTIVE: yield DistributionPolicy.ADAPTIVE;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/MultilevelDispatchValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/search/MultilevelDispatchValidator.java
deleted file mode 100644
index 666fd77226b..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/MultilevelDispatchValidator.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.search;
-
-import com.yahoo.vespa.model.content.DispatchSpec;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Class used to validate that multilevel dispatch is correctly setup in an indexed content cluster.
- *
- * @author geirst
- */
-public class MultilevelDispatchValidator {
-
- private final String clusterName;
- private final DispatchSpec dispatchSpec;
- private final List<SearchNode> searchNodes;
-
- public MultilevelDispatchValidator(String clusterName,
- DispatchSpec dispatchSpec,
- List<SearchNode> searchNodes) {
- this.clusterName = clusterName;
- this.dispatchSpec = dispatchSpec;
- this.searchNodes = searchNodes;
- }
-
- public void validate() {
- validateThatWeReferenceNodesOnlyOnce();
- validateThatWeReferenceAllNodes();
- validateThatWeUseValidNodeReferences();
- }
-
- private void validateThatWeReferenceNodesOnlyOnce() {
- Set<Integer> distKeys = new HashSet<>();
- for (DispatchSpec.Group group : dispatchSpec.getGroups()) {
- for (DispatchSpec.Node node : group.getNodes()) {
- int distKey = node.getDistributionKey();
- if (distKeys.contains(distKey)) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected nodes to be referenced only once in dispatch groups, but node with distribution key '" + distKey + "' is referenced multiple times.");
- }
- distKeys.add(distKey);
- }
- }
- }
-
- private void validateThatWeReferenceAllNodes() {
- Set<Integer> distKeys = createDistributionKeysSet();
- for (DispatchSpec.Group group : dispatchSpec.getGroups()) {
- for (DispatchSpec.Node node : group.getNodes()) {
- distKeys.remove(node.getDistributionKey());
- }
- }
- if (!distKeys.isEmpty()) {
- Object[] sorted = distKeys.toArray();
- Arrays.sort(sorted);
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected all nodes to be referenced in dispatch groups, but " + distKeys.size() +
- " node(s) with distribution keys " + Arrays.toString(sorted) + " are not referenced.");
- }
- }
-
- private void validateThatWeUseValidNodeReferences() {
- Set<Integer> distKeys = createDistributionKeysSet();
- for (DispatchSpec.Group group : dispatchSpec.getGroups()) {
- for (DispatchSpec.Node node : group.getNodes()) {
- int distKey = node.getDistributionKey();
- if (!distKeys.contains(distKey)) {
- throw new IllegalArgumentException(getErrorMsgPrefix() + "Expected all node references in dispatch groups to reference existing nodes, " +
- "but node with distribution key '" + distKey + "' does not exists.");
- }
- }
- }
- }
-
- private Set<Integer> createDistributionKeysSet() {
- Set<Integer> distKeys = new HashSet<>();
- for (SearchNode node : searchNodes) {
- distKeys.add(node.getDistributionKey());
- }
- return distKeys;
- }
-
- private String getErrorMsgPrefix() {
- return "In indexed content cluster '" + clusterName + "': ";
- }
-
-}
diff --git a/config-model/src/main/resources/schema/content.rnc b/config-model/src/main/resources/schema/content.rnc
index a73236454c6..4d8b44f3fc1 100644
--- a/config-model/src/main/resources/schema/content.rnc
+++ b/config-model/src/main/resources/schema/content.rnc
@@ -144,6 +144,7 @@ SearchCoverage = element coverage {
element max-wait-after-coverage-factor { xsd:double { minInclusive = "0" maxInclusive = "1" } }?
}
+# TODO: Deprecated, remove in Vespa 9
Dispatch = element dispatch {
element num-dispatch-groups { xsd:nonNegativeInteger }? &
DispatchGroup*
diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
index 19fe9e0038d..7273053db5c 100644
--- a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
@@ -1313,7 +1313,7 @@ public class ModelProvisioningTest {
" </documents>" +
" <nodes count='24'/>" +
" <engine><proton><searchable-copies>5</searchable-copies></proton></engine>" +
- " <dispatch><num-dispatch-groups>7</num-dispatch-groups></dispatch>" +
+ " <dispatch><num-dispatch-groups>7</num-dispatch-groups></dispatch>" + // TODO: Allowed, but ignored, remove in Vespa 9
" </content>" +
"</services>";
@@ -1327,7 +1327,6 @@ public class ModelProvisioningTest {
assertEquals(4, cluster.getRedundancy().effectiveInitialRedundancy());
assertEquals(4, cluster.getRedundancy().effectiveFinalRedundancy());
assertEquals(4, cluster.getRedundancy().effectiveReadyCopies());
- assertEquals(4, cluster.getSearch().getIndexed().getDispatchSpec().getGroups().size());
assertEquals(4, cluster.getSearch().getIndexed().getSearchableCopies());
assertFalse(cluster.getRootGroup().getPartitions().isPresent());
assertEquals(4, cluster.getRootGroup().getNodes().size());
@@ -1477,7 +1476,6 @@ public class ModelProvisioningTest {
assertEquals(1, cluster.getRedundancy().effectiveFinalRedundancy());
assertEquals(1, cluster.getRedundancy().effectiveReadyCopies());
- assertEquals(1, cluster.getSearch().getIndexed().getDispatchSpec().getGroups().size());
assertFalse(cluster.getRootGroup().getPartitions().isPresent());
assertEquals(1, cluster.getRootGroup().getNodes().size());
assertEquals(0, cluster.getRootGroup().getSubgroups().size());
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java
index 1498b9871d3..f184519e928 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Deployer.java
@@ -54,8 +54,8 @@ public interface Deployer {
*/
Optional<Deployment> deployFromLocalActive(ApplicationId application, Duration timeout, boolean bootstrap);
- /** Returns the time the current local active session was activated, or empty if there is no local active session */
- Optional<Instant> lastDeployTime(ApplicationId application);
+ /** Returns the time the active session was activated, or empty if there is no active session */
+ Optional<Instant> activationTime(ApplicationId application);
/** Whether the deployer is bootstrapping, some users of the deployer will want to hold off with deployments in that case. */
default boolean bootstrapping() { return false; }
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 0d3cea59a46..3502ece9cb7 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
@@ -443,7 +443,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
}
@Override
- public Optional<Instant> lastDeployTime(ApplicationId application) {
+ public Optional<Instant> activationTime(ApplicationId application) {
Tenant tenant = tenantRepository.getTenant(application.tenant());
if (tenant == null) return Optional.empty();
Optional<Instant> activatedTime = getActiveSession(tenant, application).map(Session::getActivatedTime);
@@ -743,7 +743,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
public HttpResponse getLogs(ApplicationId applicationId, Optional<DomainName> hostname, String apiParams) {
String logServerURI = getLogServerURI(applicationId, hostname) + apiParams;
- return logRetriever.getLogs(logServerURI, lastDeployTime(applicationId));
+ return logRetriever.getLogs(logServerURI, activationTime(applicationId));
}
// ---------------- Methods to do call against tester containers in hosted ------------------------------
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
index 81362018939..d0b34b6094d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
@@ -318,10 +318,6 @@ public class RoutingController {
new Record(Record.Type.CNAME, RecordName.from(endpoint.dnsName()), RecordData.fqdn(vipHostname)),
Priority.normal,
Optional.of(application.get().id()));
- controller.nameServiceForwarder().removeRecords(
- Record.Type.CNAME, RecordName.from(endpoint.legacyRegionalDnsName()),
- Priority.normal,
- Optional.of(application.get().id()));
}
Map<ClusterSpec.Id, EndpointList> applicationEndpointsByCluster = applicationEndpoints.groupingBy(Endpoint::cluster);
for (var kv : applicationEndpointsByCluster.entrySet()) {
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 1c19810c37e..f55bb0dab6f 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
@@ -40,7 +40,6 @@ public class Endpoint {
private final ClusterSpec.Id cluster;
private final Optional<InstanceName> instance;
private final URI url;
- private final URI legacyRegionalUrl;
private final List<Target> targets;
private final Scope scope;
private final boolean legacy;
@@ -48,7 +47,7 @@ public class Endpoint {
private boolean tokenEndpoint;
private Endpoint(TenantAndApplicationId application, Optional<InstanceName> instanceName, EndpointId id,
- ClusterSpec.Id cluster, URI url, URI legacyRegionalUrl, List<Target> targets, Scope scope, Port port, boolean legacy,
+ ClusterSpec.Id cluster, URI url, List<Target> targets, Scope scope, Port port, boolean legacy,
RoutingMethod routingMethod, boolean certificateName, boolean tokenEndpoint) {
Objects.requireNonNull(application, "application must be non-null");
Objects.requireNonNull(instanceName, "instanceName must be non-null");
@@ -62,7 +61,6 @@ public class Endpoint {
this.cluster = requireCluster(cluster, certificateName);
this.instance = requireInstance(instanceName, scope);
this.url = url;
- this.legacyRegionalUrl = legacyRegionalUrl;
this.targets = List.copyOf(requireTargets(targets, application, instanceName, scope, certificateName));
this.scope = requireScope(scope, routingMethod);
this.legacy = legacy;
@@ -102,12 +100,6 @@ public class Endpoint {
return url.getAuthority().replaceAll(":.*", "");
}
- /** Returns the legacy DNS name with region, for application endpoints */
- public String legacyRegionalDnsName() {
- if (scope != Scope.application) throw new IllegalStateException("legacy regional URL is only for application scope endpoints, not " + this);
- return legacyRegionalUrl.getAuthority().replaceAll(":.*", "");
- }
-
/** Returns the target(s) to which this routes traffic */
public List<Target> targets() {
return targets;
@@ -597,21 +589,11 @@ public class Endpoint {
Objects.requireNonNull(system, "system must be non-null"),
Objects.requireNonNull(port, "port must be non-null"),
false);
- URI legacyRegionalUrl = scope != Scope.application ? null
- : createUrl(endpointOrClusterAsString(endpointId, cluster),
- Objects.requireNonNull(application, "application must be non-null"),
- Objects.requireNonNull(instance, "instance must be non-null"),
- Objects.requireNonNull(targets, "targets must be non-null"),
- Objects.requireNonNull(scope, "scope must be non-null"),
- Objects.requireNonNull(system, "system must be non-null"),
- Objects.requireNonNull(port, "port must be non-null"),
- true);
return new Endpoint(application,
instance,
endpointId,
cluster,
url,
- legacyRegionalUrl,
targets,
scope,
port,
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java
index cd632917842..a20d5497945 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java
@@ -336,14 +336,10 @@ public class RoutingPolicies {
if (!aliasTargets.isEmpty()) {
nameServiceForwarderIn(targetZone).createAlias(
RecordName.from(applicationEndpoint.dnsName()), aliasTargets, Priority.normal, owner);
- nameServiceForwarderIn(targetZone).removeRecords(Type.ALIAS, RecordName.from(applicationEndpoint.legacyRegionalDnsName()),
- Priority.normal, owner);
}
if (!directTargets.isEmpty()) {
nameServiceForwarderIn(targetZone).createDirect(
RecordName.from(applicationEndpoint.dnsName()), directTargets, Priority.normal, owner);
- nameServiceForwarderIn(targetZone).removeRecords(Type.DIRECT, RecordName.from(applicationEndpoint.legacyRegionalDnsName()),
- Priority.normal, owner);
}
});
@@ -358,12 +354,6 @@ public class RoutingPolicies {
target.data(),
Priority.normal,
owner);
- // TODO(mpolden): Remove this and legacy name support in Endpoint after 2023-06-22
- nameServiceForwarderIn(targetZone).removeRecords(target.type(),
- RecordName.from(applicationEndpoint.legacyRegionalDnsName()),
- target.data(),
- Priority.normal,
- owner);
});
});
}
@@ -528,22 +518,12 @@ public class RoutingPolicies {
RecordData.fqdn(policy.canonicalName().get().value()),
Priority.normal,
ownerOf(allocation));
- forwarder.removeRecords(Record.Type.ALIAS,
- RecordName.from(endpoint.legacyRegionalDnsName()),
- RecordData.fqdn(policy.canonicalName().get().value()),
- Priority.normal,
- ownerOf(allocation));
} else {
forwarder.removeRecords(Record.Type.DIRECT,
RecordName.from(endpoint.dnsName()),
RecordData.from(policy.ipAddress().get()),
Priority.normal,
ownerOf(allocation));
- forwarder.removeRecords(Record.Type.DIRECT,
- RecordName.from(endpoint.legacyRegionalDnsName()),
- RecordData.from(policy.ipAddress().get()),
- Priority.normal,
- ownerOf(allocation));
}
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index ca31ceebc17..dc96aa6c62c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -277,41 +277,6 @@ public class EndpointTest {
}
@Test
- void application_endpoints_legacy_dns_names() {
- Map<String, Endpoint> tests = Map.of(
- "weighted.a1.t1.us-west-1.r.vespa-app.cloud",
- Endpoint.of(app1)
- .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
- Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
- .routingMethod(RoutingMethod.exclusive)
- .on(Port.tls())
- .in(SystemName.Public),
- "weighted.a1.t1.us-west-1.r.cd.vespa-app.cloud",
- Endpoint.of(app1)
- .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
- Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
- .routingMethod(RoutingMethod.exclusive)
- .on(Port.tls())
- .in(SystemName.PublicCd),
- "a2.t2.us-east-3-r.vespa.oath.cloud",
- Endpoint.of(app2)
- .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
- Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
- .routingMethod(RoutingMethod.exclusive)
- .on(Port.tls())
- .in(SystemName.main),
- "cd.a2.t2.us-east-3-r.cd.vespa.oath.cloud",
- Endpoint.of(app2)
- .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
- Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
- .routingMethod(RoutingMethod.exclusive)
- .on(Port.tls())
- .in(SystemName.cd)
- );
- tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.legacyRegionalDnsName()));
- }
-
- @Test
void application_endpoints() {
Map<String, Endpoint> tests = Map.of(
"https://weighted.a1.t1.a.vespa-app.cloud/",
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java
index d1b900b9969..f355e66b1e4 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java
@@ -238,7 +238,7 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
/** Returns the parent node of the given child node */
public Optional<Node> parentOf(Node child) {
- return child.parentHostname().flatMap(this::get).map(NodeFamily::node);
+ return child.parentHostname().flatMap(this::node);
}
/** Returns the nodes contained in the group identified by given index */
@@ -391,26 +391,19 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
}
private Map<String, NodeFamily> cache() {
- return nodeCache.updateAndGet((oldValue) -> {
- if (oldValue != null) {
- return oldValue;
- }
- Map<String, NodeFamily> newValue = new HashMap<>();
- for (var node : this) {
- NodeFamily family;
- if (node.parentHostname().isEmpty()) {
- family = new NodeFamily(node, new ArrayList<>());
- for (var child : this) {
- if (child.hasParent(node.hostname())) {
- family.children.add(child);
- }
- }
- } else {
- family = new NodeFamily(node, List.of());
- }
- newValue.put(node.hostname(), family);
- }
- return newValue;
+ return nodeCache.updateAndGet((cached) -> {
+ if (cached != null)
+ return cached;
+
+ Map<String, List<Node>> children = new HashMap<>();
+ for (Node node : this)
+ node.parentHostname().ifPresent(parent -> children.computeIfAbsent(parent, __ -> new ArrayList<>()).add(node));
+
+ Map<String, NodeFamily> families = new HashMap<>();
+ for (Node node : this)
+ families.put(node.hostname(), new NodeFamily(node, children.getOrDefault(node.hostname(), List.of())));
+
+ return families;
});
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
index a1eae42da38..91a10a1d08e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ApplicationMaintainer.java
@@ -80,16 +80,16 @@ public abstract class ApplicationMaintainer extends NodeRepositoryMaintainer {
if ( ! canDeployNow(application)) return; // redeployment is no longer needed
log.log(Level.INFO, () -> application + " will be redeployed" +
(reason == null || reason.isBlank() ? "" : " due to " + reason) +
- ", last deploy time " + getLastDeployTime(application));
+ ", last activated " + activationTime(application));
deployment.activate();
} finally {
pendingDeployments.remove(application);
}
}
- /** Returns the last time application was activated. Epoch is returned if the application has never been deployed. */
- protected final Instant getLastDeployTime(ApplicationId application) {
- return deployer.lastDeployTime(application).orElse(Instant.EPOCH);
+ /** Returns the last time application was activated. Epoch is returned if the application has never been activated. */
+ protected final Instant activationTime(ApplicationId application) {
+ return deployer.activationTime(application).orElse(Instant.EPOCH);
}
@Override
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ExpeditedChangeApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ExpeditedChangeApplicationMaintainer.java
index ffa125230a8..1b81a977019 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ExpeditedChangeApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ExpeditedChangeApplicationMaintainer.java
@@ -68,15 +68,15 @@ public class ExpeditedChangeApplicationMaintainer extends ApplicationMaintainer
/** Returns the reason for doing an expedited deploy. */
private Optional<String> hasNodesWithChanges(ApplicationId applicationId, NodeList nodes) {
- Optional<Instant> lastDeployTime = deployer().lastDeployTime(applicationId);
- if (lastDeployTime.isEmpty()) return Optional.empty();
+ Optional<Instant> activationTime = deployer().activationTime(applicationId);
+ if (activationTime.isEmpty()) return Optional.empty();
List<String> reasons = nodes.stream()
.flatMap(node -> node.history()
.events()
.stream()
.filter(event -> expediteChangeBy(event.agent()))
- .filter(event -> lastDeployTime.get().isBefore(event.at()))
+ .filter(event -> activationTime.get().isBefore(event.at()))
.map(event -> event.type() + (event.agent() == Agent.system ? "" : " by " + event.agent())))
.sorted()
.distinct()
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java
index fcc8e904a47..36db8bff8e9 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java
@@ -90,9 +90,9 @@ public abstract class NodeMover<MOVE> extends NodeRepositoryMaintainer {
private boolean deployedRecently(ApplicationId application) {
Instant now = nodeRepository().clock().instant();
- return deployer.lastDeployTime(application)
- .map(lastDeployTime -> lastDeployTime.isAfter(now.minus(waitTimeAfterPreviousDeployment)))
- // We only know last deploy time for applications that were deployed on this config server,
+ return deployer.activationTime(application)
+ .map(lastActivatedTime -> lastActivatedTime.isAfter(now.minus(waitTimeAfterPreviousDeployment)))
+ // We only know last activated time for applications that were deployed on this config server,
// the rest will be deployed on another config server
.orElse(true);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
index 10a828c887a..28679b504aa 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
@@ -39,7 +39,7 @@ public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
@Override
protected boolean canDeployNow(ApplicationId application) {
- return deployer().lastDeployTime(application)
+ return deployer().activationTime(application)
// Don't deploy if a regular deploy just happened
.map(lastDeployTime -> lastDeployTime.isBefore(nodeRepository().clock().instant().minus(minTimeBetweenRedeployments)))
// We only know last deploy time for applications that were deployed on this config server,
@@ -57,7 +57,7 @@ public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
.map(node -> node.allocation().get().owner())
.distinct()
.filter(this::canDeployNow)
- .collect(Collectors.toMap(Function.identity(), this::getLastDeployTime));
+ .collect(Collectors.toMap(Function.identity(), this::activationTime));
return deploymentTimes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
index 41a7dd70e9f..a2e0e59e329 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
@@ -207,8 +207,10 @@ class NodeAllocation {
// TODO: Write this in a way that is simple to read
// If either the parent is dedicated to a cluster type different from this cluster
return ! candidate.parent.flatMap(Node::exclusiveToClusterType).map(cluster.type()::equals).orElse(true) ||
- // or this cluster is requiring exclusivity, but the host is exclusive to a different owner
- (nodeRepository.exclusiveAllocation(cluster) && !candidate.parent.flatMap(Node::exclusiveToApplicationId).map(application::equals).orElse(false));
+ // or the parent is dedicated to a different application
+ ! candidate.parent.flatMap(Node::exclusiveToApplicationId).map(application::equals).orElse(true) ||
+ // or this cluster requires exclusivity, but the host is not exclusive
+ (nodeRepository.exclusiveAllocation(cluster) && candidate.parent.flatMap(Node::exclusiveToApplicationId).isEmpty());
}
// In zones with shared hosts we require that if either of the nodes on the host requires exclusivity,
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
index a1712fe4a0b..dcde521bfda 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockDeployer.java
@@ -120,7 +120,7 @@ public class MockDeployer implements Deployer {
}
@Override
- public Optional<Instant> lastDeployTime(ApplicationId application) {
+ public Optional<Instant> activationTime(ApplicationId application) {
return Optional.ofNullable(lastDeployTimes.get(application));
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
index 3c459871490..5650bc953f0 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
@@ -58,8 +58,8 @@ public class AutoscalingMaintainerTest {
new MockDeployer.ApplicationContext(app2, cluster2, Capacity.from(new ClusterResources(2, 1, highResources))));
tester.maintainer().maintain(); // noop
- assertTrue(tester.deployer().lastDeployTime(app1).isEmpty());
- assertTrue(tester.deployer().lastDeployTime(app2).isEmpty());
+ assertTrue(tester.deployer().activationTime(app1).isEmpty());
+ assertTrue(tester.deployer().activationTime(app2).isEmpty());
tester.deploy(app1, cluster1, Capacity.from(new ClusterResources(5, 1, new NodeResources(4, 4, 10, 0.1)),
new ClusterResources(5, 1, new NodeResources(4, 4, 10, 0.1)),
@@ -70,16 +70,16 @@ public class AutoscalingMaintainerTest {
tester.clock().advance(Duration.ofMinutes(10));
tester.maintainer().maintain(); // noop
- assertTrue(tester.deployer().lastDeployTime(app1).isEmpty());
- assertTrue(tester.deployer().lastDeployTime(app2).isEmpty());
+ assertTrue(tester.deployer().activationTime(app1).isEmpty());
+ assertTrue(tester.deployer().activationTime(app2).isEmpty());
tester.addMeasurements(0.9f, 0.9f, 0.9f, 0, 500, app1, cluster1.id());
tester.addMeasurements(0.9f, 0.9f, 0.9f, 0, 500, app2, cluster2.id());
tester.clock().advance(Duration.ofMinutes(10));
tester.maintainer().maintain();
- assertTrue(tester.deployer().lastDeployTime(app1).isEmpty()); // since autoscaling is off
- assertTrue(tester.deployer().lastDeployTime(app2).isPresent());
+ assertTrue(tester.deployer().activationTime(app1).isEmpty()); // since autoscaling is off
+ assertTrue(tester.deployer().activationTime(app2).isPresent());
Load peakAt90 = tester.nodeRepository().applications().require(app1).cluster(cluster1.id()).get().target().peak();
Load idealAt90 = tester.nodeRepository().applications().require(app1).cluster(cluster1.id()).get().target().ideal();
assertNotEquals(Load.zero(), peakAt90);
@@ -116,8 +116,8 @@ public class AutoscalingMaintainerTest {
tester.clock().advance(Duration.ofMinutes(10));
Instant firstMaintenanceTime = tester.clock().instant();
tester.maintainer().maintain();
- assertTrue(tester.deployer().lastDeployTime(app1).isPresent());
- assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().lastDeployTime(app1).get().toEpochMilli());
+ assertTrue(tester.deployer().activationTime(app1).isPresent());
+ assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().activationTime(app1).get().toEpochMilli());
List<ScalingEvent> events = tester.nodeRepository().applications().get(app1).get().cluster(cluster1.id()).get().scalingEvents();
assertEquals(2, events.size());
assertEquals(Optional.of(firstMaintenanceTime), events.get(0).completion());
@@ -129,25 +129,25 @@ public class AutoscalingMaintainerTest {
// Measure overload still, since change is not applied, but metrics are discarded
tester.addMeasurements(0.9f, 0.9f, 0.9f, 0, 500, app1, cluster1.id());
tester.maintainer().maintain();
- assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().lastDeployTime(app1).get().toEpochMilli());
+ assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().activationTime(app1).get().toEpochMilli());
// Measure underload, but no autoscaling since we still haven't measured we're on the new config generation
tester.addMeasurements(0.1f, 0.1f, 0.1f, 0, 500, app1, cluster1.id());
tester.maintainer().maintain();
- assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().lastDeployTime(app1).get().toEpochMilli());
+ assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().activationTime(app1).get().toEpochMilli());
// Add measurement of the expected generation, leading to rescaling
// - record scaling completion
tester.clock().advance(Duration.ofMinutes(5));
tester.addMeasurements(0.1f, 0.1f, 0.1f, 1, 1, app1, cluster1.id());
tester.maintainer().maintain();
- assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().lastDeployTime(app1).get().toEpochMilli());
+ assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().activationTime(app1).get().toEpochMilli());
// - measure underload
tester.clock().advance(Duration.ofDays(4)); // Exit cooling period
tester.addMeasurements(0.1f, 0.1f, 0.1f, 1, 500, app1, cluster1.id());
Instant lastMaintenanceTime = tester.clock().instant();
tester.maintainer().maintain();
- assertEquals(lastMaintenanceTime.toEpochMilli(), tester.deployer().lastDeployTime(app1).get().toEpochMilli());
+ assertEquals(lastMaintenanceTime.toEpochMilli(), tester.deployer().activationTime(app1).get().toEpochMilli());
events = tester.nodeRepository().applications().get(app1).get().cluster(cluster1.id()).get().scalingEvents();
assertEquals(2, events.get(2).generation());
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
index cad5a87acfd..900d77fcb26 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
@@ -143,21 +143,21 @@ public class PeriodicApplicationMaintainerTest {
assertEquals("No deployment expected", 4, fixture.deployer.redeployments);
Instant firstDeployTime = clock.instant();
- assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
- assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
+ assertEquals(firstDeployTime, fixture.deployer.activationTime(fixture.app1).get());
+ assertEquals(firstDeployTime, fixture.deployer.activationTime(fixture.app2).get());
clock.advance(Duration.ofMinutes(5));
fixture.runApplicationMaintainer();
// Too soon: Not redeployed:
assertEquals("No deployment expected", 4, fixture.deployer.redeployments);
- assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
- assertEquals(firstDeployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
+ assertEquals(firstDeployTime, fixture.deployer.activationTime(fixture.app1).get());
+ assertEquals(firstDeployTime, fixture.deployer.activationTime(fixture.app2).get());
clock.advance(Duration.ofMinutes(30));
fixture.runApplicationMaintainer();
// Redeployed:
assertEquals("No deployment expected", 6, fixture.deployer.redeployments);
- assertEquals(clock.instant(), fixture.deployer.lastDeployTime(fixture.app1).get());
- assertEquals(clock.instant(), fixture.deployer.lastDeployTime(fixture.app2).get());
+ assertEquals(clock.instant(), fixture.deployer.activationTime(fixture.app1).get());
+ assertEquals(clock.instant(), fixture.deployer.activationTime(fixture.app2).get());
}
@Test(timeout = 60_000)
@@ -188,8 +188,8 @@ public class PeriodicApplicationMaintainerTest {
fixture.deployer.lock().unlock();
fixture.runApplicationMaintainer();
Instant deployTime = clock.instant();
- assertEquals(deployTime, fixture.deployer.lastDeployTime(fixture.app1).get());
- assertEquals(deployTime, fixture.deployer.lastDeployTime(fixture.app2).get());
+ assertEquals(deployTime, fixture.deployer.activationTime(fixture.app1).get());
+ assertEquals(deployTime, fixture.deployer.activationTime(fixture.app2).get());
// Too soon: Already deployed recently
clock.advance(Duration.ofMinutes(5));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
index 7dbed807480..e1e83ad2fb3 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
@@ -214,7 +214,8 @@ public class DynamicProvisioningTest {
@Test
public void retires_on_exclusivity_violation() {
- var tester = tester(true);
+ var tester = tester(false);
+ tester.flagSource().withJacksonFlag(PermanentFlags.SHARED_HOST.id(), new SharedHost(List.of(new HostResources(1., 1., 1., 1., "fast", "local", null, 10, "x86_64"))), SharedHost.class);
ApplicationId application1 = ProvisioningTester.applicationId();
NodeResources resources = new NodeResources(4, 80, 100, 1);
prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources, tester);
@@ -229,8 +230,20 @@ public class DynamicProvisioningTest {
// Redeploy without exclusive again is no-op
prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, smallerExclusiveResources, tester);
- assertEquals(8, tester.nodeRepository().nodes().list().owner(application1).size());
- assertEquals(initialNodes, tester.nodeRepository().nodes().list().owner(application1).retired());
+ NodeList nodes = tester.nodeRepository().nodes().list();
+ assertEquals(8, nodes.owner(application1).size());
+ assertEquals(initialNodes, nodes.owner(application1).retired());
+
+ // Remove the old retired nodes and make 2 random parents of current nodes violate exclusivity
+ tester.patchNodes(initialNodes.asList(), node -> node.removable(true));
+ NodeList exclusiveViolators = nodes.owner(application1).not().retired().first(2);
+ List<Node> parents = exclusiveViolators.mapToList(node -> nodes.parentOf(node).get());
+ tester.patchNode(parents.get(0), node -> node.withExclusiveToApplicationId(ApplicationId.defaultId()));
+ tester.patchNode(parents.get(1), node -> node.withExclusiveToClusterType(ClusterSpec.Type.container));
+
+ prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, smallerExclusiveResources, tester);
+ assertEquals(10, tester.nodeRepository().nodes().list().owner(application1).size());
+ assertEquals(exclusiveViolators, tester.nodeRepository().nodes().list().owner(application1).retired());
}
@Test
diff --git a/searchcore/src/vespa/searchcore/grouping/groupingcontext.cpp b/searchcore/src/vespa/searchcore/grouping/groupingcontext.cpp
index 30a1f6074f1..f76fe873b03 100644
--- a/searchcore/src/vespa/searchcore/grouping/groupingcontext.cpp
+++ b/searchcore/src/vespa/searchcore/grouping/groupingcontext.cpp
@@ -3,6 +3,8 @@
#include "groupingcontext.h"
#include <vespa/searchlib/aggregation/predicates.h>
#include <vespa/searchlib/aggregation/modifiers.h>
+#include <vespa/searchlib/aggregation/hitsaggregationresult.h>
+#include <vespa/searchlib/common/bitvector.h>
namespace search::grouping {
@@ -17,12 +19,11 @@ GroupingContext::deserialize(const char *groupSpec, uint32_t groupSpecLen)
vespalib::NBOSerializer nis(is);
uint32_t numGroupings = 0;
nis >> numGroupings;
+ _groupingList.reserve(numGroupings);
for (size_t i = 0; i < numGroupings; i++) {
auto grouping = std::make_shared<search::aggregation::Grouping>();
grouping->deserialize(nis);
- grouping->setClock(&_clock);
- grouping->setTimeOfDoom(_timeOfDoom);
- _groupingList.push_back(grouping);
+ _groupingList.push_back(std::move(grouping));
}
}
}
@@ -31,7 +32,7 @@ size_t
GroupingContext::countFS4Hits()
{
size_t numFs4Hits(0);
- for (GroupingPtr & g : _groupingList) {
+ for (auto & g : _groupingList) {
CountFS4Hits counter;
g->select(counter, counter);
numFs4Hits += counter.getHitCount();
@@ -42,7 +43,7 @@ GroupingContext::countFS4Hits()
void
GroupingContext::setDistributionKey(uint32_t distributionKey)
{
- for (GroupingPtr & g : _groupingList) {
+ for (auto & g : _groupingList) {
FS4HitSetDistributionKey updater(distributionKey);
g->select(updater, updater);
}
@@ -65,22 +66,20 @@ GroupingContext::GroupingContext(const vespalib::Clock & clock, vespalib::steady
_os(),
_groupingList(),
_enableNestedMultivalueGrouping(true)
-{
-}
+{ }
-GroupingContext::GroupingContext(const GroupingContext & rhs) :
- _clock(rhs._clock),
- _timeOfDoom(rhs._timeOfDoom),
- _os(),
- _groupingList(),
- _enableNestedMultivalueGrouping(rhs._enableNestedMultivalueGrouping)
-{
-}
+GroupingContext::GroupingContext(const GroupingContext & rhs)
+ : _clock(rhs._clock),
+ _timeOfDoom(rhs._timeOfDoom),
+ _os(),
+ _groupingList(),
+ _enableNestedMultivalueGrouping(rhs._enableNestedMultivalueGrouping)
+{ }
void
-GroupingContext::addGrouping(const GroupingPtr & g)
+GroupingContext::addGrouping(std::shared_ptr<Grouping> g)
{
- _groupingList.push_back(g);
+ _groupingList.push_back(std::move(g));
}
void
@@ -89,9 +88,8 @@ GroupingContext::serialize()
vespalib::NBOSerializer nos(_os);
nos << (uint32_t)_groupingList.size();
- for (size_t i = 0; i < _groupingList.size(); i++) {
- search::aggregation::Grouping & grouping(*_groupingList[i]);
- grouping.serialize(nos);
+ for (const auto & grouping : _groupingList) {
+ grouping->serialize(nos);
}
}
@@ -104,4 +102,55 @@ GroupingContext::needRanking() const
return true;
}
+using DocId = uint32_t;
+
+unsigned int
+GroupingContext::aggregateRanked(Grouping &grouping, const RankedHit *rankedHit, unsigned int len) const {
+ unsigned int i(0);
+ for(; (i < len) && !hasExpired(); i++) {
+ grouping.aggregate(rankedHit[i].getDocId(), rankedHit[i].getRank());
+ }
+ return i;
+}
+
+void
+GroupingContext::aggregate(Grouping & grouping, const BitVector * bVec, unsigned int lidLimit) const {
+ for (DocId d(bVec->getFirstTrueBit()); (d < lidLimit) && !hasExpired(); d = bVec->getNextTrueBit(d+1)) {
+ grouping.aggregate(d, 0.0);
+ }
+}
+void
+GroupingContext::aggregate(Grouping & grouping, const BitVector * bVec, unsigned int lidLimit, unsigned int topN) const {
+ for(DocId d(bVec->getFirstTrueBit()), i(0); (d < lidLimit) && (i < topN) && !hasExpired(); d = bVec->getNextTrueBit(d+1), i++) {
+ grouping.aggregate(d, 0.0);
+ }
+}
+
+void
+GroupingContext::aggregate(Grouping & grouping, const RankedHit * rankedHit, unsigned int len, const BitVector * bVec) const
+{
+ grouping.preAggregate(false);
+ uint32_t count = aggregateRanked(grouping, rankedHit, grouping.getMaxN(len));
+ if (bVec != nullptr) {
+ int64_t topN = grouping.getTopN();
+ if (topN > count) {
+ aggregate(grouping, bVec, bVec->size(), topN - count);
+ } else {
+ aggregate(grouping, bVec, bVec->size());
+ }
+ }
+ grouping.postProcess();
+}
+
+void
+GroupingContext::aggregate(Grouping & grouping, const RankedHit * rankedHit, unsigned int len) const
+{
+ bool isOrdered(! grouping.needResort());
+ grouping.preAggregate(isOrdered);
+ search::aggregation::HitsAggregationResult::SetOrdered pred;
+ grouping.select(pred, pred);
+ aggregateRanked(grouping, rankedHit, grouping.getMaxN(len));
+ grouping.postProcess();
+}
+
}
diff --git a/searchcore/src/vespa/searchcore/grouping/groupingcontext.h b/searchcore/src/vespa/searchcore/grouping/groupingcontext.h
index af71cf30017..880ffe5f767 100644
--- a/searchcore/src/vespa/searchcore/grouping/groupingcontext.h
+++ b/searchcore/src/vespa/searchcore/grouping/groupingcontext.h
@@ -3,9 +3,8 @@
#include <vespa/searchlib/aggregation/grouping.h>
#include <vespa/vespalib/objects/nbostream.h>
-#include <vespa/vespalib/util/time.h>
+#include <vespa/vespalib/util/clock.h>
#include <vector>
-#include <memory>
namespace search::grouping {
@@ -18,16 +17,8 @@ class GroupingContext
{
public:
using UP = std::unique_ptr<GroupingContext>;
- using GroupingPtr = std::shared_ptr<search::aggregation::Grouping>;
- using GroupingList = std::vector<GroupingPtr>;
-
-private:
- const vespalib::Clock & _clock;
- vespalib::steady_time _timeOfDoom;
- vespalib::nbostream _os;
- GroupingList _groupingList;
- bool _enableNestedMultivalueGrouping;
-public:
+ using Grouping = search::aggregation::Grouping;
+ using GroupingList = std::vector<std::shared_ptr<Grouping>>;
/**
* Deserialize a grouping spec into this context.
@@ -63,7 +54,7 @@ public:
* Add another grouping to this context.
* @param g Pointer to the grouping object to become part of this context.
**/
- void addGrouping(const GroupingPtr & g);
+ void addGrouping(std::shared_ptr<Grouping> g);
/**
* Reset the context to an empty state.
@@ -74,7 +65,7 @@ public:
* Return the internal list of grouping expressions in this context.
* @return a list of groupings.
**/
- GroupingList &getGroupingList() { return _groupingList; }
+ GroupingList &getGroupingList() noexcept { return _groupingList; }
/**
* Serialize the grouping expressions in this context.
@@ -84,7 +75,7 @@ public:
/**
* Check whether this context contains any groupings.
**/
- bool empty() const { return _groupingList.empty(); }
+ bool empty() const noexcept { return _groupingList.empty(); }
/**
* Obtain the grouping result.
@@ -108,13 +99,26 @@ public:
/**
* Obtain the time of doom.
*/
- vespalib::steady_time getTimeOfDoom() const { return _timeOfDoom; }
+ vespalib::steady_time getTimeOfDoom() const noexcept { return _timeOfDoom; }
+ bool hasExpired() const noexcept { return _clock.getTimeNS() > _timeOfDoom; }
/**
* Figure out if ranking is necessary for any of the grouping requests here.
* @return true if ranking is required.
*/
bool needRanking() const;
- bool enableNestedMultivalueGrouping() const { return _enableNestedMultivalueGrouping; }
+ bool enableNestedMultivalueGrouping() const noexcept { return _enableNestedMultivalueGrouping; }
+
+ void aggregate(Grouping & grouping, const RankedHit * rankedHit, unsigned int len, const BitVector * bv) const;
+ void aggregate(Grouping & grouping, const RankedHit * rankedHit, unsigned int len) const;
+private:
+ unsigned int aggregateRanked(Grouping & grouping, const RankedHit * rankedHit, unsigned int len) const;
+ void aggregate(Grouping & grouping, const BitVector * bv, unsigned int lidLimit) const;
+ void aggregate(Grouping & grouping, const BitVector * bv, unsigned int , unsigned int topN) const;
+ const vespalib::Clock & _clock;
+ vespalib::steady_time _timeOfDoom;
+ vespalib::nbostream _os;
+ GroupingList _groupingList;
+ bool _enableNestedMultivalueGrouping;
};
}
diff --git a/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp b/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp
index e907f4a7c76..3eb410ad2c2 100644
--- a/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp
+++ b/searchcore/src/vespa/searchcore/grouping/groupingmanager.cpp
@@ -69,14 +69,12 @@ GroupingManager::init(const IAttributeContext &attrCtx)
void
GroupingManager::groupInRelevanceOrder(const RankedHit *searchResults, uint32_t binSize)
{
- GroupingContext::GroupingList &groupingList(_groupingContext.getGroupingList());
- for (size_t i = 0; i < groupingList.size(); ++i) {
- Grouping & g = *groupingList[i];
- if ( ! g.needResort() ) {
- g.aggregate(searchResults, binSize);
- LOG(debug, "groupInRelevanceOrder: %s", g.asString().c_str());
- g.cleanTemporary();
- g.cleanupAttributeReferences();
+ for (const auto & g : _groupingContext.getGroupingList()) {
+ if ( ! g->needResort() ) {
+ _groupingContext.aggregate(*g, searchResults, binSize);
+ LOG(debug, "groupInRelevanceOrder: %s", g->asString().c_str());
+ g->cleanTemporary();
+ g->cleanupAttributeReferences();
}
}
}
@@ -84,14 +82,12 @@ GroupingManager::groupInRelevanceOrder(const RankedHit *searchResults, uint32_t
void
GroupingManager::groupUnordered(const RankedHit *searchResults, uint32_t binSize, const search::BitVector * overflow)
{
- GroupingContext::GroupingList &groupingList(_groupingContext.getGroupingList());
- for (size_t i = 0; i < groupingList.size(); ++i) {
- Grouping & g = *groupingList[i];
- if ( g.needResort() ) {
- g.aggregate(searchResults, binSize, overflow);
- LOG(debug, "groupUnordered: %s", g.asString().c_str());
- g.cleanTemporary();
- g.cleanupAttributeReferences();
+ for (const auto & g : _groupingContext.getGroupingList()) {
+ if ( g->needResort() ) {
+ _groupingContext.aggregate(*g, searchResults, binSize, overflow);
+ LOG(debug, "groupUnordered: %s", g->asString().c_str());
+ g->cleanTemporary();
+ g->cleanupAttributeReferences();
}
}
}
diff --git a/searchcore/src/vespa/searchcore/grouping/groupingsession.cpp b/searchcore/src/vespa/searchcore/grouping/groupingsession.cpp
index fe476039ab8..fa63b0c97c6 100644
--- a/searchcore/src/vespa/searchcore/grouping/groupingsession.cpp
+++ b/searchcore/src/vespa/searchcore/grouping/groupingsession.cpp
@@ -44,9 +44,9 @@ GroupingSession::init(GroupingContext & groupingContext, const IAttributeContext
auto gp = std::make_shared<Grouping>(*g);
gp->setLastLevel(gp->levels().size());
_groupingMap[gp->getId()] = gp;
- g = gp;
+ g = std::move(gp);
}
- _mgrContext->addGrouping(g);
+ _mgrContext->addGrouping(std::move(g));
}
_groupingManager->init(attrCtx);
}
@@ -64,9 +64,8 @@ GroupingSession::createThreadContext(size_t thread_id, const IAttributeContext &
{
auto ctx = std::make_unique<GroupingContext>(*_mgrContext);
if (thread_id == 0) {
- GroupingContext::GroupingList &groupingList = _mgrContext->getGroupingList();
- for (size_t i = 0; i < groupingList.size(); ++i) {
- ctx->addGrouping(groupingList[i]);
+ for (const auto & grouping : _mgrContext->getGroupingList()) {
+ ctx->addGrouping(grouping);
}
} else {
ctx->deserialize(_mgrContext->getResult().peek(), _mgrContext->getResult().size());
diff --git a/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp b/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp
index 9b85ddbdde8..1608c633124 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/result_processor.cpp
@@ -119,7 +119,6 @@ ResultProcessor::Result::UP
ResultProcessor::makeReply(PartialResultUP full_result)
{
auto reply = std::make_unique<search::engine::SearchReply>();
- const search::IDocumentMetaStore &metaStore = _metaStore;
search::engine::SearchReply &r = *reply;
PartialResult &result = *full_result;
size_t numFs4Hits(0);
@@ -127,7 +126,7 @@ ResultProcessor::makeReply(PartialResultUP full_result)
if (_wasMerged) {
_groupingSession->getGroupingManager().prune();
}
- _groupingSession->getGroupingManager().convertToGlobalId(metaStore);
+ _groupingSession->getGroupingManager().convertToGlobalId(_metaStore);
_groupingSession->continueExecution(_groupingContext);
numFs4Hits = _groupingContext.countFS4Hits();
_groupingContext.getResult().swap(r.groupResult);
@@ -144,7 +143,7 @@ ResultProcessor::makeReply(PartialResultUP full_result)
search::engine::SearchReply::Hit &dst = r.hits[i];
const search::RankedHit &src = result.hit(hitOffset + i);
uint32_t docId = src.getDocId();
- if (metaStore.getGidEvenIfMoved(docId, gid)) {
+ if (_metaStore.getGidEvenIfMoved(docId, gid)) {
dst.gid = gid;
}
dst.metric = src.getRank();
diff --git a/searchcore/src/vespa/searchcore/proton/matching/result_processor.h b/searchcore/src/vespa/searchcore/proton/matching/result_processor.h
index 4a59609da5c..54b9adc4723 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/result_processor.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/result_processor.h
@@ -100,7 +100,6 @@ public:
size_t offset, size_t hits);
~ResultProcessor();
- size_t countFS4Hits();
void prepareThreadContextCreation(size_t num_threads);
Context::UP createThreadContext(const vespalib::Doom & hardDoom, size_t thread_id, uint32_t distributionKey);
std::vector<std::pair<uint32_t,uint32_t>> extract_docid_ordering(const PartialResult &result) const;
diff --git a/searchlib/CMakeLists.txt b/searchlib/CMakeLists.txt
index eb199a7ba44..e885f9bfba7 100644
--- a/searchlib/CMakeLists.txt
+++ b/searchlib/CMakeLists.txt
@@ -97,6 +97,7 @@ vespa_define_module(
src/tests/attribute/searchable
src/tests/attribute/searchcontext
src/tests/attribute/searchcontextelementiterator
+ src/tests/attribute/sort_blob_writers
src/tests/attribute/sourceselector
src/tests/attribute/stringattribute
src/tests/attribute/tensorattribute
diff --git a/searchlib/src/tests/attribute/sort_blob_writers/CMakeLists.txt b/searchlib/src/tests/attribute/sort_blob_writers/CMakeLists.txt
new file mode 100644
index 00000000000..5aad3c55cb7
--- /dev/null
+++ b/searchlib/src/tests/attribute/sort_blob_writers/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchlib_sort_blob_writers_test_app TEST
+ SOURCES
+ sort_blob_writers_test.cpp
+ DEPENDS
+ searchlib
+ GTest::GTest
+)
+vespa_add_test(NAME searchlib_sort_blob_writers_test_app COMMAND searchlib_sort_blob_writers_test_app)
diff --git a/searchlib/src/tests/attribute/sort_blob_writers/sort_blob_writers_test.cpp b/searchlib/src/tests/attribute/sort_blob_writers/sort_blob_writers_test.cpp
new file mode 100644
index 00000000000..79e781ddff4
--- /dev/null
+++ b/searchlib/src/tests/attribute/sort_blob_writers/sort_blob_writers_test.cpp
@@ -0,0 +1,344 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchlib/attribute/numeric_sort_blob_writer.h>
+#include <vespa/searchlib/attribute/string_sort_blob_writer.h>
+#include <vespa/searchlib/common/converters.h>
+#include <vespa/fastlib/text/normwordfolder.h>
+#include <vespa/vespalib/gtest/gtest.h>
+#include <vespa/vespalib/util/arrayref.h>
+#include <vespa/vespalib/util/sort.h>
+
+using search::attribute::NumericSortBlobWriter;
+using search::attribute::SortBlobWriter;
+using search::attribute::StringSortBlobWriter;
+using search::common::BlobConverter;
+using search::common::LowercaseConverter;
+using vespalib::ConstArrayRef;
+
+namespace {
+
+using SortData = std::vector<unsigned char>;
+
+SortData missing_value{1};
+
+template <typename T, bool asc>
+SortData
+serialized_present_numeric(T value)
+{
+ SortData s;
+ s.resize(1 + sizeof(T));
+ s[0] = SortBlobWriter::has_value;
+ auto ret = vespalib::serializeForSort<vespalib::convertForSort<T, asc>>(value, s.data() + 1, s.size() - 1);
+ assert(size_t(ret) == s.size() - 1);
+ return s;
+}
+
+SortData
+serialized_present_string(const char *value, bool asc)
+{
+ ConstArrayRef<unsigned char> src(reinterpret_cast<const unsigned char*>(value), strlen(value) + 1);
+ SortData s;
+ s.reserve(src.size() + 1);
+ s.emplace_back(SortBlobWriter::has_value);
+ unsigned char xor_value = asc ? 0 : 255;
+ for (auto c : src) {
+ s.emplace_back(c ^ xor_value);
+ }
+ return s;
+}
+
+template <typename T>
+SortData
+serialized_present(T value, bool asc)
+{
+ if constexpr (std::is_same_v<T, const char*>) {
+ return serialized_present_string(value, asc);
+ } else {
+ if (asc) {
+ return serialized_present_numeric<T, true>(value);
+ }
+ return serialized_present_numeric<T, false>(value);
+ }
+}
+
+template <typename T, bool asc>
+SortData
+sort_data_numeric(std::vector<T> values)
+{
+ size_t len = 0;
+ SortData s;
+ while (true) {
+ s.clear();
+ s.resize(len);
+ NumericSortBlobWriter<T, asc> writer;
+ for (auto& v : values) {
+ writer.candidate(v);
+ }
+ auto result = writer.write(s.data(), s.size());
+ if (result >= 0) {
+ s.resize(result);
+ return s;
+ }
+ ++len;
+ }
+}
+
+SortData
+sort_data_string(std::vector<const char*> values, const BlobConverter* bc, bool asc)
+{
+ size_t len = 0;
+ SortData s;
+ while (true) {
+ s.clear();
+ s.resize(len);
+ StringSortBlobWriter writer(s.data(), s.size(), bc, asc);
+ bool fail = false;
+ for (auto& v : values) {
+ if (!writer.candidate(v)) {
+ fail = true;
+ break;
+ }
+ }
+ if (!fail) {
+ auto result = writer.write();
+ if (result >= 0) {
+ s.resize(result);
+ return s;
+ }
+ }
+ ++len;
+ }
+}
+
+SortData
+sort_data_string(std::vector<const char*> values, bool asc)
+{
+ return sort_data_string(values, nullptr, asc);
+}
+
+template <typename T>
+SortData
+sort_data(std::vector<T> values, bool asc)
+{
+ if constexpr (std::is_same_v<T, const char*>) {
+ return sort_data_string(values, asc);
+ }
+ if (asc) {
+ return sort_data_numeric<T, true>(std::move(values));
+ }
+ return sort_data_numeric<T, false>(std::move(values));
+}
+
+SortData
+switch_sort_order(SortData value)
+{
+ assert(value.size() >= 1);
+ ConstArrayRef<unsigned char> src(value.data() + 1, value.size() - 1);
+ SortData s;
+ s.reserve(src.size() + 1);
+ s.emplace_back(value[0]);
+ for (auto c : src) {
+ s.emplace_back(c ^ 255);
+ }
+ return s;
+}
+
+struct SetupHook {
+ SetupHook();
+};
+
+SetupHook::SetupHook()
+{
+ Fast_NormalizeWordFolder::Setup(Fast_NormalizeWordFolder::DO_ACCENT_REMOVAL |
+ Fast_NormalizeWordFolder::DO_SHARP_S_SUBSTITUTION |
+ Fast_NormalizeWordFolder::DO_LIGATURE_SUBSTITUTION |
+ Fast_NormalizeWordFolder::DO_MULTICHAR_EXPANSION);
+}
+
+SetupHook setup_hook;
+
+}
+
+template <typename Param>
+class SortBlobWritersTest : public ::testing::Test
+{
+protected:
+ SortBlobWritersTest();
+ ~SortBlobWritersTest() override;
+};
+
+
+template <typename Param>
+SortBlobWritersTest<Param>::SortBlobWritersTest()
+ : ::testing::Test()
+{
+}
+
+template <typename Param>
+SortBlobWritersTest<Param>::~SortBlobWritersTest() = default;
+
+struct Int8Params {
+ using Type = int8_t;
+ static constexpr int8_t value = 42;
+ static SortData sort_asc;
+ static SortData sort_desc;
+ static std::vector<int8_t> values;
+ static constexpr int8_t min_value = -4;
+ static constexpr int8_t max_value = 9;
+};
+
+SortData Int8Params::sort_asc{0, 128 ^ 42};
+SortData Int8Params::sort_desc{0, 127 ^ 42};
+std::vector<int8_t> Int8Params::values{5, 7, -4, 9};
+
+struct Int16Params {
+ using Type = int16_t;
+ static constexpr int16_t value = 43;
+ static SortData sort_asc;
+ static SortData sort_desc;
+ static std::vector<int16_t> values;
+ static constexpr int16_t min_value = -4;
+ static constexpr int16_t max_value = 9;
+};
+
+SortData Int16Params::sort_asc{0, 128, 43};
+SortData Int16Params::sort_desc{0, 127, 255 ^ 43};
+std::vector<int16_t> Int16Params::values{5, 7, -4, 9};
+
+struct Int32Params {
+ using Type = int32_t;
+ static constexpr int32_t value = 44;
+ static SortData sort_asc;
+ static SortData sort_desc;
+ static std::vector<int32_t> values;
+ static constexpr int32_t min_value = -4;
+ static constexpr int32_t max_value = 9;
+};
+
+SortData Int32Params::sort_asc{0, 128, 0, 0, 44};
+SortData Int32Params::sort_desc{0, 127, 255, 255, 255 ^ 44};
+std::vector<int32_t> Int32Params::values{5, 7, -4, 9};
+
+struct Int64Params {
+ using Type = int64_t;
+ static constexpr int64_t value = 45;
+ static SortData sort_asc;
+ static SortData sort_desc;
+ static std::vector<int64_t> values;
+ static constexpr int64_t min_value = -4;
+ static constexpr int64_t max_value = 9;
+};
+
+SortData Int64Params::sort_asc{0, 128, 0, 0, 0, 0, 0, 0, 45};
+SortData Int64Params::sort_desc{0, 127, 255, 255, 255, 255, 255, 255, 255 ^ 45};
+std::vector<int64_t> Int64Params::values{5, 7, -4, 9};
+
+struct FloatParams {
+ using Type = float;
+ static constexpr float value = 46;
+ static std::vector<float> values;
+ static std::vector<float> values_with_nan;
+ static std::vector<float> values_only_nan;
+ static constexpr float min_value = -4;
+ static constexpr float max_value = 9;
+};
+
+std::vector<float> FloatParams::values{5, 7, -4, 9};
+std::vector<float> FloatParams::values_with_nan{5, 7, std::numeric_limits<float>::quiet_NaN(), -4, 9};
+std::vector<float> FloatParams::values_only_nan{std::numeric_limits<float>::quiet_NaN()};
+
+struct DoubleParams {
+ using Type = double;
+ static constexpr double value = 47;
+ static std::vector<double> values;
+ static std::vector<double> values_with_nan;
+ static std::vector<double> values_only_nan;
+ static constexpr double min_value = -4;
+ static constexpr double max_value = 9;
+};
+
+std::vector<double> DoubleParams::values{5, 7, -4, 9};
+std::vector<double> DoubleParams::values_with_nan{5, 7, std::numeric_limits<double>::quiet_NaN(), -4, 9};
+std::vector<double> DoubleParams::values_only_nan{std::numeric_limits<double>::quiet_NaN()};
+
+struct StringParams {
+ using Type = const char*;
+ static constexpr const char* value = "Hello";
+ static std::vector<const char*> values;
+ static constexpr const char* min_value = "always";
+ static constexpr const char* max_value = "this";
+};
+
+std::vector<const char*> StringParams::values{"this", "always", "happens"};
+
+using SortBlobWritersTestTypes = testing::Types<Int8Params, Int16Params, Int32Params, Int64Params, FloatParams, DoubleParams, StringParams>;
+
+TYPED_TEST_SUITE(SortBlobWritersTest, SortBlobWritersTestTypes);
+
+TYPED_TEST(SortBlobWritersTest, empty_arrays)
+{
+ using Type = typename TypeParam::Type;
+ EXPECT_EQ(missing_value, sort_data<Type>({}, true));
+ EXPECT_EQ(missing_value, sort_data<Type>({}, false));
+}
+
+TYPED_TEST(SortBlobWritersTest, single_values)
+{
+ using Type = typename TypeParam::Type;
+ auto& value = TypeParam::value;
+ EXPECT_EQ(serialized_present<Type>(value, true), sort_data<Type>({value}, true));
+ EXPECT_EQ(serialized_present<Type>(value, false), sort_data<Type>({value}, false));
+ if constexpr (std::is_integral_v<Type>) {
+ EXPECT_EQ(TypeParam::sort_asc, sort_data<Type>({value}, true));
+ EXPECT_EQ(TypeParam::sort_desc, sort_data<Type>({value}, false));
+ }
+ EXPECT_EQ(switch_sort_order(sort_data<Type>({value}, false)), sort_data<Type>({value}, true));
+ EXPECT_EQ(switch_sort_order(sort_data<Type>({value}, true)), sort_data<Type>({value}, false));
+ EXPECT_GT(missing_value, sort_data<Type>({value}, true));
+ EXPECT_GT(missing_value, sort_data<Type>({value}, false));
+}
+
+TYPED_TEST(SortBlobWritersTest, multiple_values)
+{
+ using Type = typename TypeParam::Type;
+ auto& values = TypeParam::values;
+ EXPECT_EQ(serialized_present<Type>(TypeParam::min_value, true), sort_data<Type>(values, true));
+ EXPECT_EQ(serialized_present<Type>(TypeParam::max_value, false), sort_data<Type>(values, false));
+}
+
+template <typename Param>
+using SortBlobFloatingPointWritersTest = SortBlobWritersTest<Param>;
+
+using SortBlobFloatingPointWritersTestTypes = testing::Types<FloatParams, DoubleParams>;
+
+TYPED_TEST_SUITE(SortBlobFloatingPointWritersTest, SortBlobFloatingPointWritersTestTypes);
+
+TYPED_TEST(SortBlobFloatingPointWritersTest, skip_nan_values)
+{
+ using Type = typename TypeParam::Type;
+ auto& values_only_nan = TypeParam::values_only_nan;
+ auto& values_with_nan = TypeParam::values_with_nan;
+ EXPECT_EQ(missing_value, sort_data<Type>(values_only_nan, true));
+ EXPECT_EQ(missing_value, sort_data<Type>(values_only_nan, false));
+ EXPECT_EQ(serialized_present<Type>(TypeParam::min_value, true), sort_data<Type>(values_with_nan, true));
+ EXPECT_EQ(serialized_present<Type>(TypeParam::max_value, false), sort_data<Type>(values_with_nan, false));
+}
+
+using SortBlobStringWriterTest = SortBlobWritersTest<const char*>;
+
+TEST_F(SortBlobStringWriterTest, blob_converter_is_used)
+{
+ LowercaseConverter lowercase;
+ EXPECT_EQ(serialized_present_string("hello", true), sort_data_string({"Hello"}, &lowercase, true));
+ EXPECT_EQ(serialized_present_string("hello", false), sort_data_string({"Hello"}, &lowercase, false));
+ EXPECT_EQ(serialized_present_string("always", true), sort_data_string({"Hello", "always"}, &lowercase, true));
+ EXPECT_EQ(serialized_present_string("hello", false), sort_data_string({"Hello", "always"}, &lowercase, false));
+}
+
+TEST_F(SortBlobStringWriterTest, prefix_is_first)
+{
+ EXPECT_EQ(serialized_present_string("aaa", true), sort_data_string({"aaa", "aaaa"}, true));
+ EXPECT_EQ(serialized_present_string("aaaa", false), sort_data_string({"aaa", "aaaa"}, false));
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/groupingengine/groupingengine_benchmark.cpp b/searchlib/src/tests/groupingengine/groupingengine_benchmark.cpp
index cd2f4a58552..711e15dd186 100644
--- a/searchlib/src/tests/groupingengine/groupingengine_benchmark.cpp
+++ b/searchlib/src/tests/groupingengine/groupingengine_benchmark.cpp
@@ -5,7 +5,6 @@
#include <vespa/searchlib/aggregation/aggregation.h>
#include <vespa/searchlib/attribute/extendableattributes.h>
#include <vespa/searchlib/attribute/attributemanager.h>
-#include <vespa/searchlib/aggregation/hitsaggregationresult.h>
#include <vespa/searchlib/aggregation/fs4hit.h>
#include <vespa/searchlib/expression/fixedwidthbucketfunctionnode.h>
#include <vespa/searchlib/grouping/groupingengine.h>
@@ -147,8 +146,8 @@ private:
CheckAttributeReferences() : _numrefs(0) { }
int _numrefs;
private:
- virtual void execute(vespalib::Identifiable &obj) override {
- if (static_cast<AttributeNode &>(obj).getAttribute() != NULL) {
+ void execute(vespalib::Identifiable &obj) override {
+ if (static_cast<AttributeNode &>(obj).getAttribute() != nullptr) {
_numrefs++;
}
}
@@ -261,13 +260,13 @@ Test::Main()
aggrType = _argv[3];
}
if (_argc > 4) {
- numDocs = strtol(_argv[4], NULL, 0);
+ numDocs = strtol(_argv[4], nullptr, 0);
}
if (_argc > 5) {
- numQueries = strtol(_argv[5], NULL, 0);
+ numQueries = strtol(_argv[5], nullptr, 0);
}
if (_argc > 6) {
- maxGroups = strtol(_argv[6], NULL, 0);
+ maxGroups = strtol(_argv[6], nullptr, 0);
}
TEST_INIT("grouping_benchmark");
LOG(info, "sizeof(Group) = %ld", sizeof(Group));
diff --git a/searchlib/src/tests/sort/CMakeLists.txt b/searchlib/src/tests/sort/CMakeLists.txt
index def7c0681c3..0159520fe12 100644
--- a/searchlib/src/tests/sort/CMakeLists.txt
+++ b/searchlib/src/tests/sort/CMakeLists.txt
@@ -15,7 +15,7 @@ vespa_add_executable(searchlib_sort_test_app
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND VESPA_USE_LTO)
target_link_options(searchlib_sort_test_app PRIVATE "-Wno-aggressive-loop-optimizations")
endif()
-#vespa_add_test(NAME searchlib_sort_test_app COMMAND searchlib_sort_test_app)
+vespa_add_test(NAME searchlib_sort_test_app COMMAND searchlib_sort_test_app)
vespa_add_executable(searchlib_uca_stress_app
SOURCES
uca.cpp
diff --git a/searchlib/src/tests/sort/sort_test.cpp b/searchlib/src/tests/sort/sort_test.cpp
index 9c48498eae9..614253b1393 100644
--- a/searchlib/src/tests/sort/sort_test.cpp
+++ b/searchlib/src/tests/sort/sort_test.cpp
@@ -18,23 +18,9 @@ using namespace search::common;
using namespace search::uca;
using vespalib::ConstBufferRef;
-class Test : public vespalib::TestApp
-{
-public:
- int Main() override;
- void testUnsignedIntegerSort();
- template <typename T>
- void testSignedIntegerSort();
- void testStringSort();
- void testIcu();
- void testStringCaseInsensitiveSort();
- void testSortSpec();
- void testSameAsJavaOrder();
-};
-
struct LoadedStrings
{
- LoadedStrings(const char * v=NULL) : _value(v), _currRadix(_value) { }
+ LoadedStrings(const char * v=nullptr) : _value(v), _currRadix(_value) { }
class ValueRadix
{
@@ -58,7 +44,7 @@ struct LoadedStrings
const char * _currRadix;
};
-void Test::testIcu()
+TEST("testIcu")
{
{
const std::string src("Creation of Bob2007 this is atumated string\this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string;this is atumated string; _ 12345567890-=,./;'[;");
@@ -70,10 +56,10 @@ void Test::testIcu()
}
}
-void Test::testUnsignedIntegerSort()
+TEST("testUnsignedIntegerSort")
{
search::NumericRadixSorter<uint32_t, true> S;
- S(NULL, 0);
+ S(nullptr, 0);
Array<uint32_t> array1(1);
array1[0] = 1567;
@@ -99,10 +85,10 @@ public:
};
template <typename T>
-void Test::testSignedIntegerSort()
+void testSignedIntegerSort()
{
search::NumericRadixSorter<T, true> S;
- S(NULL, 0);
+ S(nullptr, 0);
Array<T> array1(1);
array1[0] = 1567;
@@ -122,14 +108,19 @@ void Test::testSignedIntegerSort()
}
}
-void Test::testStringSort()
+TEST("testSignedIntegerSort") {
+ testSignedIntegerSort<int32_t>();
+ testSignedIntegerSort<int64_t>();
+}
+
+TEST("testStringSort")
{
Array<LoadedStrings> array1(1);
unsigned int N(0x1000);
Array<LoadedStrings> loaded(N);
std::vector<uint32_t> radixScratchPad(N);
- search::radix_sort(LoadedStrings::ValueRadix(), LoadedStrings::ValueCompare(), search::AlwaysEof<LoadedStrings>(), 1, static_cast<LoadedStrings *>(NULL), 0, &radixScratchPad[0], 0);
+ search::radix_sort(LoadedStrings::ValueRadix(), LoadedStrings::ValueCompare(), search::AlwaysEof<LoadedStrings>(), 1, static_cast<LoadedStrings *>(nullptr), 0, &radixScratchPad[0], 0);
array1[0] = LoadedStrings("a");
search::radix_sort(LoadedStrings::ValueRadix(), LoadedStrings::ValueCompare(), search::AlwaysEof<LoadedStrings>(), 1, &array1[0], 1, &radixScratchPad[0], 0);
@@ -143,97 +134,97 @@ void Test::testStringSort()
search::radix_sort(LoadedStrings::ValueRadix(), LoadedStrings::ValueCompare(), search::AlwaysEof<LoadedStrings>(), 1, &loaded[0], N, &radixScratchPad[0], 0);
LoadedStrings::ValueCompare vc;
for(size_t i(1); i < N; i++) {
- ASSERT_TRUE( ! vc(loaded[i], loaded[i-1]));
+ ASSERT_FALSE(vc(loaded[i], loaded[i-1]));
}
}
-void Test::testStringCaseInsensitiveSort()
+TEST("testStringCaseInsensitiveSort")
{
}
-void Test::testSortSpec()
+TEST("testSortSpec")
{
UcaConverterFactory ucaFactory;
{
SortSpec sortspec("-name", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() == NULL);
+ EXPECT_FALSE( sortspec[0]._ascending);
+ EXPECT_FALSE(sortspec[0]._converter);
}
{
SortSpec sortspec("-lowercase(name)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<LowercaseConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<LowercaseConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,nn_no)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,nn_no,PRIMARY)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,nn_no,SECONDARY)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,nn_no,TERTIARY)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,nn_no,QUATERNARY)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,nn_no,IDENTICAL)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,zh)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
SortSpec sortspec("-uca(name,finnes_ikke)", ucaFactory);
EXPECT_EQUAL(sortspec.size(), 1u);
EXPECT_EQUAL(sortspec[0]._field, "name");
- EXPECT_TRUE( ! sortspec[0]._ascending);
- EXPECT_TRUE(sortspec[0]._converter.get() != NULL);
- EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != NULL);
+ EXPECT_FALSE(sortspec[0]._ascending);
+ EXPECT_TRUE(sortspec[0]._converter);
+ EXPECT_TRUE(dynamic_cast<UcaConverter *>(sortspec[0]._converter.get()) != nullptr);
}
{
try {
@@ -246,7 +237,7 @@ void Test::testSortSpec()
}
}
-void Test::testSameAsJavaOrder()
+TEST("testSameAsJavaOrder")
{
std::vector<vespalib::string> javaOrder;
std::ifstream is("javaorder.zh");
@@ -261,6 +252,7 @@ void Test::testSameAsJavaOrder()
UcaConverter uca("zh", "PRIMARY");
vespalib::ConstBufferRef fkey = uca.convert(vespalib::ConstBufferRef(javaOrder[0].c_str(), javaOrder[0].size()));
vespalib::string prev(fkey.c_str(), fkey.size());
+ return; // TODO: temporarily ignored as java and c++ are no longer in sync
for (size_t i(1); i < javaOrder.size(); i++) {
vespalib::ConstBufferRef key = uca.convert(vespalib::ConstBufferRef(javaOrder[i].c_str(), javaOrder[i].size()));
vespalib::HexDump dump(key.c_str(), key.size());
@@ -275,21 +267,4 @@ void Test::testSameAsJavaOrder()
}
}
-
-TEST_APPHOOK(Test);
-
-int Test::Main()
-{
- TEST_INIT("sort_test");
-
- testUnsignedIntegerSort();
- testSignedIntegerSort<int32_t>();
- testSignedIntegerSort<int64_t>();
- testStringSort();
- testStringCaseInsensitiveSort();
- testSortSpec();
- testIcu();
- testSameAsJavaOrder();
-
- TEST_DONE();
-}
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/vespa/searchlib/aggregation/group.cpp b/searchlib/src/vespa/searchlib/aggregation/group.cpp
index 5d6e62f30f0..5a16756f0d7 100644
--- a/searchlib/src/vespa/searchlib/aggregation/group.cpp
+++ b/searchlib/src/vespa/searchlib/aggregation/group.cpp
@@ -1,16 +1,12 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "group.h"
-#include "maxaggregationresult.h"
-#include "groupinglevel.h"
#include "grouping.h"
+#include <vespa/searchlib/expression/aggregationrefnode.h>
-#include <vespa/vespalib/objects/objectdumper.h>
#include <vespa/vespalib/objects/visit.hpp>
#include <vespa/vespalib/stllike/hash_set.hpp>
-#include <cmath>
#include <cassert>
-#include <algorithm>
namespace search::aggregation {
@@ -766,10 +762,22 @@ Group::Value::partialCopy(const Value & rhs) {
memcpy(_orderBy, rhs._orderBy, sizeof(_orderBy));
}
+namespace {
+ class AggregationRefNodeConfigure : public vespalib::ObjectOperation, public vespalib::ObjectPredicate
+ {
+ public:
+ AggregationRefNodeConfigure(expression::ExpressionNode::CP * & exprVec) : _exprVec(exprVec) { }
+ private:
+ void execute(vespalib::Identifiable &obj) override { static_cast<AggregationRefNode&>(obj).locateExpression(_exprVec); }
+ bool check(const vespalib::Identifiable &obj) const override { return obj.inherits(AggregationRefNode::classId); }
+ expression::ExpressionNode::CP * & _exprVec;
+ };
+}
+
void
Group::Value::setupAggregationReferences()
{
- AggregationRefNode::Configure exprRefSetup(_aggregationResults);
+ AggregationRefNodeConfigure exprRefSetup(_aggregationResults);
select(exprRefSetup, exprRefSetup);
}
diff --git a/searchlib/src/vespa/searchlib/aggregation/grouping.cpp b/searchlib/src/vespa/searchlib/aggregation/grouping.cpp
index ea35310c727..e9df10d3a61 100644
--- a/searchlib/src/vespa/searchlib/aggregation/grouping.cpp
+++ b/searchlib/src/vespa/searchlib/aggregation/grouping.cpp
@@ -12,7 +12,6 @@
#include <vespa/vespalib/objects/serializer.hpp>
#include <vespa/vespalib/objects/deserializer.hpp>
#include <vespa/searchlib/common/idocumentmetastore.h>
-#include <vespa/searchlib/common/bitvector.h>
#include <vespa/log/log.h>
LOG_SETUP(".searchlib.aggregation.grouping");
@@ -129,11 +128,8 @@ Grouping::Grouping() noexcept
_firstLevel(0),
_lastLevel(0),
_levels(),
- _root(),
- _clock(nullptr),
- _timeOfDoom(vespalib::duration::zero())
-{
-}
+ _root()
+{ }
Grouping::Grouping(const Grouping &) = default;
Grouping & Grouping::operator = (const Grouping &) = default;
@@ -221,66 +217,14 @@ Grouping::postProcess()
}
void
-Grouping::aggregateWithoutClock(const RankedHit * rankedHit, unsigned int len) {
- for(unsigned int i(0); i < len; i++) {
- aggregate(rankedHit[i].getDocId(), rankedHit[i].getRank());
- }
-}
-
-void
-Grouping::aggregateWithClock(const RankedHit * rankedHit, unsigned int len) {
- for(unsigned int i(0); (i < len) && !hasExpired(); i++) {
- aggregate(rankedHit[i].getDocId(), rankedHit[i].getRank());
- }
-}
-
-void
Grouping::aggregate(const RankedHit * rankedHit, unsigned int len)
{
bool isOrdered(! needResort());
preAggregate(isOrdered);
HitsAggregationResult::SetOrdered pred;
select(pred, pred);
- if (_clock == nullptr) {
- aggregateWithoutClock(rankedHit, getMaxN(len));
- } else {
- aggregateWithClock(rankedHit, getMaxN(len));
- }
- postProcess();
-}
-
-void
-Grouping::aggregate(const RankedHit * rankedHit, unsigned int len, const BitVector * bVec)
-{
- preAggregate(false);
- if (_clock == nullptr) {
- aggregateWithoutClock(rankedHit, getMaxN(len));
- } else {
- aggregateWithClock(rankedHit, getMaxN(len));
- }
- if (bVec != nullptr) {
- unsigned int sz(bVec->size());
- if (_clock == nullptr) {
- if (getTopN() > 0) {
- for(DocId d(bVec->getFirstTrueBit()), i(0), m(getMaxN(sz)); (d < sz) && (i < m); d = bVec->getNextTrueBit(d+1), i++) {
- aggregate(d, 0.0);
- }
- } else {
- for(DocId d(bVec->getFirstTrueBit()); d < sz; d = bVec->getNextTrueBit(d+1)) {
- aggregate(d, 0.0);
- }
- }
- } else {
- if (getTopN() > 0) {
- for(DocId d(bVec->getFirstTrueBit()), i(0), m(getMaxN(sz)); (d < sz) && (i < m) && !hasExpired(); d = bVec->getNextTrueBit(d+1), i++) {
- aggregate(d, 0.0);
- }
- } else {
- for(DocId d(bVec->getFirstTrueBit()); (d < sz) && !hasExpired(); d = bVec->getNextTrueBit(d+1)) {
- aggregate(d, 0.0);
- }
- }
- }
+ for(unsigned int i(0), m(getMaxN(len)); i < m; i++) {
+ aggregate(rankedHit[i].getDocId(), rankedHit[i].getRank());
}
postProcess();
}
diff --git a/searchlib/src/vespa/searchlib/aggregation/grouping.h b/searchlib/src/vespa/searchlib/aggregation/grouping.h
index 2136075443e..49a56143607 100644
--- a/searchlib/src/vespa/searchlib/aggregation/grouping.h
+++ b/searchlib/src/vespa/searchlib/aggregation/grouping.h
@@ -3,7 +3,6 @@
#include "groupinglevel.h"
#include <vespa/searchlib/common/rankedhit.h>
-#include <vespa/vespalib/util/clock.h>
namespace search {
class BitVector;
@@ -30,13 +29,6 @@ private:
uint32_t _lastLevel; // last processing level this iteration
GroupingLevelList _levels; // grouping parameters per level
Group _root; // the grouping tree
- const vespalib::Clock *_clock; // An optional clock to be used for timeout handling.
- vespalib::steady_time _timeOfDoom; // Used if clock is specified. This is time when request expires.
-
- bool hasExpired() const { return _clock->getTimeNS() > _timeOfDoom; }
- void aggregateWithoutClock(const RankedHit * rankedHit, unsigned int len);
- void aggregateWithClock(const RankedHit * rankedHit, unsigned int len);
- void postProcess();
public:
DECLARE_IDENTIFIABLE_NS2(search, aggregation, Grouping);
DECLARE_NBO_SERIALIZE;
@@ -58,8 +50,6 @@ public:
Grouping &setLastLevel(unsigned int level) { _lastLevel = level; return *this; }
Grouping &addLevel(GroupingLevel && level) { _levels.push_back(std::move(level)); return *this; }
Grouping &setRoot(const Group &root_) { _root = root_; return *this; }
- Grouping &setClock(const vespalib::Clock * clock) { _clock = clock; return *this; }
- Grouping &setTimeOfDoom(vespalib::steady_time timeOfDoom) { _timeOfDoom = timeOfDoom; return *this; }
unsigned int getId() const noexcept { return _id; }
bool valid() const noexcept { return _valid; }
@@ -84,12 +74,12 @@ public:
void preAggregate(bool isOrdered);
void prune(const Grouping & b);
void aggregate(DocId from, DocId to);
- void aggregate(const RankedHit * rankedHit, unsigned int len);
- void aggregate(const RankedHit * rankedHit, unsigned int len, const BitVector * bVec);
void aggregate(DocId docId, HitRank rank = 0);
void aggregate(const document::Document & doc, HitRank rank = 0);
+ void aggregate(const RankedHit * rankedHit, unsigned int len);
void convertToGlobalId(const IDocumentMetaStore &metaStore);
void postAggregate();
+ void postProcess();
void sortById();
void cleanTemporary();
void configureStaticStuff(const expression::ConfigureStaticParams & params);
diff --git a/searchlib/src/vespa/searchlib/aggregation/groupinglevel.h b/searchlib/src/vespa/searchlib/aggregation/groupinglevel.h
index 2cffe498e96..dc8f021e343 100644
--- a/searchlib/src/vespa/searchlib/aggregation/groupinglevel.h
+++ b/searchlib/src/vespa/searchlib/aggregation/groupinglevel.h
@@ -2,7 +2,6 @@
#pragma once
#include "group.h"
-#include <vespa/searchlib/expression/aggregationrefnode.h>
#include <vespa/searchlib/expression/currentindex.h>
namespace search::expression { class CurrentIndexSetup; }
diff --git a/searchlib/src/vespa/searchlib/attribute/CMakeLists.txt b/searchlib/src/vespa/searchlib/attribute/CMakeLists.txt
index 896226f005d..c54169ffd11 100644
--- a/searchlib/src/vespa/searchlib/attribute/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/attribute/CMakeLists.txt
@@ -100,6 +100,7 @@ vespa_add_library(searchlib_attribute OBJECT
numeric_matcher.cpp
numeric_range_matcher.cpp
numeric_search_context.cpp
+ numeric_sort_blob_writer.cpp
posting_list_merger.cpp
postingchange.cpp
postinglistattribute.cpp
@@ -143,6 +144,7 @@ vespa_add_library(searchlib_attribute OBJECT
string_matcher.cpp
string_search_context.cpp
string_search_helper.cpp
+ string_sort_blob_writer.cpp
valuemodifier.cpp
DEPENDS
)
diff --git a/searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.cpp b/searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.cpp
new file mode 100644
index 00000000000..0c2aacc4ef1
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.cpp
@@ -0,0 +1,77 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "numeric_sort_blob_writer.h"
+#include <vespa/vespalib/util/sort.h>
+#include <cmath>
+#include <type_traits>
+
+namespace search::attribute {
+
+template <typename T, bool asc>
+NumericSortBlobWriter<T, asc>::NumericSortBlobWriter() noexcept
+: _best()
+{
+}
+
+template <typename T, bool asc>
+NumericSortBlobWriter<T, asc>::~NumericSortBlobWriter() noexcept = default;
+
+template <typename T, bool asc>
+void
+NumericSortBlobWriter<T, asc>::candidate(T val)
+{
+ if constexpr (std::disjunction_v<std::is_same<T,float>,std::is_same<T, double>>) {
+ if (std::isnan(val)) {
+ return;
+ }
+ }
+ if (_best.has_value()) {
+ if constexpr (asc) {
+ if (_best.value() <= val) {
+ return;
+ }
+ } else {
+ if (_best.value() >= val) {
+ return;
+ }
+ }
+ }
+ _best = val;
+}
+
+template <typename T, bool asc>
+long
+NumericSortBlobWriter<T, asc>::write(void *serTo, size_t available)
+{
+ auto dst = static_cast<unsigned char*>(serTo);
+ if (_best.has_value()) {
+ if (available < 1 + sizeof(T)) {
+ return -1;
+ }
+ *dst = has_value;
+ auto ret = vespalib::serializeForSort<vespalib::convertForSort<T, asc>>(_best.value(), dst + 1, available - 1);
+ return (ret >= 0) ? (ret + 1) : -1;
+ } else {
+ if (available < 1) {
+ return -1;
+ }
+ *dst = missing_value;
+ return 1;
+ }
+}
+
+template class NumericSortBlobWriter<int8_t, true>;
+template class NumericSortBlobWriter<int16_t, true>;
+template class NumericSortBlobWriter<int32_t, true>;
+template class NumericSortBlobWriter<int64_t, true>;
+template class NumericSortBlobWriter<float, true>;
+template class NumericSortBlobWriter<double, true>;
+
+template class NumericSortBlobWriter<int8_t, false>;
+template class NumericSortBlobWriter<int16_t, false>;
+template class NumericSortBlobWriter<int32_t, false>;
+template class NumericSortBlobWriter<int64_t, false>;
+template class NumericSortBlobWriter<float, false>;
+template class NumericSortBlobWriter<double, false>;
+
+}
diff --git a/searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.h b/searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.h
new file mode 100644
index 00000000000..b2b6d578628
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/attribute/numeric_sort_blob_writer.h
@@ -0,0 +1,25 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "sort_blob_writer.h"
+#include <optional>
+
+namespace search::attribute {
+
+/*
+ * Class for writing numeric sort blobs for arrays and
+ * weighted sets of type T with ascending or descending
+ * sort order.
+ */
+template <typename T, bool asc>
+class NumericSortBlobWriter : public SortBlobWriter {
+ std::optional<T> _best;
+public:
+ NumericSortBlobWriter() noexcept;
+ ~NumericSortBlobWriter() noexcept;
+ void candidate(T val);
+ long write(void *serTo, size_t available);
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/attribute/sort_blob_writer.h b/searchlib/src/vespa/searchlib/attribute/sort_blob_writer.h
new file mode 100644
index 00000000000..31aca300349
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/attribute/sort_blob_writer.h
@@ -0,0 +1,21 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace search::attribute {
+
+/*
+ * Base class for sort blob writers. Contains definition of tags
+ * describing if value is present (any present value sorts before
+ * a missing value).
+ */
+class SortBlobWriter {
+public:
+ /*
+ * Missing value is always sorted last.
+ */
+ static constexpr unsigned char has_value = 0;
+ static constexpr unsigned char missing_value = 1;
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.cpp b/searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.cpp
new file mode 100644
index 00000000000..01838b074a1
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.cpp
@@ -0,0 +1,72 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "string_sort_blob_writer.h"
+#include <vespa/searchcommon/common/iblobconverter.h>
+#include <vespa/vespalib/util/arrayref.h>
+#include <algorithm>
+#include <cstring>
+
+namespace search::attribute {
+
+StringSortBlobWriter::StringSortBlobWriter(void* serialize_to, size_t available, const BlobConverter* bc, bool asc) noexcept
+ : _best_size(),
+ _serialize_to(static_cast<unsigned char*>(serialize_to)),
+ _available(available),
+ _bc(bc),
+ _asc(asc)
+{
+}
+
+StringSortBlobWriter::~StringSortBlobWriter() noexcept = default;
+
+bool
+StringSortBlobWriter::candidate(const char* val)
+{
+ size_t size = std::strlen(val) + 1;
+ vespalib::ConstBufferRef buf(val, size);
+ if (_bc != nullptr) {
+ buf = _bc->convert(buf);
+ }
+ if (_best_size.has_value()) {
+ auto common_size = std::min(_best_size.value(), buf.size());
+ auto cmpres = std::memcmp(_serialize_to + 1, buf.data(), common_size);
+ if (_asc) {
+ if (cmpres < 0 || (cmpres == 0 && _best_size.value() < buf.size())) {
+ return true;
+ }
+ } else {
+ if (cmpres > 0 || (cmpres == 0 && _best_size.value() > buf.size())) {
+ return true;
+ }
+ }
+ }
+ if (_available < buf.size() + 1) {
+ return false;
+ }
+ _serialize_to[0] = has_value;
+ memcpy(_serialize_to + 1, buf.data(), buf.size());
+ _best_size = buf.size();
+ return true;
+}
+
+long
+StringSortBlobWriter::write()
+{
+ if (_best_size.has_value()) {
+ if (!_asc) {
+ vespalib::ArrayRef<unsigned char> buf(_serialize_to + 1, _best_size.value());
+ for (auto& c : buf) {
+ c = 0xff - c;
+ }
+ }
+ return 1 + _best_size.value();
+ } else {
+ if (_available < 1) {
+ return -1;
+ }
+ _serialize_to[0] = missing_value;
+ return 1;
+ }
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.h b/searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.h
new file mode 100644
index 00000000000..12b1d871875
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/attribute/string_sort_blob_writer.h
@@ -0,0 +1,31 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "sort_blob_writer.h"
+#include <optional>
+
+namespace search::common { class BlobConverter; }
+
+namespace search::attribute {
+
+/*
+ * Class for writing numeric sort blobs for arrays and
+ * weighted sets of string with ascending or descending
+ * sort order.
+ */
+class StringSortBlobWriter : public SortBlobWriter {
+ using BlobConverter = common::BlobConverter;
+ std::optional<size_t> _best_size;
+ unsigned char* _serialize_to;
+ size_t _available;
+ const BlobConverter* _bc;
+ const bool _asc;
+public:
+ StringSortBlobWriter(void* serialize_to, size_t available, const BlobConverter* bc, bool asc) noexcept;
+ ~StringSortBlobWriter() noexcept;
+ bool candidate(const char* val);
+ long write();
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/expression/aggregationrefnode.h b/searchlib/src/vespa/searchlib/expression/aggregationrefnode.h
index cae438c8351..f321014dcc7 100644
--- a/searchlib/src/vespa/searchlib/expression/aggregationrefnode.h
+++ b/searchlib/src/vespa/searchlib/expression/aggregationrefnode.h
@@ -2,31 +2,18 @@
#pragma once
#include "expressionnode.h"
-#include "serializer.h"
-#include <vespa/vespalib/objects/objectoperation.h>
-#include <vespa/vespalib/objects/objectpredicate.h>
-namespace search {
-namespace expression {
+namespace search::expression {
class AggregationRefNode : public ExpressionNode
{
public:
DECLARE_NBO_SERIALIZE;
- class Configure : public vespalib::ObjectOperation, public vespalib::ObjectPredicate
- {
- public:
- Configure(ExpressionNodeArray & exprVec) : _exprVec(exprVec) { }
- private:
- void execute(vespalib::Identifiable &obj) override { static_cast<AggregationRefNode&>(obj).locateExpression(_exprVec); }
- bool check(const vespalib::Identifiable &obj) const override { return obj.inherits(AggregationRefNode::classId); }
- ExpressionNodeArray & _exprVec;
- };
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
DECLARE_EXPRESSIONNODE(AggregationRefNode);
- AggregationRefNode() : _index(0), _expressionNode(NULL) { }
- AggregationRefNode(uint32_t index) : _index(index), _expressionNode(NULL) { }
+ AggregationRefNode() : _index(0), _expressionNode(nullptr) { }
+ AggregationRefNode(uint32_t index) : _index(index), _expressionNode(nullptr) { }
AggregationRefNode(const AggregationRefNode & rhs);
AggregationRefNode & operator = (const AggregationRefNode & exprref);
@@ -35,13 +22,10 @@ public:
void onPrepare(bool preserveAccurateTypes) override { _expressionNode->prepare(preserveAccurateTypes); }
bool onExecute() const override;
-private:
void locateExpression(ExpressionNodeArray & exprVec) const;
-
+private:
uint32_t _index;
mutable ExpressionNode *_expressionNode;
};
}
-}
-
diff --git a/searchlib/src/vespa/searchlib/expression/expressiontree.cpp b/searchlib/src/vespa/searchlib/expression/expressiontree.cpp
index efc9e77baf0..72a517a572e 100644
--- a/searchlib/src/vespa/searchlib/expression/expressiontree.cpp
+++ b/searchlib/src/vespa/searchlib/expression/expressiontree.cpp
@@ -21,14 +21,12 @@ ExpressionTree::Configure::execute(vespalib::Identifiable &obj) {
e.prepare(false);
}
-ExpressionTree::ExpressionTree() :
+ExpressionTree::ExpressionTree() noexcept :
_root(),
_attributeNodes(),
_documentAccessorNodes(),
_relevanceNodes()
-{
- prepare(false);
-}
+{ }
ExpressionTree::ExpressionTree(const ExpressionNode &root) :
_root(root.clone()),
diff --git a/searchlib/src/vespa/searchlib/expression/expressiontree.h b/searchlib/src/vespa/searchlib/expression/expressiontree.h
index 7617638ff16..9f7372c4a94 100644
--- a/searchlib/src/vespa/searchlib/expression/expressiontree.h
+++ b/searchlib/src/vespa/searchlib/expression/expressiontree.h
@@ -49,7 +49,7 @@ public:
bool check(const vespalib::Identifiable &obj) const override { return obj.inherits(ExpressionTree::classId); }
};
- ExpressionTree();
+ ExpressionTree() noexcept;
ExpressionTree(const ExpressionNode & root);
ExpressionTree(ExpressionNode::UP root);
ExpressionTree(const ExpressionTree & rhs);
@@ -76,7 +76,6 @@ private:
using AttributeNodeList = std::vector<AttributeNode *>;
using DocumentAccessorNodeList = std::vector<DocumentAccessorNode *>;
using RelevanceNodeList = std::vector<RelevanceNode *>;
- using InterpolatedLookupList = std::vector<InterpolatedLookup *>;
ExpressionNode::CP _root;
AttributeNodeList _attributeNodes;
diff --git a/vespalib/src/vespa/vespalib/util/sort.h b/vespalib/src/vespa/vespalib/util/sort.h
index a5fe1ae8e31..dab1abd2d33 100644
--- a/vespalib/src/vespa/vespalib/util/sort.h
+++ b/vespalib/src/vespa/vespalib/util/sort.h
@@ -2,6 +2,7 @@
#pragma once
#include <vespa/vespalib/objects/nbo.h>
+#include <cstring>
#include <functional>
#include <limits>
#include <algorithm>