summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLester Solbakken <lesters@users.noreply.github.com>2021-05-27 10:41:31 +0200
committerGitHub <noreply@github.com>2021-05-27 10:41:31 +0200
commite3d4dbac364216f8d93493d4a5f34835a268fbcf (patch)
tree90bc2cf28e08123a55854c2db1217f556d349a2e
parent92efe91ec3d7be1902e7ca9c0e290c7859d535af (diff)
parent6b6e59869ab5259a8cd2e382cd2b5164a963a293 (diff)
Merge branch 'master' into lesters/wire-in-stateless-onnx-rt
-rw-r--r--CMakeLists.txt5
-rw-r--r--ann_benchmark/CMakeLists.txt13
-rw-r--r--ann_benchmark/src/tests/ann_benchmark/.gitignore1
-rw-r--r--ann_benchmark/src/tests/ann_benchmark/CMakeLists.txt3
-rw-r--r--ann_benchmark/src/tests/ann_benchmark/test_angular.py41
-rw-r--r--ann_benchmark/src/tests/ann_benchmark/test_euclidean.py61
-rw-r--r--ann_benchmark/src/vespa/ann_benchmark/.gitignore1
-rw-r--r--ann_benchmark/src/vespa/ann_benchmark/CMakeLists.txt28
-rw-r--r--ann_benchmark/src/vespa/ann_benchmark/setup.py27
-rw-r--r--ann_benchmark/src/vespa/ann_benchmark/vespa_ann_benchmark.cpp246
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/SystemStateBroadcaster.java7
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/AppSubDirs.java32
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/ApplicationPackageXmlFilesValidator.java2
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java37
-rw-r--r--config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java14
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java6
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java9
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java6
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java16
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java72
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/DocumentOnlySearch.java6
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/ImmutableSearch.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/OnnxModel.java58
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFile.java35
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFiles.java35
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java93
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java65
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankingConstants.java1
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/Search.java48
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java114
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java3
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java29
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/RawRankProfile.java29
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java17
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/HostResource.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java22
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java14
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java88
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/Container.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java18
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java16
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java123
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java30
-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/DomResourceLimitsBuilder.java13
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java7
-rw-r--r--config-model/src/main/javacc/SDParser.jj14
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java57
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/DocumentGraphValidatorTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java14
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/ImportedFieldsEnumeratorTest.java15
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/RankProfileRegistryTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/EmptyRankProfileTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/IdTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/LiteralBoostTestCase.java6
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/SearchOrdererTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/TypeConversionTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/VsmFieldsTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java5
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ParentChildSearchModel.java3
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankProfileSearchFixture.java3
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolverTestCase.java18
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTransformerTokensTestCase.java3
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionsTestCase.java8
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java4
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java5
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java30
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java12
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/ml/ModelEvaluationTest.java5
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java4
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java1
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java11
-rw-r--r--config-provisioning/src/test/java/com/yahoo/config/provision/ClusterResourcesTest.java20
-rw-r--r--configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java6
-rw-r--r--configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/DefaultClusterReindexingStatusClient.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/InfraDeployerProvider.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java155
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployer.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java12
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java28
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java34
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java14
-rw-r--r--container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def2
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java9
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java5
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java28
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java53
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMock.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorImpl.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorMock.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/vcmr/VCMRReport.java148
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java15
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java47
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java176
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java123
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ConvergenceSummary.java14
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/NodeList.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainer.java28
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java25
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java85
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java11
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java)91
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainerTest.java26
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java18
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg177
-rw-r--r--dist/vespa.spec33
-rw-r--r--document/src/main/java/com/yahoo/document/DataType.java2
-rw-r--r--document/src/main/java/com/yahoo/document/json/TokenBuffer.java2
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/TensorReader.java29
-rw-r--r--document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java39
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/externslobrokpolicy.cpp5
-rw-r--r--eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp7
-rw-r--r--eval/src/vespa/eval/onnx/onnx_wrapper.cpp3
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java6
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java27
-rw-r--r--model-evaluation/abi-spec.json2
-rw-r--r--model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java14
-rw-r--r--model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java12
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelTester.java7
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java5
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/evaluation/OnnxEvaluatorTest.java5
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java5
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/handler/OnnxEvaluationHandlerTest.java5
-rw-r--r--model-evaluation/src/test/resources/config/models/ranking-expressions.cfg0
-rw-r--r--model-evaluation/src/test/resources/config/onnx/ranking-expressions.cfg0
-rw-r--r--model-evaluation/src/test/resources/config/rankexpression/ranking-expressions.cfg0
-rw-r--r--model-evaluation/src/test/resources/config/smallconstant/ranking-expressions.cfg0
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/TestProcessFactory.java15
-rw-r--r--node-repository/pom.xml2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsDb.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java15
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java24
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java48
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java5
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/ClusterId.java50
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java40
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java65
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java54
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java37
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java24
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java32
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java2
-rw-r--r--parent/pom.xml4
-rw-r--r--pom.xml1
-rw-r--r--searchcore/src/apps/tests/persistenceconformance_test.cpp1
-rw-r--r--searchcore/src/apps/verify_ranksetup/verify_ranksetup.cpp34
-rw-r--r--searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp1
-rw-r--r--searchcore/src/apps/vespa-proton-cmd/vespa-proton-cmd.cpp4
-rw-r--r--searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp2
-rw-r--r--searchcore/src/tests/proton/documentdb/documentdbconfig/documentdbconfig_test.cpp9
-rw-r--r--searchcore/src/tests/proton/documentdb/fileconfigmanager/fileconfigmanager_test.cpp6
-rw-r--r--searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp1
-rw-r--r--searchcore/src/tests/proton/matching/index_environment/.gitattributes1
-rw-r--r--searchcore/src/tests/proton/matching/index_environment/index_environment_test.cpp27
-rw-r--r--searchcore/src/tests/proton/matching/index_environment/my_expr6
-rw-r--r--searchcore/src/tests/proton/matching/index_environment/my_expr.lz4bin0 -> 164 bytes
-rw-r--r--searchcore/src/tests/proton/matching/matching_test.cpp2
-rw-r--r--searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp5
-rw-r--r--searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp9
-rw-r--r--searchcore/src/tests/proton/verify_ranksetup/bad_ranking_expression1
-rw-r--r--searchcore/src/tests/proton/verify_ranksetup/good_ranking_expression1
-rw-r--r--searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp36
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/CMakeLists.txt1
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/indexenvironment.cpp24
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/indexenvironment.h5
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/matcher.cpp23
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/matcher.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.cpp67
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.h35
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp10
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h8
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp67
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/matchers.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/pruneremoveddocumentsjob.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.cpp6
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.h1
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp7
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h5
-rw-r--r--searchlib/src/tests/features/ranking_expression/ranking_expression_test.cpp29
-rw-r--r--searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp5
-rw-r--r--searchlib/src/vespa/searchlib/fef/iindexenvironment.h5
-rw-r--r--searchlib/src/vespa/searchlib/fef/test/indexenvironment.cpp16
-rw-r--r--searchlib/src/vespa/searchlib/fef/test/indexenvironment.h5
-rw-r--r--slobrok/src/vespa/slobrok/cfg.cpp1
-rw-r--r--slobrok/src/vespa/slobrok/sbmirror.cpp5
-rw-r--r--storage/src/tests/distributor/distributor_stripe_pool_test.cpp2
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/bucketprioritydatabase.h4
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenanceoperation.h12
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenanceoperationgenerator.h8
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenancepriority.h8
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenancepriorityandtype.h6
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenanceprioritygenerator.h7
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenancescanner.h7
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.h8
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp4
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h17
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/prioritizedbucket.h11
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.cpp4
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.h10
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/simplemaintenancescanner.h6
-rw-r--r--storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp3
-rw-r--r--storage/src/vespa/storage/distributor/operations/operation.h1
-rw-r--r--storage/src/vespa/storage/distributor/statechecker.cpp1
-rw-r--r--storage/src/vespa/storage/persistence/mergehandler.cpp26
-rw-r--r--streamingvisitors/src/vespa/searchvisitor/indexenvironment.h4
-rw-r--r--vespa-feed-client-cli/CMakeLists.txt4
-rw-r--r--vespa-feed-client-cli/OWNERS2
-rw-r--r--vespa-feed-client-cli/pom.xml87
-rw-r--r--vespa-feed-client-cli/src/main/java/ai/vespa/feed/client/CliArguments.java (renamed from vespa-feed-client/src/main/java/ai/vespa/feed/client/CliArguments.java)45
-rw-r--r--vespa-feed-client-cli/src/main/java/ai/vespa/feed/client/CliClient.java (renamed from vespa-feed-client/src/main/java/ai/vespa/feed/client/CliClient.java)12
-rwxr-xr-xvespa-feed-client-cli/src/main/sh/vespa-feed-client.sh82
-rw-r--r--vespa-feed-client-cli/src/test/java/ai/vespa/feed/client/CliArgumentsTest.java (renamed from vespa-feed-client/src/test/java/ai/vespa/feed/client/CliArgumentsTest.java)8
-rw-r--r--vespa-feed-client-cli/src/test/resources/help.txt (renamed from vespa-feed-client/src/test/resources/help.txt)2
-rw-r--r--vespa-feed-client/CMakeLists.txt1
-rw-r--r--vespa-feed-client/pom.xml23
-rw-r--r--vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClientBuilder.java28
-rw-r--r--vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpFeedClient.java86
-rw-r--r--vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java5
-rw-r--r--vespajlib/src/main/java/com/yahoo/lang/MutableBoolean.java4
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java2
-rw-r--r--zookeeper-server/zookeeper-server-3.6.2/CMakeLists.txt2
-rw-r--r--zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt2
-rw-r--r--zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java4
-rw-r--r--zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java3
-rw-r--r--zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java9
-rw-r--r--zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ReconfigException.java4
-rw-r--r--zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java7
-rw-r--r--zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java9
284 files changed, 3498 insertions, 1543 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c98994cf993..7bf698b7a98 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,9 @@ find_package(JNI REQUIRED)
find_package(GTest REQUIRED)
+set(PYBIND11_FIND_PYTHON ON)
+find_package(pybind11 CONFIG REQUIRED)
+
include(build_settings.cmake)
# Enable CTest unit testing
@@ -42,6 +45,7 @@ vespa_install_data(valgrind-suppressions.txt etc/vespa)
# Include vespa config definitions in every target
include_directories(BEFORE ${CMAKE_BINARY_DIR}/configdefinitions/src)
+add_subdirectory(ann_benchmark)
add_subdirectory(application-model)
add_subdirectory(application-preprocessor)
add_subdirectory(athenz-identity-provider-service)
@@ -132,6 +136,7 @@ add_subdirectory(vdslib)
add_subdirectory(vdstestlib)
add_subdirectory(vespa-athenz)
add_subdirectory(vespa-feed-client)
+add_subdirectory(vespa-feed-client-cli)
add_subdirectory(vespa-http-client)
add_subdirectory(vespa-osgi-testrunner)
add_subdirectory(vespa-testrunner-components)
diff --git a/ann_benchmark/CMakeLists.txt b/ann_benchmark/CMakeLists.txt
new file mode 100644
index 00000000000..d34329667e3
--- /dev/null
+++ b/ann_benchmark/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_define_module(
+ DEPENDS
+ searchlib
+
+ LIBS
+ src/vespa/ann_benchmark
+
+ APPS
+
+ TESTS
+ src/tests/ann_benchmark
+)
diff --git a/ann_benchmark/src/tests/ann_benchmark/.gitignore b/ann_benchmark/src/tests/ann_benchmark/.gitignore
new file mode 100644
index 00000000000..225fc6f6650
--- /dev/null
+++ b/ann_benchmark/src/tests/ann_benchmark/.gitignore
@@ -0,0 +1 @@
+/__pycache__
diff --git a/ann_benchmark/src/tests/ann_benchmark/CMakeLists.txt b/ann_benchmark/src/tests/ann_benchmark/CMakeLists.txt
new file mode 100644
index 00000000000..6553cd071fb
--- /dev/null
+++ b/ann_benchmark/src/tests/ann_benchmark/CMakeLists.txt
@@ -0,0 +1,3 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+vespa_add_test(NAME ann_benchmark_test NO_VALGRIND COMMAND ${PYTHON_EXECUTABLE} -m pytest WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS vespa_ann_benchmark)
diff --git a/ann_benchmark/src/tests/ann_benchmark/test_angular.py b/ann_benchmark/src/tests/ann_benchmark/test_angular.py
new file mode 100644
index 00000000000..3e48a6bd970
--- /dev/null
+++ b/ann_benchmark/src/tests/ann_benchmark/test_angular.py
@@ -0,0 +1,41 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+import pytest
+import sys
+import os
+import math
+sys.path.insert(0, os.path.abspath("../../vespa/ann_benchmark"))
+from vespa_ann_benchmark import DistanceMetric, HnswIndexParams, HnswIndex
+
+class Fixture:
+ def __init__(self, normalize):
+ metric = DistanceMetric.InnerProduct if normalize else DistanceMetric.Angular
+ self.index = HnswIndex(2, HnswIndexParams(16, 200, metric, False), normalize)
+ self.index.set_vector(0, [1, 0])
+ self.index.set_vector(1, [10, 10])
+
+ def find(self, k, value):
+ return self.index.find_top_k(k, value, k + 200)
+
+ def run_test(self):
+ top = self.find(10, [1, 1])
+ assert [top[0][0], top[1][0]] == [0, 1]
+ # Allow some rounding errors
+ epsilon = 5e-8
+ assert abs((1 - top[0][1]) - math.sqrt(0.5)) < epsilon
+ assert abs((1 - top[1][1]) - 1) < epsilon
+ top2 = self.find(10, [0, 2])
+ # Result is not sorted by distance
+ assert [top2[0][0], top2[1][0]] == [0, 1]
+ assert abs((1 - top2[0][1]) - 0) < epsilon
+ assert abs((1 - top2[1][1]) - math.sqrt(0.5)) < epsilon
+ assert 1 == self.find(1, [1, 1])[0][0]
+ assert 0 == self.find(1, [1, -1])[0][0]
+
+def test_find_angular():
+ f = Fixture(False)
+ f.run_test()
+
+def test_find_angular_normalized():
+ f = Fixture(True)
+ f.run_test()
diff --git a/ann_benchmark/src/tests/ann_benchmark/test_euclidean.py b/ann_benchmark/src/tests/ann_benchmark/test_euclidean.py
new file mode 100644
index 00000000000..b6fb06cb029
--- /dev/null
+++ b/ann_benchmark/src/tests/ann_benchmark/test_euclidean.py
@@ -0,0 +1,61 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+import pytest
+import sys
+import os
+import math
+sys.path.insert(0, os.path.abspath("../../vespa/ann_benchmark"))
+from vespa_ann_benchmark import DistanceMetric, HnswIndexParams, HnswIndex
+
+class Fixture:
+ def __init__(self):
+ self.index = HnswIndex(2, HnswIndexParams(16, 200, DistanceMetric.Euclidean, False), False)
+
+ def set(self, lid, value):
+ self.index.set_vector(lid, value)
+
+ def get(self, lid):
+ return self.index.get_vector(lid)
+
+ def clear(self, lid):
+ return self.index.clear_vector(lid)
+
+ def find(self, k, value):
+ return self.index.find_top_k(k, value, k + 200)
+
+def test_set_value():
+ f = Fixture()
+ f.set(0, [1, 2])
+ f.set(1, [3, 4])
+ assert [1, 2] == f.get(0)
+ assert [3, 4] == f.get(1)
+
+def test_clear_value():
+ f = Fixture()
+ f.set(0, [1, 2])
+ assert [1, 2] == f.get(0)
+ f.clear(0)
+ assert [0, 0] == f.get(0)
+
+def test_find():
+ f = Fixture()
+ f.set(0, [0, 0])
+ f.set(1, [10, 10])
+ top = f.find(10, [1, 1])
+ assert [top[0][0], top[1][0]] == [0, 1]
+ # Allow some rounding errors
+ epsilon = 1e-20
+ assert abs(top[0][1] - math.sqrt(2)) < epsilon
+ assert abs(top[1][1] - math.sqrt(162)) < epsilon
+ top2 = f.find(10, [9, 9])
+ # Result is not sorted by distance
+ assert [top2[0][0], top2[1][0]] == [0, 1]
+ assert abs(top2[0][1] - math.sqrt(162)) < epsilon
+ assert abs(top2[1][1] - math.sqrt(2)) < epsilon
+ assert 0 == f.find(1, [1, 1])[0][0]
+ assert 1 == f.find(1, [9, 9])[0][0]
+ f.clear(1)
+ assert 0 == f.find(1, [9, 9])[0][0]
+ assert 0 == f.find(1, [0, 0])[0][0]
+ f.clear(0)
+ assert 0 == len(f.find(1, [9, 9]))
diff --git a/ann_benchmark/src/vespa/ann_benchmark/.gitignore b/ann_benchmark/src/vespa/ann_benchmark/.gitignore
new file mode 100644
index 00000000000..8249d30d8c7
--- /dev/null
+++ b/ann_benchmark/src/vespa/ann_benchmark/.gitignore
@@ -0,0 +1 @@
+/vespa_ann_benchmark.cpython-36m-x86_64-linux-gnu.so
diff --git a/ann_benchmark/src/vespa/ann_benchmark/CMakeLists.txt b/ann_benchmark/src/vespa/ann_benchmark/CMakeLists.txt
new file mode 100644
index 00000000000..a9a8ceb3660
--- /dev/null
+++ b/ann_benchmark/src/vespa/ann_benchmark/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+install(DIRECTORY DESTINATION libexec/vespa_ann_benchmark)
+
+vespa_add_library(vespa_ann_benchmark
+ ALLOW_UNRESOLVED_SYMBOLS
+ SOURCES
+ vespa_ann_benchmark.cpp
+
+ INSTALL libexec/vespa_ann_benchmark
+ DEPENDS
+ pybind11::pybind11
+)
+
+if (TARGET pybind11::lto)
+ target_link_libraries(vespa_ann_benchmark PRIVATE pybind11::module pybind11::lto)
+else()
+ target_link_libraries(vespa_ann_benchmark PRIVATE pybind11::module)
+endif()
+
+if (COMMAND pybind11_extension)
+ pybind11_extension(vespa_ann_benchmark)
+else()
+ set_target_properties(vespa_ann_benchmark PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}")
+ set_target_properties(vespa_ann_benchmark PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}")
+endif()
+
+set_target_properties(vespa_ann_benchmark PROPERTIES CXX_VISIBILITY_PRESET "hidden")
+vespa_install_script(setup.py libexec/vespa_ann_benchmark)
diff --git a/ann_benchmark/src/vespa/ann_benchmark/setup.py b/ann_benchmark/src/vespa/ann_benchmark/setup.py
new file mode 100644
index 00000000000..e19dada5fff
--- /dev/null
+++ b/ann_benchmark/src/vespa/ann_benchmark/setup.py
@@ -0,0 +1,27 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+import os
+import sys
+import platform
+import distutils.sysconfig
+from setuptools import setup, Extension
+from setuptools.command.build_ext import build_ext
+
+class PreBuiltExt(build_ext):
+ def build_extension(self, ext):
+ print("Using prebuilt extension library")
+ libdir="lib.%s-%s-%s" % (sys.platform, platform.machine(), distutils.sysconfig.get_python_version())
+ os.system("mkdir -p build/%s" % libdir)
+ os.system("cp -p vespa_ann_benchmark.*.so build/%s" % libdir)
+
+setup(
+ name="vespa_ann_benchmark",
+ version="0.1.0",
+ author="Tor Egge",
+ author_email="Tor.Egge@verizonmedia.com",
+ description="Python binding for the Vespa implementation of an HNSW index for nearest neighbor search",
+ long_description="Python binding for the Vespa implementation of an HNSW index for nearest neighbor search used for low-level benchmarking",
+ ext_modules=[Extension("vespa_ann_benchmark", sources=[])],
+ cmdclass={"build_ext": PreBuiltExt},
+ zip_safe=False,
+)
diff --git a/ann_benchmark/src/vespa/ann_benchmark/vespa_ann_benchmark.cpp b/ann_benchmark/src/vespa/ann_benchmark/vespa_ann_benchmark.cpp
new file mode 100644
index 00000000000..3304e598862
--- /dev/null
+++ b/ann_benchmark/src/vespa/ann_benchmark/vespa_ann_benchmark.cpp
@@ -0,0 +1,246 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <vespa/searchcommon/attribute/hnsw_index_params.h>
+#include <vespa/searchlib/attribute/attributevector.h>
+#include <vespa/searchlib/attribute/attributefactory.h>
+#include <vespa/searchlib/tensor/dense_tensor_attribute.h>
+#include <vespa/searchlib/tensor/nearest_neighbor_index.h>
+#include <vespa/eval/eval/value.h>
+#include <vespa/vespalib/test/insertion_operators.h>
+#include <ostream>
+#include <sstream>
+#include <limits>
+
+namespace py = pybind11;
+
+using search::AttributeFactory;
+using search::AttributeVector;
+using search::attribute::BasicType;
+using search::attribute::Config;
+using search::attribute::CollectionType;
+using search::attribute::DistanceMetric;
+using search::attribute::HnswIndexParams;
+using search::tensor::NearestNeighborIndex;
+using search::tensor::TensorAttribute;
+using vespalib::eval::CellType;
+using vespalib::eval::DenseValueView;
+using vespalib::eval::TypedCells;
+using vespalib::eval::ValueType;
+using vespalib::eval::Value;
+
+namespace vespa_ann_benchmark {
+
+using TopKResult = std::vector<std::pair<uint32_t, double>>;
+
+namespace {
+
+std::string
+make_tensor_spec(uint32_t dim_size)
+{
+ std::ostringstream os;
+ os << "tensor<float>(x[" << dim_size << "])";
+ return os.str();
+}
+
+constexpr uint32_t lid_bias = 1; // lid 0 is reserved
+
+}
+
+/*
+ * Class exposing the Vespa implementation of an HNSW index for nearest neighbor search over data points in a high dimensional vector space.
+ *
+ * A tensor attribute field (https://docs.vespa.ai/en/reference/schema-reference.html#type:tensor) is used to store the vectors in memory.
+ * This class only supports single-threaded access (both for indexing and searching),
+ * and should only be used for low-level benchmarking.
+ * To use nearest neighbor search in a Vespa application,
+ * see https://docs.vespa.ai/en/approximate-nn-hnsw.html for more details.
+ */
+class HnswIndex
+{
+ ValueType _tensor_type;
+ HnswIndexParams _hnsw_index_params;
+ std::shared_ptr<AttributeVector> _attribute;
+ TensorAttribute* _tensor_attribute;
+ const NearestNeighborIndex* _nearest_neighbor_index;
+ size_t _dim_size;
+ bool _normalize_vectors;
+
+ bool check_lid(uint32_t lid);
+ bool check_value(const char *op, const std::vector<float>& value);
+ TypedCells get_typed_cells(const std::vector<float>& value, std::vector<float>& normalized_value);
+public:
+ HnswIndex(uint32_t dim_size, const HnswIndexParams &hnsw_index_params, bool normalize_vectors);
+ virtual ~HnswIndex();
+ void set_vector(uint32_t lid, const std::vector<float>& value);
+ std::vector<float> get_vector(uint32_t lid);
+ void clear_vector(uint32_t lid);
+ TopKResult find_top_k(uint32_t k, const std::vector<float>& value, uint32_t explore_k);
+};
+
+HnswIndex::HnswIndex(uint32_t dim_size, const HnswIndexParams &hnsw_index_params, bool normalize_vectors)
+ : _tensor_type(ValueType::error_type()),
+ _hnsw_index_params(hnsw_index_params),
+ _attribute(),
+ _tensor_attribute(nullptr),
+ _nearest_neighbor_index(nullptr),
+ _dim_size(0u),
+ _normalize_vectors(normalize_vectors)
+{
+ Config cfg(BasicType::TENSOR, CollectionType::SINGLE);
+ _tensor_type = ValueType::from_spec(make_tensor_spec(dim_size));
+ assert(_tensor_type.is_dense());
+ assert(_tensor_type.count_indexed_dimensions() == 1u);
+ _dim_size = _tensor_type.dimensions()[0].size;
+ cfg.setTensorType(_tensor_type);
+ cfg.set_distance_metric(hnsw_index_params.distance_metric());
+ cfg.set_hnsw_index_params(hnsw_index_params);
+ _attribute = AttributeFactory::createAttribute("tensor", cfg);
+ _tensor_attribute = dynamic_cast<TensorAttribute *>(_attribute.get());
+ assert(_tensor_attribute != nullptr);
+ _nearest_neighbor_index = _tensor_attribute->nearest_neighbor_index();
+ assert(_nearest_neighbor_index != nullptr);
+}
+
+HnswIndex::~HnswIndex() = default;
+
+bool
+HnswIndex::check_lid(uint32_t lid)
+{
+ if (lid >= std::numeric_limits<uint32_t>::max() - lid_bias) {
+ std::cerr << "lid is too high" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool
+HnswIndex::check_value(const char *op, const std::vector<float>& value)
+{
+ if (value.size() != _dim_size) {
+ std::cerr << op << " failed, expected vector with size " << _dim_size << ", got vector with size " << value.size() << std::endl;
+ return false;
+ }
+ return true;
+}
+
+TypedCells
+HnswIndex::get_typed_cells(const std::vector<float>& value, std::vector<float>& normalized_value)
+{
+ if (!_normalize_vectors) {
+ return {&value[0], CellType::FLOAT, value.size()};
+ }
+ float sum_of_squared = 0.0f;
+ for (auto elem : value) {
+ sum_of_squared += elem * elem;
+ }
+ float factor = 1.0f / (sqrtf(sum_of_squared) + 1e-40f);
+ normalized_value.reserve(value.size());
+ normalized_value.clear();
+ for (auto elem : value) {
+ normalized_value.emplace_back(elem * factor);
+ }
+ return {&normalized_value[0], CellType::FLOAT, normalized_value.size()};
+}
+
+void
+HnswIndex::set_vector(uint32_t lid, const std::vector<float>& value)
+{
+ if (!check_lid(lid)) {
+ return;
+ }
+ if (!check_value("set_vector", value)) {
+ return;
+ }
+ /*
+ * Not thread safe against concurrent set_vector().
+ */
+ std::vector<float> normalized_value;
+ auto typed_cells = get_typed_cells(value, normalized_value);
+ DenseValueView tensor_view(_tensor_type, typed_cells);
+ while (size_t(lid + lid_bias) >= _attribute->getNumDocs()) {
+ uint32_t new_lid = 0;
+ _attribute->addDoc(new_lid);
+ }
+ _tensor_attribute->setTensor(lid + lid_bias, tensor_view); // lid 0 is special in vespa
+ _attribute->commit();
+}
+
+std::vector<float>
+HnswIndex::get_vector(uint32_t lid)
+{
+ if (!check_lid(lid)) {
+ return {};
+ }
+ TypedCells typed_cells = _tensor_attribute->extract_cells_ref(lid + lid_bias);
+ assert(typed_cells.size == _dim_size);
+ const float* data = static_cast<const float* >(typed_cells.data);
+ return {data, data + _dim_size};
+ return {};
+}
+
+void
+HnswIndex::clear_vector(uint32_t lid)
+{
+ if (!check_lid(lid)) {
+ return;
+ }
+ if (size_t(lid + lid_bias) < _attribute->getNumDocs()) {
+ _attribute->clearDoc(lid + lid_bias);
+ _attribute->commit();
+ }
+}
+
+TopKResult
+HnswIndex::find_top_k(uint32_t k, const std::vector<float>& value, uint32_t explore_k)
+{
+ if (!check_value("find_top_k", value)) {
+ return {};
+ }
+ /*
+ * Not thread safe against concurrent set_vector() since attribute
+ * read guard is not taken here.
+ */
+ TopKResult result;
+ std::vector<float> normalized_value;
+ auto typed_cells = get_typed_cells(value, normalized_value);
+ auto raw_result = _nearest_neighbor_index->find_top_k(k, typed_cells, explore_k, std::numeric_limits<double>::max());
+ result.reserve(raw_result.size());
+ switch (_hnsw_index_params.distance_metric()) {
+ case DistanceMetric::Euclidean:
+ for (auto &raw : raw_result) {
+ result.emplace_back(raw.docid - lid_bias, sqrt(raw.distance));
+ }
+ break;
+ default:
+ for (auto &raw : raw_result) {
+ result.emplace_back(raw.docid - lid_bias, raw.distance);
+ }
+ }
+ // Results are sorted by lid, not by distance
+ return result;
+}
+
+}
+
+using vespa_ann_benchmark::HnswIndex;
+
+PYBIND11_MODULE(vespa_ann_benchmark, m) {
+ m.doc() = "vespa_ann_benchmark plugin";
+
+ py::enum_<DistanceMetric>(m, "DistanceMetric")
+ .value("Euclidean", DistanceMetric::Euclidean)
+ .value("Angular", DistanceMetric::Angular)
+ .value("InnerProduct", DistanceMetric::InnerProduct);
+
+ py::class_<HnswIndexParams>(m, "HnswIndexParams")
+ .def(py::init<uint32_t, uint32_t, DistanceMetric, bool>());
+
+ py::class_<HnswIndex>(m, "HnswIndex")
+ .def(py::init<uint32_t, const HnswIndexParams&, bool>())
+ .def("set_vector", &HnswIndex::set_vector)
+ .def("get_vector", &HnswIndex::get_vector)
+ .def("clear_vector", &HnswIndex::clear_vector)
+ .def("find_top_k", &HnswIndex::find_top_k);
+}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/SystemStateBroadcaster.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/SystemStateBroadcaster.java
index b29e232dc08..ff27576940f 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/SystemStateBroadcaster.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/SystemStateBroadcaster.java
@@ -2,10 +2,13 @@
package com.yahoo.vespa.clustercontroller.core;
import com.yahoo.jrt.ErrorCode;
-import java.util.logging.Level;
-import com.yahoo.vdslib.state.*;
+import com.yahoo.vdslib.state.ClusterState;
+import com.yahoo.vdslib.state.Node;
+import com.yahoo.vdslib.state.NodeState;
+import com.yahoo.vdslib.state.State;
import com.yahoo.vespa.clustercontroller.core.database.DatabaseHandler;
+import java.util.logging.Level;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/AppSubDirs.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/AppSubDirs.java
index 2b7aa5ab6e1..ca8f9c17066 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/AppSubDirs.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/AppSubDirs.java
@@ -13,22 +13,14 @@ import java.io.File;
*/
public class AppSubDirs {
- final Tuple2<File, String> root;
- public final Tuple2<File, String> rules;
- final Tuple2<File, String> searchchains;
- final Tuple2<File, String> docprocchains;
- final Tuple2<File, String> routingtables;
- final Tuple2<File, String> configDefs;
- final Tuple2<File, String> searchdefinitions;
+ private final Tuple2<File, String> root;
+ private final Tuple2<File, String> routingtables;
+ private final Tuple2<File, String> configDefs;
public AppSubDirs(File root) {
this.root = new Tuple2<>(root, root.getName());
- rules = createTuple(ApplicationPackage.RULES_DIR.getRelative());
- searchchains = createTuple(ApplicationPackage.SEARCHCHAINS_DIR);
- docprocchains = createTuple(ApplicationPackage.DOCPROCCHAINS_DIR);
routingtables = createTuple(ApplicationPackage.ROUTINGTABLES_DIR);
configDefs = createTuple(ApplicationPackage.CONFIG_DEFINITIONS_DIR);
- searchdefinitions = createTuple(ApplicationPackage.SEARCH_DEFINITIONS_DIR.getRelative());
}
private Tuple2<File, String> createTuple(String name) {
@@ -43,23 +35,11 @@ public class AppSubDirs {
return root.first;
}
- public File rules() {
- return rules.first;
- }
-
- public File searchchains() {
- return searchchains.first;
- }
-
- public File docprocchains() {
- return docprocchains.first;
- }
-
public File configDefs() {
return configDefs.first;
}
-
- @Deprecated // Remove after March 2020
- public File searchdefinitions() { return searchdefinitions.first; }
+ public Tuple2<File, String> routingTables() {
+ return routingtables;
+ }
}
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/ApplicationPackageXmlFilesValidator.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/ApplicationPackageXmlFilesValidator.java
index b0c4f74f9f6..79a8c6ea16c 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/ApplicationPackageXmlFilesValidator.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/ApplicationPackageXmlFilesValidator.java
@@ -40,7 +40,7 @@ public class ApplicationPackageXmlFilesValidator {
validateOptional(validators.hostsXmlValidator(), FilesApplicationPackage.HOSTS);
validateOptional(validators.deploymentXmlValidator(), FilesApplicationPackage.DEPLOYMENT_FILE.getName());
validateOptional(validators.validationOverridesXmlValidator(), FilesApplicationPackage.VALIDATION_OVERRIDES.getName());
- validateRouting(appDirs.routingtables);
+ validateRouting(appDirs.routingTables());
}
// For testing
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java
index da5bff984db..db875f669fe 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/FilesApplicationPackage.java
@@ -148,14 +148,13 @@ public class FilesApplicationPackage implements ApplicationPackage {
* @param metaData metadata for this application package
* @param includeSourceFiles include files from source dirs
*/
- @SuppressWarnings("deprecation")
private FilesApplicationPackage(File appDir, File preprocessedDir, ApplicationMetaData metaData, boolean includeSourceFiles) {
verifyAppDir(appDir);
this.includeSourceFiles = includeSourceFiles;
this.appDir = appDir;
this.preprocessedDir = preprocessedDir;
appSubDirs = new AppSubDirs(appDir);
- configDefsDir = new File(appDir, ApplicationPackage.CONFIG_DEFINITIONS_DIR);
+ configDefsDir = new File(appDir, CONFIG_DEFINITIONS_DIR);
addUserIncludeDirs();
this.metaData = metaData;
transformerFactory = TransformerFactory.newInstance();
@@ -236,9 +235,8 @@ public class FilesApplicationPackage implements ApplicationPackage {
return getHostsFile().getPath();
}
- @SuppressWarnings("deprecation")
private File getHostsFile() {
- return new File(appDir, ApplicationPackage.HOSTS);
+ return new File(appDir, HOSTS);
}
@Override
@@ -246,9 +244,8 @@ public class FilesApplicationPackage implements ApplicationPackage {
return getServicesFile().getPath();
}
- @SuppressWarnings("deprecation")
private File getServicesFile() {
- return new File(appDir, ApplicationPackage.SERVICES);
+ return new File(appDir, SERVICES);
}
@Override
@@ -338,7 +335,7 @@ public class FilesApplicationPackage implements ApplicationPackage {
addAllDefsFromConfigDir(defs, new File("src/main/resources/configdefinitions"));
addAllDefsFromConfigDir(defs, new File("src/test/resources/configdefinitions"));
}
- addAllDefsFromBundles(defs, FilesApplicationPackage.getComponents(appDir));
+ addAllDefsFromBundles(defs, getComponents(appDir));
return defs;
}
@@ -427,13 +424,13 @@ public class FilesApplicationPackage implements ApplicationPackage {
static List<File> getSearchDefinitionFiles(File appDir) {
List<File> schemaFiles = new ArrayList<>();
- File sdDir = new File(appDir, ApplicationPackage.SEARCH_DEFINITIONS_DIR.getRelative());
+ File sdDir = new File(appDir, SEARCH_DEFINITIONS_DIR.getRelative());
if (sdDir.isDirectory())
- schemaFiles.addAll(Arrays.asList(sdDir.listFiles((dir, name) -> name.matches(".*\\" + ApplicationPackage.SD_NAME_SUFFIX))));
+ schemaFiles.addAll(Arrays.asList(sdDir.listFiles((dir, name) -> name.matches(".*\\" + SD_NAME_SUFFIX))));
- sdDir = new File(appDir, ApplicationPackage.SCHEMAS_DIR.getRelative());
+ sdDir = new File(appDir, SCHEMAS_DIR.getRelative());
if (sdDir.isDirectory())
- schemaFiles.addAll(Arrays.asList(sdDir.listFiles((dir, name) -> name.matches(".*\\" + ApplicationPackage.SD_NAME_SUFFIX))));
+ schemaFiles.addAll(Arrays.asList(sdDir.listFiles((dir, name) -> name.matches(".*\\" + SD_NAME_SUFFIX))));
return schemaFiles;
}
@@ -445,16 +442,16 @@ public class FilesApplicationPackage implements ApplicationPackage {
// Only for use by deploy processor
public static List<Component> getComponents(File appDir) {
List<Component> components = new ArrayList<>();
- for (Bundle bundle : Bundle.getBundles(new File(appDir, ApplicationPackage.COMPONENT_DIR))) {
- components.add(new Component(bundle, new ComponentInfo(new File(ApplicationPackage.COMPONENT_DIR, bundle.getFile().getName()).getPath())));
+ for (Bundle bundle : Bundle.getBundles(new File(appDir, COMPONENT_DIR))) {
+ components.add(new Component(bundle, new ComponentInfo(new File(COMPONENT_DIR, bundle.getFile().getName()).getPath())));
}
return components;
}
private static List<ComponentInfo> getComponentsInfo(File appDir) {
List<ComponentInfo> components = new ArrayList<>();
- for (Bundle bundle : Bundle.getBundles(new File(appDir, ApplicationPackage.COMPONENT_DIR))) {
- components.add(new ComponentInfo(new File(ApplicationPackage.COMPONENT_DIR, bundle.getFile().getName()).getPath()));
+ for (Bundle bundle : Bundle.getBundles(new File(appDir, COMPONENT_DIR))) {
+ components.add(new ComponentInfo(new File(COMPONENT_DIR, bundle.getFile().getName()).getPath()));
}
return components;
}
@@ -545,10 +542,10 @@ public class FilesApplicationPackage implements ApplicationPackage {
if (new File(name).isAbsolute())
throw new IllegalArgumentException("Absolute path to ranking expression file is not allowed: " + name);
- File sdDir = new File(appDir, ApplicationPackage.SCHEMAS_DIR.getRelative());
+ File sdDir = new File(appDir, SCHEMAS_DIR.getRelative());
File expressionFile = new File(sdDir, name);
if ( ! expressionFile.exists()) {
- sdDir = new File(appDir, ApplicationPackage.SEARCH_DEFINITIONS_DIR.getRelative());
+ sdDir = new File(appDir, SEARCH_DEFINITIONS_DIR.getRelative());
expressionFile = new File(sdDir, name);
}
return expressionFile;
@@ -611,7 +608,7 @@ public class FilesApplicationPackage implements ApplicationPackage {
File servicesFile = validateServicesFile();
preprocessXML(new File(preprocessedDir, SERVICES), servicesFile, zone);
preprocessXML(new File(preprocessedDir, HOSTS), getHostsFile(), zone);
- FilesApplicationPackage preprocessed = FilesApplicationPackage.fromFile(preprocessedDir, includeSourceFiles);
+ FilesApplicationPackage preprocessed = fromFile(preprocessedDir, includeSourceFiles);
preprocessed.copyUserDefsIntoApplication();
return preprocessed;
}
@@ -629,7 +626,7 @@ public class FilesApplicationPackage implements ApplicationPackage {
ConfigDefinitionDir defDir = new ConfigDefinitionDir(destination);
// Copy the user's def files from components.
List<Bundle> bundlesAdded = new ArrayList<>();
- for (FilesApplicationPackage.Component component : FilesApplicationPackage.getComponents(appSubDirs.root())) {
+ for (Component component : getComponents(appSubDirs.root())) {
Bundle bundle = component.getBundle();
defDir.addConfigDefinitionsFromBundle(bundle, bundlesAdded);
bundlesAdded.add(bundle);
@@ -645,7 +642,7 @@ public class FilesApplicationPackage implements ApplicationPackage {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
- for (File file : appDir.listFiles((dir, name) -> !name.equals(ApplicationPackage.EXT_DIR) && !name.startsWith("."))) {
+ for (File file : appDir.listFiles((dir, name) -> !name.equals(EXT_DIR) && !name.startsWith("."))) {
addPathToDigest(file, "", md5, true, false);
}
return toLowerCase(HexDump.toHexString(md5.digest()));
diff --git a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java
index 6888626633b..e779d59ba24 100644
--- a/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java
+++ b/config-application-package/src/main/java/com/yahoo/config/model/application/provider/PreGeneratedFileRegistry.java
@@ -7,7 +7,11 @@ import com.yahoo.config.application.api.FileRegistry;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -50,7 +54,7 @@ public class PreGeneratedFileRegistry implements FileRegistry {
}
public static String exportRegistry(FileRegistry registry) {
- List<FileRegistry.Entry> entries = registry.export();
+ List<Entry> entries = registry.export();
StringBuilder builder = new StringBuilder();
builder.append(registry.fileSourceHost()).append('\n');
@@ -66,7 +70,11 @@ public class PreGeneratedFileRegistry implements FileRegistry {
}
public FileReference addFile(String relativePath) {
- return new FileReference(path2Hash.get(relativePath));
+ String reference = path2Hash.get(relativePath);
+ if (reference == null) {
+ throw new IllegalArgumentException("File '" + relativePath + "' not found");
+ }
+ return new FileReference(reference);
}
@Override
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java
index 0d868057adb..d97ff5ca774 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java
@@ -10,10 +10,7 @@ import com.yahoo.path.Path;
import com.yahoo.text.XML;
import com.yahoo.vespa.config.ConfigDefinitionKey;
import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
index 5d28088d108..e3afdec85b9 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
@@ -19,6 +19,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URI;
+import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -83,6 +84,7 @@ public interface ModelContext {
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean useBucketExecutorForLidSpaceCompact() { return true; }
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean useBucketExecutorForBucketMove() { return true; }
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean useBucketExecutorForPruneRemoved() { throw new UnsupportedOperationException("TODO specify default value"); }
+ @ModelFeatureFlag(owners = {"baldersheim"}) default boolean useExternalRankExpressions() { return false; }
@ModelFeatureFlag(owners = {"geirst"}) default boolean enableFeedBlockInDistributor() { return true; }
@ModelFeatureFlag(owners = {"baldersheim", "geirst", "toregge"}) default double maxDeadBytesRatio() { return 0.05; }
@ModelFeatureFlag(owners = {"hmusum"}, removeAfter = "7.406") default int clusterControllerMaxHeapSizeInMb() { return 128; }
@@ -91,7 +93,7 @@ public interface ModelContext {
@ModelFeatureFlag(owners = {"tokle"}) default boolean tenantIamRole() { return false; }
@ModelFeatureFlag(owners = {"vekterli"}) default int maxActivationInhibitedOutOfSyncGroups() { return 0; }
@ModelFeatureFlag(owners = {"hmusum"}) default String jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type type) { return ""; }
- @ModelFeatureFlag(owners = {"bjorncs", "jonmv"}) default boolean enableJdiscHttp2() { return false; }
+ @ModelFeatureFlag(owners = {"bjorncs", "jonmv"}, removeAfter = "7.409") default boolean enableJdiscHttp2() { return true; }
@ModelFeatureFlag(owners = {"tokle", "bjorncs"}) default boolean enableCustomAclMapping() { return false; }
@ModelFeatureFlag(owners = {"geirst", "vekterli"}) default int numDistributorStripes() { return 0; }
}
@@ -135,6 +137,8 @@ public interface ModelContext {
// Allow disabling mTLS for now, harden later
default boolean allowDisableMtls() { return true; }
+
+ default List<X509Certificate> operatorCertificates() { return List.of(); }
}
@Retention(RetentionPolicy.RUNTIME)
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
index a77dd65c608..3fd2d009412 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
@@ -432,7 +432,7 @@ public class DeployState implements ConfigDefinitionStore {
RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
QueryProfiles queryProfiles = new QueryProfilesBuilder().build(applicationPackage, logger);
SemanticRules semanticRules = new SemanticRuleBuilder().build(applicationPackage);
- SearchDocumentModel searchDocumentModel = createSearchDocumentModel(rankProfileRegistry, logger, queryProfiles, validationParameters);
+ SearchDocumentModel searchDocumentModel = createSearchDocumentModel(rankProfileRegistry, queryProfiles, validationParameters);
return new DeployState(applicationPackage,
searchDocumentModel,
rankProfileRegistry,
@@ -458,16 +458,15 @@ public class DeployState implements ConfigDefinitionStore {
}
private SearchDocumentModel createSearchDocumentModel(RankProfileRegistry rankProfileRegistry,
- DeployLogger logger,
QueryProfiles queryProfiles,
ValidationParameters validationParameters) {
Collection<NamedReader> readers = applicationPackage.getSearchDefinitions();
Map<String, String> names = new LinkedHashMap<>();
- SearchBuilder builder = new SearchBuilder(applicationPackage, rankProfileRegistry, queryProfiles.getRegistry());
+ SearchBuilder builder = new SearchBuilder(applicationPackage, logger, rankProfileRegistry, queryProfiles.getRegistry());
for (NamedReader reader : readers) {
try {
String readerName = reader.getName();
- String topLevelName = builder.importReader(reader, readerName, logger);
+ String topLevelName = builder.importReader(reader, readerName);
String sdName = stripSuffix(readerName, ApplicationPackage.SD_NAME_SUFFIX);
names.put(topLevelName, sdName);
if ( ! sdName.equals(topLevelName)) {
@@ -483,7 +482,7 @@ public class DeployState implements ConfigDefinitionStore {
closeIgnoreException(reader.getReader());
}
}
- builder.build(! validationParameters.ignoreValidationErrors(), logger);
+ builder.build(! validationParameters.ignoreValidationErrors());
return SearchDocumentModel.fromBuilderAndNames(builder, names);
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
index e6bf3a835c6..66e728957f3 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
@@ -52,6 +52,7 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
private double feedConcurrency = 0.5;
private boolean useBucketExecutorForPruneRemoved;
private boolean enableFeedBlockInDistributor = true;
+ private boolean useExternalRankExpression = false;
private int clusterControllerMaxHeapSizeInMb = 128;
private int metricsProxyMaxHeapSizeInMb = 256;
private int maxActivationInhibitedOutOfSyncGroups = 0;
@@ -98,7 +99,12 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
@Override public String jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type type) { return jvmOmitStackTraceInFastThrowOption; }
@Override public int numDistributorStripes() { return numDistributorStripes; }
@Override public boolean allowDisableMtls() { return allowDisableMtls; }
+ @Override public boolean useExternalRankExpressions() { return useExternalRankExpression; }
+ public TestProperties useExternalRankExpression(boolean value) {
+ useExternalRankExpression = value;
+ return this;
+ }
public TestProperties setFeedConcurrency(double feedConcurrency) {
this.feedConcurrency = feedConcurrency;
return this;
diff --git a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
index 602e0c80d4b..2784c111019 100644
--- a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
+++ b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
@@ -43,7 +43,7 @@ public abstract class AbstractConfigProducer<CHILD extends AbstractConfigProduce
private final String subId;
private String configId = null;
- private List<Service> descendantServices = new ArrayList<>();
+ private final List<Service> descendantServices = new ArrayList<>();
private AbstractConfigProducer parent = null;
@@ -59,8 +59,8 @@ public abstract class AbstractConfigProducer<CHILD extends AbstractConfigProduce
* Creates a new AbstractConfigProducer with the given parent and subId.
* This constructor will add the resulting producer to the children of parent.
*
- * @param parent The parent of this ConfigProducer
- * @param subId The fragment of the config id for the producer
+ * @param parent the parent of this ConfigProducer
+ * @param subId the fragment of the config id for the producer
*/
public AbstractConfigProducer(AbstractConfigProducer parent, String subId) {
this(subId);
@@ -69,7 +69,13 @@ public abstract class AbstractConfigProducer<CHILD extends AbstractConfigProduce
}
}
- protected final void setParent(AbstractConfigProducer parent) {
+ /** Removes this from the config model */
+ protected void remove() {
+ if (parent != null)
+ parent.removeChild(this);
+ }
+
+ protected final void setParent(AbstractConfigProducer<?> parent) {
this.parent = parent;
computeConfigId();
}
@@ -92,7 +98,7 @@ public abstract class AbstractConfigProducer<CHILD extends AbstractConfigProduce
/**
* Adds a child to this config producer.
*
- * @param child The child config producer to add.
+ * @param child the child config producer to add
*/
protected void addChild(CHILD child) {
if (child == null) {
diff --git a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java
index 2594c64b951..7200c3211ba 100644
--- a/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java
+++ b/config-model/src/main/java/com/yahoo/config/model/test/MockApplicationPackage.java
@@ -6,6 +6,7 @@ import com.yahoo.config.application.api.ComponentInfo;
import com.yahoo.config.application.api.UnparsedConfigDefinition;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.component.Version;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.InstanceName;
@@ -116,6 +117,7 @@ public class MockApplicationPackage implements ApplicationPackage {
public List<NamedReader> getSearchDefinitions() {
ArrayList<NamedReader> readers = new ArrayList<>();
SearchBuilder searchBuilder = new SearchBuilder(this,
+ new BaseDeployLogger(),
new RankProfileRegistry(),
queryProfileRegistry);
for (String sd : schemas) {
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java b/config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java
new file mode 100644
index 00000000000..77ce2dd41b5
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java
@@ -0,0 +1,72 @@
+package com.yahoo.searchdefinition;
+
+import com.yahoo.config.FileReference;
+import com.yahoo.path.Path;
+import com.yahoo.vespa.model.AbstractService;
+import com.yahoo.vespa.model.utils.FileSender;
+
+import java.util.Collection;
+import java.util.Objects;
+
+public class DistributableResource {
+ public enum PathType { FILE, URI };
+
+ /** The search definition-unique name of this constant */
+ private final String name;
+ private String path = null;
+ private String fileReference = "";
+ private PathType pathType = PathType.FILE;
+
+ public PathType getPathType() {
+ return pathType;
+ }
+
+ public DistributableResource(String name) {
+ this(name, null);
+ }
+ public DistributableResource(String name, String path) {
+ this.name = name;
+ this.path = path;
+ }
+
+ public void setFileName(String fileName) {
+ Objects.requireNonNull(fileName, "Filename cannot be null");
+ this.path = fileName;
+ this.pathType = PathType.FILE;
+ }
+
+ public void setUri(String uri) {
+ Objects.requireNonNull(uri, "uri cannot be null");
+ this.path = uri;
+ this.pathType = PathType.URI;
+ }
+
+ protected void setFileReference(String fileReference) { this.fileReference = fileReference; }
+ /** Initiate sending of this constant to some services over file distribution */
+ public void sendTo(Collection<? extends AbstractService> services) {
+ FileReference reference = (pathType == PathType.FILE)
+ ? FileSender.sendFileToServices(path, services)
+ : FileSender.sendUriToServices(path, services);
+ this.fileReference = reference.value();
+ }
+
+ public String getName() { return name; }
+ public String getFileName() { return path; }
+ public Path getFilePath() { return Path.fromString(path); }
+ public String getUri() { return path; }
+ public String getFileReference() { return fileReference; }
+
+ public void validate() {
+ if (path == null || path.isEmpty())
+ throw new IllegalArgumentException("Distributable resource must have a file or uri.");
+ }
+
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("resource '").append(name)
+ .append(pathType == PathType.FILE ? "' from file '" : " from uri ").append(path)
+ .append("' with ref '").append(fileReference)
+ .append("'");
+ return b.toString();
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentOnlySearch.java b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentOnlySearch.java
index 5940f908be8..6e17bd0f0bb 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentOnlySearch.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentOnlySearch.java
@@ -1,6 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition;
+import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.searchdefinition.document.SDDocumentType;
/**
@@ -11,8 +13,8 @@ import com.yahoo.searchdefinition.document.SDDocumentType;
*/
public class DocumentOnlySearch extends Search {
- public DocumentOnlySearch() {
- // empty
+ public DocumentOnlySearch(ApplicationPackage applicationPackage, DeployLogger deployLogger) {
+ super(applicationPackage, deployLogger);
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/ImmutableSearch.java b/config-model/src/main/java/com/yahoo/searchdefinition/ImmutableSearch.java
index 6ac73ad45a9..efd9ed5e88f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/ImmutableSearch.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/ImmutableSearch.java
@@ -28,6 +28,7 @@ public interface ImmutableSearch {
Reader getRankingExpression(String fileName);
ApplicationPackage applicationPackage();
RankingConstants rankingConstants();
+ RankExpressionFiles rankExpressionFiles();
OnnxModels onnxModels();
Stream<ImmutableSDField> allImportedFields();
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/OnnxModel.java b/config-model/src/main/java/com/yahoo/searchdefinition/OnnxModel.java
index 3e5726d6d94..3c42987512b 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/OnnxModel.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/OnnxModel.java
@@ -1,14 +1,9 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition;
-import com.yahoo.config.FileReference;
-import com.yahoo.path.Path;
import com.yahoo.tensor.TensorType;
-import com.yahoo.vespa.model.AbstractService;
import com.yahoo.vespa.model.ml.OnnxModelInfo;
-import com.yahoo.vespa.model.utils.FileSender;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -19,42 +14,26 @@ import java.util.Objects;
*
* @author lesters
*/
-public class OnnxModel {
+public class OnnxModel extends DistributableResource {
- public enum PathType {FILE, URI};
-
- private final String name;
- private PathType pathType = PathType.FILE;
- private String path = null;
- private String fileReference = "";
private OnnxModelInfo modelInfo = null;
private Map<String, String> inputMap = new HashMap<>();
private Map<String, String> outputMap = new HashMap<>();
public OnnxModel(String name) {
- this.name = name;
+ super(name);
}
public OnnxModel(String name, String fileName) {
- this(name);
- this.path = fileName;
+ super(name, fileName);
validate();
}
- public void setFileName(String fileName) {
- Objects.requireNonNull(fileName, "Filename cannot be null");
- this.path = fileName;
- this.pathType = PathType.FILE;
- }
-
+ @Override
public void setUri(String uri) {
throw new IllegalArgumentException("URI for ONNX models are not currently supported");
}
- public PathType getPathType() {
- return pathType;
- }
-
public void addInputNameMapping(String onnxName, String vespaName) {
addInputNameMapping(onnxName, vespaName, true);
}
@@ -90,20 +69,6 @@ public class OnnxModel {
this.modelInfo = modelInfo;
}
- /** Initiate sending of this constant to some services over file distribution */
- public void sendTo(Collection<? extends AbstractService> services) {
- FileReference reference = (pathType == OnnxModel.PathType.FILE)
- ? FileSender.sendFileToServices(path, services)
- : FileSender.sendUriToServices(path, services);
- this.fileReference = reference.value();
- }
-
- public String getName() { return name; }
- public String getFileName() { return path; }
- public Path getFilePath() { return Path.fromString(path); }
- public String getUri() { return path; }
- public String getFileReference() { return fileReference; }
-
public Map<String, String> getInputMap() { return Collections.unmodifiableMap(inputMap); }
public Map<String, String> getOutputMap() { return Collections.unmodifiableMap(outputMap); }
@@ -114,19 +79,4 @@ public class OnnxModel {
TensorType getTensorType(String onnxName, Map<String, TensorType> inputTypes) {
return modelInfo != null ? modelInfo.getTensorType(onnxName, inputTypes) : TensorType.empty;
}
-
- public void validate() {
- if (path == null || path.isEmpty())
- throw new IllegalArgumentException("ONNX models must have a file or uri.");
- }
-
- public String toString() {
- StringBuilder b = new StringBuilder();
- b.append("onnx-model '").append(name)
- .append(pathType == PathType.FILE ? "' from file '" : " from uri ").append(path)
- .append("' with ref '").append(fileReference)
- .append("'");
- return b.toString();
- }
-
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFile.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFile.java
new file mode 100644
index 00000000000..56385efeb0b
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFile.java
@@ -0,0 +1,35 @@
+package com.yahoo.searchdefinition;
+
+import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.vespa.model.AbstractService;
+import com.yahoo.vespa.model.utils.FileSender;
+
+import java.util.Collection;
+
+public class RankExpressionFile extends DistributableResource {
+
+ public RankExpressionFile(String name, String path) {
+ super(name, path);
+ validate();
+ }
+
+ @Override
+ public void sendTo(Collection<? extends AbstractService> services) {
+ /*
+ * TODO This is a very dirty hack due to using both SEARCH_DEFINITIONS_DIR and SCHEMA_DIR
+ * and doing so inconsistently, combined with using both fields from application package on disk and in zookeeper.
+ * The mess is spread out nicely, but ZookeeperClient, and writeSearchDefinitions and ZkApplicationPackage and FilesApplicationPackage
+ * should be consolidated
+ */
+ try {
+ setFileReference(FileSender.sendFileToServices(ApplicationPackage.SCHEMAS_DIR + "/" + getFileName(), services).value());
+ } catch (IllegalArgumentException e1) {
+ try {
+ setFileReference(FileSender.sendFileToServices(ApplicationPackage.SEARCH_DEFINITIONS_DIR + "/" + getFileName(), services).value());
+ } catch (IllegalArgumentException e2) {
+ throw new IllegalArgumentException("Failed to find expression file '" + getFileName() + "' in '"
+ + ApplicationPackage.SEARCH_DEFINITIONS_DIR + "' or '" + ApplicationPackage.SCHEMAS_DIR + "'.", e2);
+ }
+ }
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFiles.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFiles.java
new file mode 100644
index 00000000000..ebc91e0693f
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankExpressionFiles.java
@@ -0,0 +1,35 @@
+package com.yahoo.searchdefinition;
+
+import com.yahoo.vespa.model.AbstractService;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class RankExpressionFiles {
+ private final Map<String, RankExpressionFile> expressions = new HashMap<>();
+
+ public void add(RankExpressionFile expression) {
+ expression.validate();
+ String name = expression.getName();
+ if (expressions.containsKey(name))
+ throw new IllegalArgumentException("Rank expression file '" + name + "' defined twice");
+ expressions.put(name, expression);
+ }
+
+ /** Returns the ranking constant with the given name, or null if not present */
+ public RankExpressionFile get(String name) {
+ return expressions.get(name);
+ }
+
+ /** Returns a read-only map of the ranking constants in this indexed by name */
+ public Map<String, RankExpressionFile> asMap() {
+ return Collections.unmodifiableMap(expressions);
+ }
+
+ /** Initiate sending of these constants to some services over file distribution */
+ public void sendTo(Collection<? extends AbstractService> services) {
+ expressions.values().forEach(constant -> constant.sendTo(services));
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
index 95b291cf744..b0a7d2aaca2 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
@@ -52,6 +52,8 @@ import java.util.stream.Stream;
*/
public class RankProfile implements Cloneable {
+ public final static String FIRST_PHASE = "firstphase";
+ public final static String SECOND_PHASE = "secondphase";
/** The search definition-unique name of this rank profile */
private final String name;
@@ -76,6 +78,8 @@ public class RankProfile implements Cloneable {
/** The ranking expression to be used for second phase */
private RankingExpression secondPhaseRanking = null;
+ private Set<String> externalFileExpressions = new HashSet<>();
+
/** Number of hits to be reranked in second phase, -1 means use default */
private int rerankCount = -1;
@@ -165,6 +169,10 @@ public class RankProfile implements Cloneable {
return search != null ? search.rankingConstants() : model.rankingConstants();
}
+ public RankExpressionFiles rankExpressionFiles() {
+ return search != null ? search.rankExpressionFiles() : model.rankExpressionFiles();
+ }
+
public Map<String, OnnxModel> onnxModels() {
return search != null ? search.onnxModels().asMap() : onnxModels.asMap();
}
@@ -316,7 +324,7 @@ public class RankProfile implements Cloneable {
public void addConstant(String name, Value value) {
if (value instanceof TensorValue) {
- TensorType type = ((TensorValue)value).type();
+ TensorType type = value.type();
if (type.dimensions().stream().anyMatch(d -> d.isIndexed() && d.size().isEmpty()))
throw new IllegalArgumentException("Illegal type of constant " + name + " type " + type +
": Dense tensor dimensions must have a size");
@@ -370,9 +378,44 @@ public class RankProfile implements Cloneable {
this.firstPhaseRanking = rankingExpression;
}
+ public String getUniqueExpressionName(String name) {
+ return getName() + "_" + name;
+ }
+ public String getFirstPhaseFile() {
+ String name = FIRST_PHASE;
+ if (externalFileExpressions.contains(name)) {
+ return rankExpressionFiles().get(getUniqueExpressionName(name)).getFileName();
+ }
+ if ((firstPhaseRanking == null) && (getInherited() != null)) {
+ return getInherited().getFirstPhaseFile();
+ }
+ return null;
+ }
+
+ public String getSecondPhaseFile() {
+ String name = SECOND_PHASE;
+ if (externalFileExpressions.contains(name)) {
+ return rankExpressionFiles().get(getUniqueExpressionName(name)).getFileName();
+ }
+ if ((secondPhaseRanking == null) && (getInherited() != null)) {
+ return getInherited().getSecondPhaseFile();
+ }
+ return null;
+ }
+
+ public String getExpressionFile(String name) {
+ if (externalFileExpressions.contains(name)) {
+ return rankExpressionFiles().get(getUniqueExpressionName(name)).getFileName();
+ }
+ if (getInherited() != null) {
+ return getInherited().getExpressionFile(name);
+ }
+ return null;
+ }
+
public void setFirstPhaseRanking(String expression) {
try {
- this.firstPhaseRanking = parseRankingExpression("firstphase", expression);
+ this.firstPhaseRanking = parseRankingExpression(FIRST_PHASE, expression);
}
catch (ParseException e) {
throw new IllegalArgumentException("Illegal first phase ranking function", e);
@@ -389,13 +432,9 @@ public class RankProfile implements Cloneable {
return null;
}
- public void setSecondPhaseRanking(RankingExpression rankingExpression) {
- this.secondPhaseRanking = rankingExpression;
- }
-
public void setSecondPhaseRanking(String expression) {
try {
- this.secondPhaseRanking = parseRankingExpression("secondphase", expression);
+ this.secondPhaseRanking = parseRankingExpression(SECOND_PHASE, expression);
}
catch (ParseException e) {
throw new IllegalArgumentException("Illegal second phase ranking function", e);
@@ -436,7 +475,6 @@ public class RankProfile implements Cloneable {
* the final (with inheritance included) summary features of the given parent.
* The profile must be the profile which is directly inherited by this.
*
- * @param parentProfile
*/
public void setInheritedSummaryFeatures(String parentProfile) {
if ( ! parentProfile.equals(inheritedName))
@@ -496,12 +534,7 @@ public class RankProfile implements Cloneable {
private void addRankProperty(RankProperty rankProperty) {
// Just the usual multimap semantics here
- List<RankProperty> properties = rankProperties.get(rankProperty.getName());
- if (properties == null) {
- properties = new ArrayList<>(1);
- rankProperties.put(rankProperty.getName(), properties);
- }
- properties.add(rankProperty);
+ rankProperties.computeIfAbsent(rankProperty.getName(), (String key) -> new ArrayList<>(1)).add(rankProperty);
}
@Override
@@ -675,18 +708,27 @@ public class RankProfile implements Cloneable {
}
}
- private Reader openRankingExpressionReader(String expName, String expression) {
- if ( ! expression.startsWith("file:")) return new StringReader(expression);
-
+ private static String extractFileName(String expression) {
String fileName = expression.substring("file:".length()).trim();
if ( ! fileName.endsWith(ApplicationPackage.RANKEXPRESSION_NAME_SUFFIX))
fileName = fileName + ApplicationPackage.RANKEXPRESSION_NAME_SUFFIX;
+ return fileName;
+ }
+
+ private Reader openRankingExpressionReader(String expName, String expression) {
+ if ( ! expression.startsWith("file:")) return new StringReader(expression);
+
+ String fileName = extractFileName(expression);
File file = new File(fileName);
- if ( ! (file.isAbsolute()) && file.getPath().contains("/")) // See ticket 4102122
+ if ( ! file.isAbsolute() && file.getPath().contains("/")) // See ticket 4102122
throw new IllegalArgumentException("In " + getName() +", " + expName + ", ranking references file '" + file +
"' in subdirectory, which is not supported.");
+ /* TODO balder: Disabled until end-2-end verified
+ rankExpressionFiles().add(new RankExpressionFile(getUniqueExpressionName(expName), fileName));
+ externalFileExpressions.add(expName);
+ */
return search.getRankingExpression(fileName);
}
@@ -918,12 +960,12 @@ public class RankProfile implements Cloneable {
*/
public static class RankSetting implements Serializable {
- private String fieldName;
+ private final String fieldName;
- private Type type;
+ private final Type type;
/** The rank value */
- private Object value;
+ private final Object value;
public enum Type {
@@ -932,10 +974,10 @@ public class RankProfile implements Cloneable {
WEIGHT("weight"),
PREFERBITVECTOR("preferbitvector",true);
- private String name;
+ private final String name;
/** True if this setting really pertains to an index, not a field within an index */
- private boolean isIndexLevel;
+ private final boolean isIndexLevel;
Type(String name) {
this(name,false);
@@ -1008,8 +1050,8 @@ public class RankProfile implements Cloneable {
/** A rank property. Rank properties are Value Objects */
public static class RankProperty implements Serializable {
- private String name;
- private String value;
+ private final String name;
+ private final String value;
public RankProperty(String name, String value) {
this.name = name;
@@ -1084,7 +1126,6 @@ public class RankProfile implements Cloneable {
public void setMinGroups(int value) { minGroups = value; }
public void setCutoffFactor(double value) { cutoffFactor = value; }
public void setCutoffStrategy(Diversity.CutoffStrategy strategy) { cutoffStrategy = strategy; }
- public void setCutoffStrategy(String strategy) { cutoffStrategy = Diversity.CutoffStrategy.valueOf(strategy); }
public String getAttribute() { return attribute; }
public int getMinGroups() { return minGroups; }
public double getCutoffFactor() { return cutoffFactor; }
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java
index b41cf582204..8e376f13615 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java
@@ -1,13 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition;
-import com.yahoo.config.FileReference;
import com.yahoo.tensor.TensorType;
-import com.yahoo.vespa.model.AbstractService;
-import com.yahoo.vespa.model.utils.FileSender;
-
-import java.util.Collection;
-import java.util.Objects;
/**
* A global ranking constant distributed using file distribution.
@@ -17,81 +11,38 @@ import java.util.Objects;
* @author arnej
* @author bratseth
*/
-public class RankingConstant {
-
- public enum PathType {FILE, URI};
-
- /** The search definition-unique name of this constant */
- private final String name;
+public class RankingConstant extends DistributableResource {
private TensorType tensorType = null;
- private String path = null;
- private String fileReference = "";
-
- public PathType getPathType() {
- return pathType;
- }
-
- private PathType pathType = PathType.FILE;
public RankingConstant(String name) {
- this.name = name;
+ super(name);
}
public RankingConstant(String name, TensorType type, String fileName) {
- this(name);
+ super(name, fileName);
this.tensorType = type;
- this.path = fileName;
validate();
}
- public void setFileName(String fileName) {
- Objects.requireNonNull(fileName, "Filename cannot be null");
- this.path = fileName;
- this.pathType = PathType.FILE;
- }
-
- public void setUri(String uri) {
- Objects.requireNonNull(uri, "uri cannot be null");
- this.path = uri;
- this.pathType = PathType.URI;
- }
-
public void setType(TensorType type) {
this.tensorType = type;
}
- /** Initiate sending of this constant to some services over file distribution */
- public void sendTo(Collection<? extends AbstractService> services) {
- FileReference reference = (pathType == RankingConstant.PathType.FILE)
- ? FileSender.sendFileToServices(path, services)
- : FileSender.sendUriToServices(path, services);
- this.fileReference = reference.value();
- }
-
- public String getName() { return name; }
- public String getFileName() { return path; }
- public String getUri() { return path; }
- public String getFileReference() { return fileReference; }
public TensorType getTensorType() { return tensorType; }
public String getType() { return tensorType.toString(); }
public void validate() {
- if (path == null || path.isEmpty())
- throw new IllegalArgumentException("Ranking constants must have a file or uri.");
+ super.validate();
if (tensorType == null)
- throw new IllegalArgumentException("Ranking constant '" + name + "' must have a type.");
+ throw new IllegalArgumentException("Ranking constant '" + getName() + "' must have a type.");
if (tensorType.dimensions().stream().anyMatch(d -> d.isIndexed() && d.size().isEmpty()))
- throw new IllegalArgumentException("Illegal type in field " + name + " type " + tensorType +
+ throw new IllegalArgumentException("Illegal type in field " + getName() + " type " + tensorType +
": Dense tensor dimensions must have a size");
}
public String toString() {
- StringBuilder b = new StringBuilder();
- b.append("constant '").append(name)
- .append(pathType == PathType.FILE ? "' from file '" : " from uri ").append(path)
- .append("' with ref '").append(fileReference)
- .append("' of type '").append(tensorType)
- .append("'");
+ StringBuilder b = new StringBuilder(super.toString())
+ .append("' of type '").append(tensorType).append("'");
return b.toString();
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstants.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstants.java
index adefa5566ab..82381aa63fc 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstants.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstants.java
@@ -40,5 +40,4 @@ public class RankingConstants {
public void sendTo(Collection<? extends AbstractService> services) {
constants.values().forEach(constant -> constant.sendTo(services));
}
-
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/Search.java b/config-model/src/main/java/com/yahoo/searchdefinition/Search.java
index 9b7434dccab..b07740620f8 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/Search.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/Search.java
@@ -2,6 +2,8 @@
package com.yahoo.searchdefinition;
import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.document.Field;
import com.yahoo.searchdefinition.derived.SummaryClass;
import com.yahoo.searchdefinition.document.Attribute;
@@ -15,6 +17,7 @@ import com.yahoo.searchdefinition.document.TemporaryImportedFields;
import com.yahoo.searchdefinition.document.annotation.SDAnnotationType;
import com.yahoo.vespa.documentmodel.DocumentSummary;
import com.yahoo.vespa.documentmodel.SummaryField;
+import com.yahoo.vespa.model.AbstractService;
import java.io.Reader;
import java.util.ArrayList;
@@ -27,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
+import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
@@ -42,7 +46,6 @@ import java.util.stream.Stream;
// Ensure that after the processing step, all implicit instances of the above types are explicitly represented
public class Search implements ImmutableSearch {
- private static final Logger log = Logger.getLogger(Search.class.getName());
private static final String SD_DOC_FIELD_NAME = "sddocname";
private static final List<String> RESERVED_NAMES = Arrays.asList(
"index", "index_url", "summary", "attribute", "select_input", "host", SummaryClass.DOCUMENT_ID_FIELD,
@@ -59,7 +62,7 @@ public class Search implements ImmutableSearch {
private String name;
/** True if this doesn't define a search, just a document type */
- private boolean documentsOnly = false;
+ private final boolean documentsOnly;
private boolean rawAsBase64 = false;
@@ -78,6 +81,9 @@ public class Search implements ImmutableSearch {
/** The explicitly defined summaries of this search definition. _Must_ preserve order. */
private final Map<String, DocumentSummary> summaries = new LinkedHashMap<>();
+ /** External rank expression files of this */
+ private final RankExpressionFiles rankExpressionFiles = new RankExpressionFiles();
+
/** Ranking constants of this */
private final RankingConstants rankingConstants = new RankingConstants();
@@ -88,26 +94,33 @@ public class Search implements ImmutableSearch {
private Optional<ImportedFields> importedFields = Optional.empty();
private final ApplicationPackage applicationPackage;
+ private final DeployLogger deployLogger;
- /**
- * Creates a search definition which just holds a set of documents which should not (here, directly) be searchable
- */
- protected Search() {
- applicationPackage = null;
- documentsOnly = true;
+ /** Testin only */
+ public Search(String name) {
+ this(name, null, new BaseDeployLogger());
}
-
/**
* Creates a proper search definition
*
* @param name of the the searchdefinition
* @param applicationPackage the application containing this
*/
- public Search(String name, ApplicationPackage applicationPackage) {
- this.applicationPackage = applicationPackage;
+ public Search(String name, ApplicationPackage applicationPackage, DeployLogger deployLogger) {
+ this(applicationPackage, deployLogger, false);
this.name = name;
}
+ protected Search(ApplicationPackage applicationPackage, DeployLogger deployLogger) {
+ this(applicationPackage, deployLogger, true);
+ }
+
+ private Search(ApplicationPackage applicationPackage, DeployLogger deployLogger, boolean documentsOnly) {
+ this.applicationPackage = applicationPackage;
+ this.deployLogger = deployLogger;
+ this.documentsOnly = documentsOnly;
+ }
+
protected void setName(String name) {
this.name = name;
}
@@ -172,11 +185,20 @@ public class Search implements ImmutableSearch {
}
@Override
+ public RankExpressionFiles rankExpressionFiles() { return rankExpressionFiles; }
+
+ @Override
public RankingConstants rankingConstants() { return rankingConstants; }
@Override
public OnnxModels onnxModels() { return onnxModels; }
+ public void sendTo(Collection<? extends AbstractService> services) {
+ rankingConstants.sendTo(services);
+ rankExpressionFiles.sendTo(services);
+ onnxModels.sendTo(services);
+ }
+
public Optional<TemporaryImportedFields> temporaryImportedFields() {
return temporaryImportedFields;
}
@@ -310,7 +332,7 @@ public class Search implements ImmutableSearch {
*/
public void addExtraField(SDField field) {
if (fields.containsKey(field.getName())) {
- log.warning("Duplicate field " + field.getName() + " in search definition " + getName());
+ deployLogger.logApplicationPackage(Level.WARNING, "Duplicate field " + field.getName() + " in search definition " + getName());
} else {
field.setIsExtraField(true);
fields.put(field.getName(), field);
@@ -422,7 +444,7 @@ public class Search implements ImmutableSearch {
if (current.getRankType() != null &&
!consolidated.getRankType().equals(current.getRankType()))
{
- log.warning("Conflicting rank type settings for " +
+ deployLogger.logApplicationPackage(Level.WARNING, "Conflicting rank type settings for " +
first.getName() + " in " + this + ", using " +
consolidated.getRankType());
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
index 852fdc17726..fc110504f4d 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/SearchBuilder.java
@@ -41,53 +41,72 @@ import java.util.List;
public class SearchBuilder {
private final DocumentTypeManager docTypeMgr = new DocumentTypeManager();
- private List<Search> searchList = new LinkedList<>();
- private ApplicationPackage app;
- private boolean isBuilt = false;
- private DocumentModel model = new DocumentModel();
+ private final DocumentModel model = new DocumentModel();
+ private final ApplicationPackage app;
private final RankProfileRegistry rankProfileRegistry;
private final QueryProfileRegistry queryProfileRegistry;
+ private final DeployLogger deployLogger;
+ private List<Search> searchList = new LinkedList<>();
+ private boolean isBuilt = false;
/** True to build the document aspect only, skipping instantiation of rank profiles */
private final boolean documentsOnly;
/** For testing only */
public SearchBuilder() {
- this(MockApplicationPackage.createEmpty(), new RankProfileRegistry(), new QueryProfileRegistry());
+ this(new RankProfileRegistry(), new QueryProfileRegistry());
+ }
+
+ /** For testing only */
+ public SearchBuilder(DeployLogger deployLogger) {
+ this(MockApplicationPackage.createEmpty(), deployLogger);
+ }
+
+ /** For testing only */
+ public SearchBuilder(DeployLogger deployLogger, RankProfileRegistry rankProfileRegistry) {
+ this(MockApplicationPackage.createEmpty(), deployLogger, rankProfileRegistry);
}
/** Used for generating documents for typed access to document fields in Java */
public SearchBuilder(boolean documentsOnly) {
- this(MockApplicationPackage.createEmpty(), new RankProfileRegistry(), new QueryProfileRegistry(), documentsOnly);
+ this(MockApplicationPackage.createEmpty(), new BaseDeployLogger(), new RankProfileRegistry(), new QueryProfileRegistry(), documentsOnly);
}
/** For testing only */
- public SearchBuilder(ApplicationPackage app) {
- this(app, new RankProfileRegistry(), new QueryProfileRegistry());
+ public SearchBuilder(ApplicationPackage app, DeployLogger deployLogger) {
+ this(app, deployLogger, new RankProfileRegistry(), new QueryProfileRegistry());
+ }
+
+ /** For testing only */
+ public SearchBuilder(ApplicationPackage app, DeployLogger deployLogger, RankProfileRegistry rankProfileRegistry) {
+ this(app, deployLogger, rankProfileRegistry, new QueryProfileRegistry());
}
/** For testing only */
public SearchBuilder(RankProfileRegistry rankProfileRegistry) {
- this(MockApplicationPackage.createEmpty(), rankProfileRegistry, new QueryProfileRegistry());
+ this(rankProfileRegistry, new QueryProfileRegistry());
}
/** For testing only */
public SearchBuilder(RankProfileRegistry rankProfileRegistry, QueryProfileRegistry queryProfileRegistry) {
- this(MockApplicationPackage.createEmpty(), rankProfileRegistry, queryProfileRegistry);
+ this(MockApplicationPackage.createEmpty(), new BaseDeployLogger(), rankProfileRegistry, queryProfileRegistry);
}
public SearchBuilder(ApplicationPackage app,
+ DeployLogger deployLogger,
RankProfileRegistry rankProfileRegistry,
QueryProfileRegistry queryProfileRegistry) {
- this(app, rankProfileRegistry, queryProfileRegistry, false);
+ this(app, deployLogger, rankProfileRegistry, queryProfileRegistry, false);
}
- public SearchBuilder(ApplicationPackage app,
- RankProfileRegistry rankProfileRegistry,
- QueryProfileRegistry queryProfileRegistry,
- boolean documentsOnly) {
+ private SearchBuilder(ApplicationPackage app,
+ DeployLogger deployLogger,
+ RankProfileRegistry rankProfileRegistry,
+ QueryProfileRegistry queryProfileRegistry,
+ boolean documentsOnly) {
this.app = app;
this.rankProfileRegistry = rankProfileRegistry;
this.queryProfileRegistry = queryProfileRegistry;
+ this.deployLogger = deployLogger;
this.documentsOnly = documentsOnly;
}
@@ -95,29 +114,17 @@ public class SearchBuilder {
* Import search definition.
*
* @param fileName The name of the file to import.
- * @param deployLogger Logger for deploy messages.
* @return The name of the imported object.
* @throws IOException Thrown if the file can not be read for some reason.
* @throws ParseException Thrown if the file does not contain a valid search definition. ```
*/
- public String importFile(String fileName, DeployLogger deployLogger) throws IOException, ParseException {
+ public String importFile(String fileName) throws IOException, ParseException {
File file = new File(fileName);
- return importString(IOUtils.readFile(file), file.getAbsoluteFile().getParent(), deployLogger);
+ return importString(IOUtils.readFile(file), file.getAbsoluteFile().getParent());
}
- /**
- * Import search definition.
- *
- * @param fileName The name of the file to import.
- * @return The name of the imported object.
- * @throws IOException Thrown if the file can not be read for some reason.
- * @throws ParseException Thrown if the file does not contain a valid search definition.
- */
- public String importFile(String fileName) throws IOException, ParseException {
- return importFile(fileName, new BaseDeployLogger());
- }
- public String importFile(Path file) throws IOException, ParseException {
- return importFile(file.toString(), new BaseDeployLogger());
+ private String importFile(Path file) throws IOException, ParseException {
+ return importFile(file.toString());
}
/**
@@ -129,17 +136,8 @@ public class SearchBuilder {
* @return The name of the imported object.
* @throws ParseException Thrown if the file does not contain a valid search definition.
*/
- public String importReader(NamedReader reader, String searchDefDir, DeployLogger deployLogger) throws IOException, ParseException {
- return importString(IOUtils.readAll(reader), searchDefDir, deployLogger);
- }
-
- /**
- * See #{@link #importReader}
- *
- * Convenience, should only be used for testing as logs will be swallowed.
- */
public String importReader(NamedReader reader, String searchDefDir) throws IOException, ParseException {
- return importString(IOUtils.readAll(reader), searchDefDir, new BaseDeployLogger());
+ return importString(IOUtils.readAll(reader), searchDefDir);
}
/**
@@ -150,21 +148,10 @@ public class SearchBuilder {
* @throws ParseException thrown if the file does not contain a valid search definition.
*/
public String importString(String str) throws ParseException {
- return importString(str, null, new BaseDeployLogger());
- }
-
- /**
- * Import search definition.
- *
- * @param str the string to parse.
- * @return the name of the imported object.
- * @throws ParseException thrown if the file does not contain a valid search definition.
- */
- public String importString(String str, DeployLogger logger) throws ParseException {
- return importString(str, null, logger);
+ return importString(str, null);
}
- private String importString(String str, String searchDefDir, DeployLogger deployLogger) throws ParseException {
+ private String importString(String str, String searchDefDir) throws ParseException {
SimpleCharStream stream = new SimpleCharStream(str);
try {
return importRawSearch(new SDParser(stream, deployLogger, app, rankProfileRegistry, documentsOnly)
@@ -208,7 +195,7 @@ public class SearchBuilder {
* @throws IllegalStateException Thrown if this method has already been called.
*/
public void build() {
- build(true, new BaseDeployLogger());
+ build(true);
}
/**
@@ -216,9 +203,8 @@ public class SearchBuilder {
* #getSearch(String)} method.
*
* @throws IllegalStateException Thrown if this method has already been called.
- * @param deployLogger The logger to use during build
*/
- public void build(boolean validate, DeployLogger deployLogger) {
+ public void build(boolean validate) {
if (isBuilt) throw new IllegalStateException("Model already built");
List<Search> built = new ArrayList<>();
@@ -325,9 +311,9 @@ public class SearchBuilder {
}
public static SearchBuilder createFromString(String sd, DeployLogger logger) throws ParseException {
- SearchBuilder builder = new SearchBuilder(MockApplicationPackage.createEmpty());
- builder.importString(sd, logger);
- builder.build(true, logger);
+ SearchBuilder builder = new SearchBuilder(logger);
+ builder.importString(sd);
+ builder.build(true);
return builder;
}
@@ -386,12 +372,13 @@ public class SearchBuilder {
QueryProfileRegistry queryprofileRegistry)
throws IOException, ParseException {
SearchBuilder builder = new SearchBuilder(MockApplicationPackage.createEmpty(),
+ deployLogger,
rankProfileRegistry,
queryprofileRegistry);
for (String fileName : fileNames) {
builder.importFile(fileName);
}
- builder.build(true, deployLogger);
+ builder.build(true);
return builder;
}
@@ -413,15 +400,16 @@ public class SearchBuilder {
public static SearchBuilder createFromDirectory(String dir,
RankProfileRegistry rankProfileRegistry,
QueryProfileRegistry queryProfileRegistry,
- DeployLogger logger,
+ DeployLogger deployLogger,
ApplicationPackage applicationPackage) throws IOException, ParseException {
SearchBuilder builder = new SearchBuilder(applicationPackage,
+ deployLogger,
rankProfileRegistry,
queryProfileRegistry);
for (Iterator<Path> i = Files.list(new File(dir).toPath()).filter(p -> p.getFileName().toString().endsWith(".sd")).iterator(); i.hasNext(); ) {
builder.importFile(i.next());
}
- builder.build(true, logger);
+ builder.build(true);
return builder;
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
index 4d6ce783947..52d99c523ea 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
@@ -85,7 +85,8 @@ public class DerivedConfiguration {
summaries = new Summaries(search, deployLogger);
summaryMap = new SummaryMap(search);
juniperrc = new Juniperrc(search);
- rankProfileList = new RankProfileList(search, search.rankingConstants(), attributeFields, rankProfileRegistry, queryProfiles, importedModels, deployProperties);
+ rankProfileList = new RankProfileList(search, search.rankingConstants(), search.rankExpressionFiles(), attributeFields,
+ rankProfileRegistry, queryProfiles, importedModels, deployProperties);
indexingScript = new IndexingScript(search);
indexInfo = new IndexInfo(search);
indexSchema = new IndexSchema(search);
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java
index 42fa1df802b..d414b9ed79f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java
@@ -6,6 +6,8 @@ import com.yahoo.config.model.api.ModelContext;
import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.searchdefinition.OnnxModel;
import com.yahoo.searchdefinition.OnnxModels;
+import com.yahoo.searchdefinition.RankExpressionFile;
+import com.yahoo.searchdefinition.RankExpressionFiles;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.RankingConstant;
import com.yahoo.searchdefinition.RankingConstants;
@@ -14,6 +16,7 @@ import com.yahoo.searchdefinition.RankProfile;
import com.yahoo.searchdefinition.Search;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.vespa.model.AbstractService;
import java.util.Collection;
@@ -25,21 +28,21 @@ import java.util.logging.Logger;
*
* @author bratseth
*/
-public class RankProfileList extends Derived implements RankProfilesConfig.Producer,
- RankingConstantsConfig.Producer,
- OnnxModelsConfig.Producer {
+public class RankProfileList extends Derived implements RankProfilesConfig.Producer {
private static final Logger log = Logger.getLogger(RankProfileList.class.getName());
private final Map<String, RawRankProfile> rankProfiles = new java.util.LinkedHashMap<>();
private final RankingConstants rankingConstants;
+ private final RankExpressionFiles rankExpressionFiles;
private final OnnxModels onnxModels;
public static RankProfileList empty = new RankProfileList();
private RankProfileList() {
- this.rankingConstants = new RankingConstants();
- this.onnxModels = new OnnxModels();
+ rankingConstants = new RankingConstants();
+ rankExpressionFiles = new RankExpressionFiles();
+ onnxModels = new OnnxModels();
}
/**
@@ -50,6 +53,7 @@ public class RankProfileList extends Derived implements RankProfilesConfig.Produ
*/
public RankProfileList(Search search,
RankingConstants rankingConstants,
+ RankExpressionFiles rankExpressionFiles,
AttributeFields attributeFields,
RankProfileRegistry rankProfileRegistry,
QueryProfileRegistry queryProfiles,
@@ -57,7 +61,8 @@ public class RankProfileList extends Derived implements RankProfilesConfig.Produ
ModelContext.Properties deployProperties) {
setName(search == null ? "default" : search.getName());
this.rankingConstants = rankingConstants;
- this.onnxModels = search == null ? new OnnxModels() : search.onnxModels(); // as ONNX models come from parsing rank expressions
+ this.rankExpressionFiles = rankExpressionFiles;
+ onnxModels = search == null ? new OnnxModels() : search.onnxModels(); // as ONNX models come from parsing rank expressions
deriveRankProfiles(rankProfileRegistry, queryProfiles, importedModels, search, attributeFields, deployProperties);
}
@@ -93,11 +98,9 @@ public class RankProfileList extends Derived implements RankProfilesConfig.Produ
return rankProfiles.get(name);
}
- public void sendConstantsTo(Collection<? extends AbstractService> services) {
+ public void sendTo(Collection<? extends AbstractService> services) {
rankingConstants.sendTo(services);
- }
-
- public void sendOnnxModelsTo(Collection<? extends AbstractService> services) {
+ rankExpressionFiles.sendTo(services);
onnxModels.sendTo(services);
}
@@ -111,7 +114,10 @@ public class RankProfileList extends Derived implements RankProfilesConfig.Produ
}
}
- @Override
+ public void getConfig(RankingExpressionsConfig.Builder builder) {
+ rankExpressionFiles.asMap().values().forEach((expr) -> builder.expression.add(new RankingExpressionsConfig.Expression.Builder().name(expr.getName()).fileref(expr.getFileReference())));
+ }
+
public void getConfig(RankingConstantsConfig.Builder builder) {
for (RankingConstant constant : rankingConstants.asMap().values()) {
if ("".equals(constant.getFileReference()))
@@ -124,7 +130,6 @@ public class RankProfileList extends Derived implements RankProfilesConfig.Produ
}
}
- @Override
public void getConfig(OnnxModelsConfig.Builder builder) {
for (OnnxModel model : onnxModels.asMap().values()) {
if ("".equals(model.getFileReference()))
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/RawRankProfile.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/RawRankProfile.java
index c656a426b61..41ac1e17d93 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/RawRankProfile.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/RawRankProfile.java
@@ -125,8 +125,8 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
*/
private Map<String, FieldRankSettings> fieldRankSettings = new java.util.LinkedHashMap<>();
+ private final RankProfile rankProfile;
private RankingExpression firstPhaseRanking = null;
-
private RankingExpression secondPhaseRanking = null;
private Set<ReferenceNode> summaryFeatures = new LinkedHashSet<>();
@@ -159,6 +159,7 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
private final Map<String, String> attributeTypes;
private final Map<String, String> queryFeatureTypes;
+ private final boolean useExternalExpressionFiles;
private Set<String> filterFields = new java.util.LinkedHashSet<>();
@@ -166,11 +167,13 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
* Creates a raw rank profile from the given rank profile
*/
Deriver(RankProfile rankProfile, QueryProfileRegistry queryProfiles, ImportedMlModels importedModels,
- AttributeFields attributeFields, ModelContext.Properties deployProperties)
+ AttributeFields attributeFields, ModelContext.Properties deployProperties)
{
+ this.rankProfile = rankProfile;
RankProfile compiled = rankProfile.compile(queryProfiles, importedModels);
attributeTypes = compiled.getAttributeTypes();
queryFeatureTypes = compiled.getQueryFeatureTypes();
+ useExternalExpressionFiles = deployProperties.featureFlags().useExternalRankExpressions();
deriveRankingFeatures(compiled, deployProperties);
deriveRankTypeSetting(compiled, attributeFields);
deriveFilterFields(compiled);
@@ -230,13 +233,13 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
Map<String, String> functionProperties) {
SerializationContext context = new SerializationContext(functionExpressions, null, functionProperties);
for (Map.Entry<String, RankProfile.RankingExpressionFunction> e : functions.entrySet()) {
+ if (useExternalExpressionFiles && rankProfile.getExpressionFile(e.getKey()) != null) continue;
String propertyName = RankingExpression.propertyName(e.getKey());
- if (context.serializedFunctions().containsKey(propertyName)) {
- continue;
- }
+ if (context.serializedFunctions().containsKey(propertyName)) continue;
+
String expressionString = e.getValue().function().getBody().getRoot().toString(new StringBuilder(), context, null, null).toString();
- context.addFunctionSerialization(RankingExpression.propertyName(e.getKey()), expressionString);
+ context.addFunctionSerialization(propertyName, expressionString);
for (Map.Entry<String, TensorType> argumentType : e.getValue().function().argumentTypes().entrySet())
context.addArgumentTypeSerialization(e.getKey(), argumentType.getKey(), argumentType.getValue());
if (e.getValue().function().returnType().isPresent())
@@ -333,7 +336,7 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
public List<Pair<String, String>> derive() {
List<Pair<String, String>> properties = new ArrayList<>();
for (RankProfile.RankProperty property : rankProperties) {
- if ("rankingExpression(firstphase).rankingScript".equals(property.getName())) {
+ if (("rankingExpression(" + RankProfile.FIRST_PHASE + ").rankingScript").equals(property.getName())) {
// Could have been set by function expansion. Set expressions, then skip this property.
try {
firstPhaseRanking = new RankingExpression(property.getValue());
@@ -341,7 +344,7 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
throw new IllegalArgumentException("Could not parse first phase expression", e);
}
}
- else if ("rankingExpression(secondphase).rankingScript".equals(property.getName())) {
+ else if (("rankingExpression(" + RankProfile.SECOND_PHASE + ").rankingScript").equals(property.getName())) {
try {
secondPhaseRanking = new RankingExpression(property.getValue());
} catch (ParseException e) {
@@ -352,8 +355,8 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
properties.add(new Pair<>(property.getName(), property.getValue()));
}
}
- properties.addAll(deriveRankingPhaseRankProperties(firstPhaseRanking, "firstphase"));
- properties.addAll(deriveRankingPhaseRankProperties(secondPhaseRanking, "secondphase"));
+ properties.addAll(deriveRankingPhaseRankProperties(firstPhaseRanking, rankProfile.getFirstPhaseFile(), RankProfile.FIRST_PHASE));
+ properties.addAll(deriveRankingPhaseRankProperties(secondPhaseRanking, rankProfile.getSecondPhaseFile(), RankProfile.SECOND_PHASE));
for (FieldRankSettings settings : fieldRankSettings.values()) {
properties.addAll(settings.deriveRankProperties());
}
@@ -420,7 +423,7 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
return properties;
}
- private List<Pair<String, String>> deriveRankingPhaseRankProperties(RankingExpression expression, String phase) {
+ private List<Pair<String, String>> deriveRankingPhaseRankProperties(RankingExpression expression, String fileName, String phase) {
List<Pair<String, String>> properties = new ArrayList<>();
if (expression == null) return properties;
@@ -428,7 +431,9 @@ public class RawRankProfile implements RankProfilesConfig.Producer {
if ("".equals(name))
name = phase;
- if (expression.getRoot() instanceof ReferenceNode) {
+ if (useExternalExpressionFiles && (fileName != null)) {
+ properties.add(new Pair<>("vespa.rank." + phase, "rankingExpression(" + rankProfile.getUniqueExpressionName(name) + ")"));
+ } else if (expression.getRoot() instanceof ReferenceNode) {
properties.add(new Pair<>("vespa.rank." + phase, expression.getRoot().toString()));
} else {
properties.add(new Pair<>("vespa.rank." + phase, "rankingExpression(" + name + ")"));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java b/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
index 6e16b1a120e..8dea1b65079 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/AbstractService.java
@@ -92,21 +92,30 @@ public abstract class AbstractService extends AbstractConfigProducer<AbstractCon
* Preferred constructor when building from XML. Use this if you are building
* in doBuild() in an AbstractConfigProducerBuilder.
* build() will call initService() in that case, after setting hostalias and baseport.
- * @param parent Parent config producer in the model tree.
- * @param name Name of this service.
+ *
+ * @param parent the parent config producer in the model tree
+ * @param name the name of this service
*/
- public AbstractService(AbstractConfigProducer parent, String name) {
+ public AbstractService(AbstractConfigProducer<?> parent, String name) {
super(parent, name);
}
/**
* Only used for testing. Stay away.
- * @param name Name of this service.
+ *
+ * @param name the name of this service.
*/
public AbstractService(String name) {
super(name);
}
+ @Override
+ public void remove() {
+ super.remove();
+ if (hostResource != null)
+ hostResource.deallocateService(this);
+ }
+
/**
* Distribute affinity on a collection of services. Services that are located on the same host
* will be assigned a specific cpu socket on that host.
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java b/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java
index a80982fe75b..b6a9d2bf97a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/HostPorts.java
@@ -6,9 +6,12 @@ import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.provision.NetworkPorts;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
@@ -36,6 +39,7 @@ public class HostPorts {
private PortFinder portFinder = new PortFinder(Collections.emptyList());
+ private boolean flushed = false;
private Optional<NetworkPorts> networkPortsList = Optional.empty();
public HostPorts(String hostname) {
@@ -143,8 +147,16 @@ public class HostPorts {
return allocator.result();
}
+ void deallocatePorts(NetworkPortRequestor service) {
+ if (flushed)
+ throw new IllegalStateException("Cannot deallocate ports after calling flushPortReservations()");
+ portDB.entrySet().removeIf(entry -> entry.getValue().getServiceName().equals(service.getServiceName()));
+ allocatedPorts--;
+ }
+
public void flushPortReservations() {
this.networkPortsList = Optional.of(new NetworkPorts(portFinder.allocations()));
+ this.flushed = true;
}
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java b/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java
index 3bc07db9507..ef041d06978 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java
@@ -79,6 +79,11 @@ public class HostResource implements Comparable<HostResource> {
return ports;
}
+ void deallocateService(AbstractService service) {
+ hostPorts.deallocatePorts(service);
+ services.remove(service.getServiceName());
+ }
+
/**
* Returns the service with the given "sentinel name" on this Host,
* or null if the name does not match any service.
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
index 4dc38c09ab1..ab00e9d295f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
@@ -33,6 +33,7 @@ import com.yahoo.container.QrConfig;
import com.yahoo.path.Path;
import com.yahoo.searchdefinition.OnnxModel;
import com.yahoo.searchdefinition.OnnxModels;
+import com.yahoo.searchdefinition.RankExpressionFiles;
import com.yahoo.searchdefinition.RankProfile;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.RankingConstants;
@@ -129,6 +130,9 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
/** The global ranking constants of this model */
private final RankingConstants rankingConstants = new RankingConstants();
+ /** External rank expression files of this */
+ private final RankExpressionFiles rankExpressionFiles = new RankExpressionFiles();
+
/** The validation overrides of this. This is never null. */
private final ValidationOverrides validationOverrides;
@@ -181,15 +185,17 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
createGlobalRankProfiles(deployState.getDeployLogger(), deployState.getImportedModels(),
deployState.rankProfileRegistry(), deployState.getQueryProfiles());
- this.rankProfileList = new RankProfileList(null, // null search -> global
- rankingConstants, AttributeFields.empty,
- deployState.rankProfileRegistry(),
- deployState.getQueryProfiles().getRegistry(),
- deployState.getImportedModels(),
- deployState.getProperties());
+ rankProfileList = new RankProfileList(null, // null search -> global
+ rankingConstants,
+ rankExpressionFiles,
+ AttributeFields.empty,
+ deployState.rankProfileRegistry(),
+ deployState.getQueryProfiles().getRegistry(),
+ deployState.getImportedModels(),
+ deployState.getProperties());
HostSystem hostSystem = root.hostSystem();
- if (complete) { // create a a completed, frozen model
+ if (complete) { // create a completed, frozen model
configModelRepo.readConfigModels(deployState, this, builder, root, configModelRegistry);
addServiceClusters(deployState, builder);
setupRouting(deployState);
@@ -260,6 +266,8 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
/** Returns the global ranking constants of this */
public RankingConstants rankingConstants() { return rankingConstants; }
+ public RankExpressionFiles rankExpressionFiles() { return rankExpressionFiles; }
+
/** Creates a mutable model with no services instantiated */
public static VespaModel createIncomplete(DeployState deployState) throws IOException, SAXException {
return new VespaModel(new NullConfigModelRegistry(), deployState, false,
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
index d1b9bbb4e58..e080ce43730 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java
@@ -148,15 +148,23 @@ public class Admin extends AbstractConfigProducer<Admin> implements Serializable
public void setClusterControllers(ClusterControllerContainerCluster clusterControllers, DeployLogger deployLogger) {
this.clusterControllers = clusterControllers;
- if (isHostedVespa)
+ if (isHostedVespa) {
+ // Prefer to put Slobroks on the admin cluster running cluster controllers to avoid unnecessary
+ // movement of the slobroks when there are changes to the content cluster nodes
+ removeSlobroks();
addSlobroks(createSlobroksOn(clusterControllers, deployLogger));
+ }
+ }
+
+ private void removeSlobroks() {
+ slobroks.forEach(Slobrok::remove);
+ slobroks.clear();
}
private List<Slobrok> createSlobroksOn(ClusterControllerContainerCluster clusterControllers, DeployLogger deployLogger) {
List<Slobrok> slobroks = new ArrayList<>();
- int index = this.slobroks.size();
for (ClusterControllerContainer clusterController : clusterControllers.getContainers()) {
- Slobrok slobrok = new Slobrok(this, index++);
+ Slobrok slobrok = new Slobrok(this, clusterController.index());
slobrok.setHostResource(clusterController.getHostResource());
slobroks.add(slobrok);
slobrok.initService(deployLogger);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java
index eed886b707f..00b6d872ddd 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/Slobrok.java
@@ -9,9 +9,10 @@ import com.yahoo.vespa.model.PortAllocBridge;
/**
* Represents a Slobrok service.
*
- * @author gjoranv
+ * @author gjoranv
*/
public class Slobrok extends AbstractService implements StateserverConfig.Producer {
+
private static final long serialVersionUID = 1L;
public final static int BASEPORT = 19099;
@@ -25,7 +26,7 @@ public class Slobrok extends AbstractService implements StateserverConfig.Produc
* @param parent the parent ConfigProducer.
* @param index unique index for all slobroks
*/
- public Slobrok(AbstractConfigProducer parent, int index) {
+ public Slobrok(AbstractConfigProducer<?> parent, int index) {
super(parent, "slobrok." + index);
portsMeta.on(0).tag("rpc").tag("admin").tag("status");
portsMeta.on(1).tag("http").tag("state");
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java
index 7049213ddab..d87c6596fa4 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/RankSetupValidator.java
@@ -8,6 +8,8 @@ import com.yahoo.log.InvalidLogFormatException;
import com.yahoo.log.LogMessage;
import com.yahoo.path.Path;
import com.yahoo.searchdefinition.OnnxModel;
+import com.yahoo.searchdefinition.RankExpressionFile;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.yolean.Exceptions;
import com.yahoo.system.ProcessExecuter;
@@ -38,7 +40,6 @@ import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
/**
* Validate rank setup for all search clusters (rank-profiles, index-schema, attributes configs), validating done
@@ -116,50 +117,61 @@ public class RankSetupValidator extends Validator {
}
private void writeConfigs(String dir, AbstractConfigProducer<?> producer) throws IOException {
- RankProfilesConfig.Builder rpcb = new RankProfilesConfig.Builder();
- ((RankProfilesConfig.Producer) producer).getConfig(rpcb);
- RankProfilesConfig rpc = new RankProfilesConfig(rpcb);
- writeConfig(dir, RankProfilesConfig.getDefName() + ".cfg", rpc);
-
- IndexschemaConfig.Builder iscb = new IndexschemaConfig.Builder();
- ((IndexschemaConfig.Producer) producer).getConfig(iscb);
- IndexschemaConfig isc = new IndexschemaConfig(iscb);
- writeConfig(dir, IndexschemaConfig.getDefName() + ".cfg", isc);
-
- AttributesConfig.Builder acb = new AttributesConfig.Builder();
- ((AttributesConfig.Producer) producer).getConfig(acb);
- AttributesConfig ac = new AttributesConfig(acb);
- writeConfig(dir, AttributesConfig.getDefName() + ".cfg", ac);
-
- RankingConstantsConfig.Builder rccb = new RankingConstantsConfig.Builder();
- ((RankingConstantsConfig.Producer) producer).getConfig(rccb);
- RankingConstantsConfig rcc = new RankingConstantsConfig(rccb);
- writeConfig(dir, RankingConstantsConfig.getDefName() + ".cfg", rcc);
-
- OnnxModelsConfig.Builder omcb = new OnnxModelsConfig.Builder();
- ((OnnxModelsConfig.Producer) producer).getConfig(omcb);
- OnnxModelsConfig omc = new OnnxModelsConfig(omcb);
- writeConfig(dir, OnnxModelsConfig.getDefName() + ".cfg", omc);
-
- ImportedFieldsConfig.Builder ifcb = new ImportedFieldsConfig.Builder();
- ((ImportedFieldsConfig.Producer) producer).getConfig(ifcb);
- ImportedFieldsConfig ifc = new ImportedFieldsConfig(ifcb);
- writeConfig(dir, ImportedFieldsConfig.getDefName() + ".cfg", ifc);
+ RankProfilesConfig.Builder rpcb = new RankProfilesConfig.Builder();
+ ((RankProfilesConfig.Producer) producer).getConfig(rpcb);
+ RankProfilesConfig rpc = new RankProfilesConfig(rpcb);
+ writeConfig(dir, RankProfilesConfig.getDefName() + ".cfg", rpc);
+
+ IndexschemaConfig.Builder iscb = new IndexschemaConfig.Builder();
+ ((IndexschemaConfig.Producer) producer).getConfig(iscb);
+ IndexschemaConfig isc = new IndexschemaConfig(iscb);
+ writeConfig(dir, IndexschemaConfig.getDefName() + ".cfg", isc);
+
+ AttributesConfig.Builder acb = new AttributesConfig.Builder();
+ ((AttributesConfig.Producer) producer).getConfig(acb);
+ AttributesConfig ac = new AttributesConfig(acb);
+ writeConfig(dir, AttributesConfig.getDefName() + ".cfg", ac);
+
+ RankingConstantsConfig.Builder rccb = new RankingConstantsConfig.Builder();
+ ((RankingConstantsConfig.Producer) producer).getConfig(rccb);
+ RankingConstantsConfig rcc = new RankingConstantsConfig(rccb);
+ writeConfig(dir, RankingConstantsConfig.getDefName() + ".cfg", rcc);
+
+ RankingExpressionsConfig.Builder recb = new RankingExpressionsConfig.Builder();
+ ((RankingExpressionsConfig.Producer) producer).getConfig(recb);
+ RankingExpressionsConfig rec = new RankingExpressionsConfig(recb);
+ writeConfig(dir, RankingExpressionsConfig.getDefName() + ".cfg", rec);
+
+ OnnxModelsConfig.Builder omcb = new OnnxModelsConfig.Builder();
+ ((OnnxModelsConfig.Producer) producer).getConfig(omcb);
+ OnnxModelsConfig omc = new OnnxModelsConfig(omcb);
+ writeConfig(dir, OnnxModelsConfig.getDefName() + ".cfg", omc);
+
+ ImportedFieldsConfig.Builder ifcb = new ImportedFieldsConfig.Builder();
+ ((ImportedFieldsConfig.Producer) producer).getConfig(ifcb);
+ ImportedFieldsConfig ifc = new ImportedFieldsConfig(ifcb);
+ writeConfig(dir, ImportedFieldsConfig.getDefName() + ".cfg", ifc);
}
private void writeExtraVerifyRanksetupConfig(String dir, DocumentDatabase db) throws IOException {
String configName = "verify-ranksetup.cfg";
String configContent = "";
+ List<String> config = new ArrayList<>();
// Assist verify-ranksetup in finding the actual ONNX model files
- Map<String, OnnxModel> models = db.getDerivedConfiguration().getSearch().onnxModels().asMap();
- if (models.values().size() > 0) {
- List<String> config = new ArrayList<>(models.values().size() * 2);
- for (OnnxModel model : models.values()) {
- String modelPath = getFileRepositoryPath(model.getFilePath(), model.getFileReference());
- config.add(String.format("file[%d].ref \"%s\"", config.size() / 2, model.getFileReference()));
- config.add(String.format("file[%d].path \"%s\"", config.size() / 2, modelPath));
- }
+ for (OnnxModel model : db.getDerivedConfiguration().getSearch().onnxModels().asMap().values()) {
+ String modelPath = getFileRepositoryPath(model.getFilePath(), model.getFileReference());
+ config.add(String.format("file[%d].ref \"%s\"", config.size() / 2, model.getFileReference()));
+ config.add(String.format("file[%d].path \"%s\"", config.size() / 2, modelPath));
+ }
+
+ for (RankExpressionFile expr : db.getDerivedConfiguration().getSearch().rankExpressionFiles().asMap().values()) {
+ String modelPath = getFileRepositoryPath(expr.getFilePath(), expr.getFileReference());
+ config.add(String.format("file[%d].ref \"%s\"", config.size() / 2, expr.getFileReference()));
+ config.add(String.format("file[%d].path \"%s\"", config.size() / 2, modelPath));
+ }
+
+ if ( ! config.isEmpty() ) {
configContent = StringUtilities.implodeMultiline(config);
}
IOUtils.writeFile(dir + configName, configContent, false);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
index a0673824907..bbec64d45fb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomAdminV4Builder.java
@@ -67,7 +67,7 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
admin,
allocateHosts(admin.hostSystem(), "slobroks", nodesSpecification));
}
- else { // TODO: Remove
+ else { // These will be removed later, if an admin cluster (for cluster controllers) is assigned
createSlobroks(deployLogger,
admin,
pickContainerHostsForSlobrok(nodesSpecification.minResources().nodes(), 2));
@@ -153,8 +153,8 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
List<HostResource> picked = sortedContainerHostsFrom(model, count, !retired);
// if we can return multiple hosts, include retired nodes which would have been picked before
- // (probably - assuming all previous nodes were retired, which is always true for a single cluster
- // at the moment (Sept 2015)) to ensure a smoother transition between the old and new topology
+ // (probably - assuming all previous nodes were retired, which is always true for a single cluster,
+ // to ensure a smoother transition between the old and new topology
// by including both new and old nodes during the retirement period
picked.addAll(sortedContainerHostsFrom(model, count, retired));
@@ -166,6 +166,7 @@ public class DomAdminV4Builder extends DomAdminBuilderBase {
List<HostResource> hosts = model.getCluster().getContainers().stream()
.filter(container -> retired == container.isRetired())
.map(Container::getHostResource)
+ .sorted(HostResource::comparePrimarilyByIndexTo)
.collect(Collectors.toList());
return hosts.subList(0, Math.min(count, hosts.size()));
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java
index 3a4e8a70613..36bf792ee82 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/DomFederationSearcherBuilder.java
@@ -43,7 +43,6 @@ public class DomFederationSearcherBuilder extends VespaDomBuilder.DomConfigProdu
return XML.getChild(searcherSpec, "source-set") != null;
}
-
private List<FederationSearcherModel.TargetSpec> readSources(Element searcherSpec) {
List<FederationSearcherModel.TargetSpec> sources = new ArrayList<>();
for (Element source : XML.getChildren(searcherSpec, "source")) {
@@ -76,14 +75,14 @@ public class DomFederationSearcherBuilder extends VespaDomBuilder.DomConfigProdu
}
@Override
- protected FederationSearcher doBuild(DeployState deployState, AbstractConfigProducer ancestor, Element searcherElement) {
+ protected FederationSearcher doBuild(DeployState deployState, AbstractConfigProducer<?> ancestor, Element searcherElement) {
FederationSearcherModel model = new FederationSearcherModelBuilder(searcherElement).build();
Optional<Component> targetSelector = buildTargetSelector(deployState, ancestor, searcherElement, model.getComponentId());
return new FederationSearcher(model, targetSelector);
}
- private Optional<Component> buildTargetSelector(DeployState deployState, AbstractConfigProducer ancestor, Element searcherElement, ComponentId namespace) {
+ private Optional<Component> buildTargetSelector(DeployState deployState, AbstractConfigProducer<?> ancestor, Element searcherElement, ComponentId namespace) {
Element targetSelectorElement = XML.getChild(searcherElement, "target-selector");
if (targetSelectorElement == null)
return Optional.empty();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java
index 0106123666d..9fb19efbf75 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/chains/search/SearchChainsBuilder.java
@@ -16,9 +16,10 @@ import java.util.List;
import java.util.Map;
/**
+ * Creates top level search chains(searchchain, provider) from xml.
+ *
* @author Tony Vaagenes
* @author gjoranv
- * Creates top level search chains(searchchain, provider) from xml.
*/
public class SearchChainsBuilder extends ChainsBuilder<Searcher<?>, SearchChain> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
index 4e78f44d0fe..317ed0f66c7 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
@@ -25,6 +25,7 @@ import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainer;
import com.yahoo.vespa.model.container.component.BindingPattern;
import com.yahoo.vespa.model.container.component.Component;
@@ -58,6 +59,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
RankProfilesConfig.Producer,
RankingConstantsConfig.Producer,
OnnxModelsConfig.Producer,
+ RankingExpressionsConfig.Producer,
ServletPathsConfig.Producer,
ContainerMbusConfig.Producer,
MetricsProxyApiConfig.Producer,
@@ -233,6 +235,10 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
if (modelEvaluation != null) modelEvaluation.getConfig(builder);
}
+ public void getConfig(RankingExpressionsConfig.Builder builder) {
+ if (modelEvaluation != null) modelEvaluation.getConfig(builder);
+ }
+
@Override
public void getConfig(ContainerMbusConfig.Builder builder) {
if (mbusParams != null) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
index 6ef29269bc1..cd596038137 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java
@@ -79,7 +79,6 @@ public abstract class Container extends AbstractService implements
private final ComponentGroup<Component<?, ?>> components = new ComponentGroup<>(this, "components");
private final JettyHttpServer defaultHttpServer;
- private final boolean enableJdiscHttp2;
protected Container(AbstractConfigProducer<?> parent, String name, int index, DeployState deployState) {
this(parent, name, false, index, deployState);
@@ -100,8 +99,6 @@ public abstract class Container extends AbstractService implements
addChild(new SimpleComponent("com.yahoo.container.jdisc.ConfiguredApplication$ApplicationContext"));
appendJvmOptions(jvmOmitStackTraceInFastThrowOption(deployState.featureFlags()));
-
- this.enableJdiscHttp2 = deployState.featureFlags().enableJdiscHttp2();
}
protected String jvmOmitStackTraceInFastThrowOption(ModelContext.FeatureFlags featureFlags) {
@@ -183,7 +180,7 @@ public abstract class Container extends AbstractService implements
}
private void initDefaultJettyConnector() {
- defaultHttpServer.addConnector(new ConnectorFactory.Builder("SearchServer", getSearchPort()).enableHttp2(enableJdiscHttp2).build());
+ defaultHttpServer.addConnector(new ConnectorFactory.Builder("SearchServer", getSearchPort()).build());
}
private ContainerServiceType myServiceType = null;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java
index 510d2fe3d99..37bfb8821c3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java
@@ -7,6 +7,7 @@ import com.yahoo.searchdefinition.derived.RankProfileList;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
@@ -18,10 +19,12 @@ import java.util.Objects;
*
* @author bratseth
*/
-public class ContainerModelEvaluation implements RankProfilesConfig.Producer,
- RankingConstantsConfig.Producer,
- OnnxModelsConfig.Producer
-{
+
+public class ContainerModelEvaluation implements
+ RankProfilesConfig.Producer,
+ RankingConstantsConfig.Producer,
+ OnnxModelsConfig.Producer,
+ RankingExpressionsConfig.Producer {
private final static String BUNDLE_NAME = "model-evaluation";
private final static String EVALUATOR_NAME = ModelsEvaluator.class.getName();
@@ -38,8 +41,7 @@ public class ContainerModelEvaluation implements RankProfilesConfig.Producer,
}
public void prepare(List<ApplicationContainer> containers) {
- rankProfileList.sendConstantsTo(containers);
- rankProfileList.sendOnnxModelsTo(containers);
+ rankProfileList.sendTo(containers);
}
@Override
@@ -57,6 +59,10 @@ public class ContainerModelEvaluation implements RankProfilesConfig.Producer,
rankProfileList.getConfig(builder);
}
+ public void getConfig(RankingExpressionsConfig.Builder builder) {
+ rankProfileList.getConfig(builder);
+ }
+
public static Handler<?> getHandler() {
Handler<?> handler = new Handler<>(new ComponentModel(REST_HANDLER_NAME, null, BUNDLE_NAME));
handler.addServerBindings(
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java
index 9b0075c79c3..0d024398de7 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java
@@ -20,7 +20,6 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig
private final String name;
private final int listenPort;
private final SslProvider sslProviderComponent;
- private final boolean enableHttp2;
private volatile ComponentId defaultRequestFilterChain;
private volatile ComponentId defaultResponseFilterChain;
@@ -33,7 +32,6 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig
this.sslProviderComponent = builder.sslProvider != null ? builder.sslProvider : new DefaultSslProvider(name);
this.defaultRequestFilterChain = builder.defaultRequestFilterChain;
this.defaultResponseFilterChain = builder.defaultResponseFilterChain;
- this.enableHttp2 = builder.enableHttp2 != null ? builder.enableHttp2 : false;
addChild(sslProviderComponent);
inject(sslProviderComponent);
}
@@ -42,7 +40,6 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig
public void getConfig(ConnectorConfig.Builder connectorBuilder) {
connectorBuilder.listenPort(listenPort);
connectorBuilder.name(name);
- connectorBuilder.http2Enabled(enableHttp2);
sslProviderComponent.amendConnectorConfig(connectorBuilder);
}
@@ -69,7 +66,6 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig
private SslProvider sslProvider;
private ComponentId defaultRequestFilterChain;
private ComponentId defaultResponseFilterChain;
- private Boolean enableHttp2;
public Builder(String name, int listenPort) {
this.name = name;
@@ -88,8 +84,6 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig
this.defaultResponseFilterChain = filterChain; return this;
}
- public Builder enableHttp2(boolean enabled) { this.enableHttp2 = enabled; return this; }
-
public ConnectorFactory build() {
return new ConnectorFactory(this);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
index 9d715073f77..766aa46fc01 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
@@ -29,32 +29,32 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
* Create connector factory that uses a certificate provided by the config-model / configserver and default hosted Vespa truststore.
*/
public static HostedSslConnectorFactory withProvidedCertificate(
- String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth, boolean enableHttp2) {
+ String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth) {
ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null, enforceHandshakeClientAuth);
- return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, enableHttp2);
+ return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth);
}
/**
* Create connector factory that uses a certificate provided by the config-model / configserver and a truststore configured by the application.
*/
public static HostedSslConnectorFactory withProvidedCertificateAndTruststore(
- String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates, boolean enableHttp2) {
+ String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates) {
ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false);
- return new HostedSslConnectorFactory(sslProvider, true, false, enableHttp2);
+ return new HostedSslConnectorFactory(sslProvider, true, false);
}
/**
* Create connector factory that uses the default certificate and truststore provided by Vespa (through Vespa-global TLS configuration).
*/
- public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName, boolean enableHttp2) {
- return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, enableHttp2);
+ public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName) {
+ return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false);
}
private HostedSslConnectorFactory(SslProvider sslProvider, boolean enforceClientAuth,
- boolean enforceHandshakeClientAuth, boolean enableHttp2) {
- super(new Builder("tls4443", 4443).sslProvider(sslProvider).enableHttp2(enableHttp2));
+ boolean enforceHandshakeClientAuth) {
+ super(new Builder("tls4443", 4443).sslProvider(sslProvider));
this.enforceClientAuth = enforceClientAuth;
this.enforceHandshakeClientAuth = enforceHandshakeClientAuth;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java
index 3b616c34a03..22fa5332d5e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java
@@ -39,7 +39,7 @@ public class JettyConnectorBuilder extends VespaDomBuilder.DomConfigProducerBuil
.map(ComponentId::new)
.ifPresent(builder::defaultResponseFilterChain);
SslProvider sslProviderComponent = getSslConfigComponents(name, serverSpec);
- return builder.sslProvider(sslProviderComponent).enableHttp2(deployState.featureFlags().enableJdiscHttp2()).build();
+ return builder.sslProvider(sslProviderComponent).build();
}
SslProvider getSslConfigComponents(String serverName, Element serverSpec) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java
index ceb48732116..6b4cb003cda 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/FederationSearcher.java
@@ -11,7 +11,12 @@ import com.yahoo.search.federation.FederationConfig;
import com.yahoo.search.searchchain.model.federation.FederationSearcherModel.TargetSpec;
import com.yahoo.vespa.model.container.component.Component;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
/**
* Config producer for the FederationSearcher.
@@ -26,8 +31,8 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
* Generates config for a single search chain contained in a target.
*/
private static final class SearchChainConfig {
+
private final SearchChain searchChain;
- //Zero if not applicable
final ComponentId providerId;
final FederationOptions targetOptions;
final List<String> documentTypes;
@@ -61,6 +66,7 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
* which can be federated to as a single entity.
*/
private static abstract class Target {
+
final ComponentId id;
final FederationOptions targetOptions;
@@ -79,41 +85,36 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
}
protected abstract void getSearchChainsConfig(FederationConfig.Target.Builder tb);
+
}
private static class SearchChainTarget extends Target {
+
private final SearchChainConfig searchChainConfig;
- public SearchChainTarget(SearchChain searchChain,
- FederationOptions targetOptions) {
+ public SearchChainTarget(SearchChain searchChain, FederationOptions targetOptions) {
super(searchChain.getComponentId(), targetOptions);
- searchChainConfig = new SearchChainConfig(
- searchChain,
- null,
- targetOptions,
- searchChain.getDocumentTypes());
+ searchChainConfig = new SearchChainConfig(searchChain, null, targetOptions, searchChain.getDocumentTypes());
}
@Override
protected void getSearchChainsConfig(FederationConfig.Target.Builder tB) {
tB.searchChain(searchChainConfig.getSearchChainConfig());
}
+
}
private static class SourceGroupTarget extends Target {
+
private final SearchChainConfig leaderConfig;
- private final List<SearchChainConfig> participantsConfig =
- new ArrayList<>();
+ private final List<SearchChainConfig> participantsConfig = new ArrayList<>();
- public SourceGroupTarget(SourceGroup group,
- FederationOptions targetOptions) {
+ public SourceGroupTarget(SourceGroup group, FederationOptions targetOptions) {
super(group.getComponentId(), applyDefaultSourceGroupOptions(targetOptions));
leaderConfig = createConfig(group.leader(), targetOptions);
- for (Source participant : group.participants()) {
- participantsConfig.add(
- createConfig(participant, targetOptions));
- }
+ for (Source participant : group.participants())
+ participantsConfig.add(createConfig(participant, targetOptions));
}
private static FederationOptions applyDefaultSourceGroupOptions(FederationOptions targetOptions) {
@@ -121,64 +122,49 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
return targetOptions.inherit(defaultSourceGroupOption);
}
- private SearchChainConfig createConfig(Source source,
- FederationOptions targetOptions) {
- return new SearchChainConfig(
- source,
- source.getParentProvider().getComponentId(),
- targetOptions,
- source.getDocumentTypes());
+ private SearchChainConfig createConfig(Source source, FederationOptions targetOptions) {
+ return new SearchChainConfig(source,
+ source.getParentProvider().getComponentId(),
+ targetOptions,
+ source.getDocumentTypes());
}
@Override
protected void getSearchChainsConfig(FederationConfig.Target.Builder tB) {
tB.searchChain(leaderConfig.getSearchChainConfig());
- for (SearchChainConfig participant : participantsConfig) {
+ for (SearchChainConfig participant : participantsConfig)
tB.searchChain(participant.getSearchChainConfig());
- }
}
}
private static class TargetResolver {
+
final ComponentRegistry<SearchChain> searchChainRegistry;
final SourceGroupRegistry sourceGroupRegistry;
- /**
- * @return true if searchChain.id newer than sourceGroup.id
- */
- private boolean newerVersion(SearchChain searchChain,
- SourceGroup sourceGroup) {
- if (searchChain == null || sourceGroup == null) {
- return false;
- } else {
- return newerVersion(searchChain.getComponentId(), sourceGroup.getComponentId());
- }
+ /** Returns true if searchChain.id newer than sourceGroup.id */
+ private boolean newerVersion(SearchChain searchChain, SourceGroup sourceGroup) {
+ if (searchChain == null || sourceGroup == null) return false;
+ return newerVersion(searchChain.getComponentId(), sourceGroup.getComponentId());
}
- /**
- * @return true if a newer than b
- */
+ /** Returns true if a newer than b */
private boolean newerVersion(ComponentId a, ComponentId b) {
return a.compareTo(b) > 0;
}
-
- TargetResolver(ComponentRegistry<SearchChain> searchChainRegistry,
- SourceGroupRegistry sourceGroupRegistry) {
+ TargetResolver(ComponentRegistry<SearchChain> searchChainRegistry, SourceGroupRegistry sourceGroupRegistry) {
this.searchChainRegistry = searchChainRegistry;
this.sourceGroupRegistry = sourceGroupRegistry;
}
Target resolve(FederationSearcherModel.TargetSpec specification) {
- SearchChain searchChain = searchChainRegistry.getComponent(
- specification.sourceSpec);
- SourceGroup sourceGroup = sourceGroupRegistry.getComponent(
- specification.sourceSpec);
+ SearchChain searchChain = searchChainRegistry.getComponent(specification.sourceSpec);
+ SourceGroup sourceGroup = sourceGroupRegistry.getComponent(specification.sourceSpec);
if (searchChain == null && sourceGroup == null) {
return null;
- } else if (sourceGroup == null ||
- newerVersion(searchChain, sourceGroup)) {
+ } else if (sourceGroup == null || newerVersion(searchChain, sourceGroup)) {
return new SearchChainTarget(searchChain, specification.federationOptions);
} else {
return new SourceGroupTarget(sourceGroup, specification.federationOptions);
@@ -186,26 +172,21 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
}
}
- private final Map<ComponentId, Target> resolvedTargets =
- new LinkedHashMap<>();
+ private final Map<ComponentId, Target> resolvedTargets = new LinkedHashMap<>();
public FederationSearcher(FederationSearcherModel searcherModel, Optional<Component> targetSelector) {
super(searcherModel);
this.targetSelector = targetSelector;
- if (targetSelector.isPresent())
- addChild(targetSelector.get());
+ targetSelector.ifPresent(selector -> addChild(selector));
}
@Override
public void getConfig(FederationConfig.Builder builder) {
- for (Target target : resolvedTargets.values()) {
+ for (Target target : resolvedTargets.values())
builder.target(target.getTargetConfig());
- }
- if (targetSelector.isPresent()) {
- builder.targetSelector(targetSelector.get().getGlobalComponentId().stringValue());
- }
+ targetSelector.ifPresent(selector -> builder.targetSelector(selector.getGlobalComponentId().stringValue()));
}
@Override
@@ -213,10 +194,8 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
initialize(getSearchChains().allChains(), getSearchChains().allSourceGroups());
}
- void initialize(ComponentRegistry<SearchChain> searchChainRegistry,
- SourceGroupRegistry sourceGroupRegistry) {
- TargetResolver targetResolver = new TargetResolver(
- searchChainRegistry, sourceGroupRegistry);
+ void initialize(ComponentRegistry<SearchChain> searchChainRegistry, SourceGroupRegistry sourceGroupRegistry) {
+ TargetResolver targetResolver = new TargetResolver(searchChainRegistry, sourceGroupRegistry);
addSourceTargets(targetResolver, model.targets);
@@ -229,16 +208,14 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
Target target = targetResolver.resolve(targetSpec);
if (target == null) {
- throw new RuntimeException("Can't find source " +
- targetSpec.sourceSpec +
- " used as a source for federation '" +
- getComponentId() + "'");
+ throw new RuntimeException("Can't find source " + targetSpec.sourceSpec +
+ " used as a source for federation '" + getComponentId() + "'");
}
Target duplicate = resolvedTargets.put(target.id, target);
if (duplicate != null && !duplicate.targetOptions.equals(target.targetOptions)) {
- throw new RuntimeException("Search chain " + target.id + " added twice with different federation options"
- + " to the federation searcher " + getComponentId());
+ throw new RuntimeException("Search chain " + target.id + " added twice with different federation options" +
+ " to the federation searcher " + getComponentId());
}
}
}
@@ -248,23 +225,21 @@ public class FederationSearcher extends Searcher<FederationSearcherModel> implem
for (GenericTarget genericTarget : defaultTargets(searchChainRegistry.allComponents())) {
ComponentSpecification specification = genericTarget.getComponentId().toSpecification();
- //Can't use genericTarget directly, as it might be part of a source group.
+ // Can't use genericTarget directly, as it might be part of a source group.
Target federationTarget = targetResolver.resolve(new TargetSpec(specification, new FederationOptions()));
- //Do not replace manually added sources, as they might have manually configured federation options
+ // Do not replace manually added sources, as they might have manually configured federation options
if (!resolvedTargets.containsKey(federationTarget.id))
resolvedTargets.put(federationTarget.id, federationTarget);
}
}
-
private static List<GenericTarget> defaultTargets(Collection<SearchChain> allSearchChains) {
- Collection<Provider> providers =
- CollectionUtil.filter(allSearchChains, Provider.class);
+ Collection<Provider> providers = CollectionUtil.filter(allSearchChains, Provider.class);
List<GenericTarget> defaultTargets = new ArrayList<>();
- for (Provider provider : providers) {
+ for (Provider provider : providers)
defaultTargets.addAll(provider.defaultFederationTargets());
- }
return defaultTargets;
}
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java
index ee4edf3fd8c..10e0f3e55da 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/searchchain/Provider.java
@@ -7,6 +7,7 @@ import com.yahoo.vespa.model.container.component.ConfigProducerGroup;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
/**
* Base config producer for search chains that communicate with backends.
@@ -15,7 +16,7 @@ import java.util.Collection;
*/
public class Provider extends GenericTarget {
- private ConfigProducerGroup<Source> sources;
+ private final ConfigProducerGroup<Source> sources;
public Provider(ChainSpecification specWithoutInnerSearchers, FederationOptions federationOptions) {
super(specWithoutInnerSearchers, federationOptions);
@@ -37,9 +38,10 @@ public class Provider extends GenericTarget {
public Collection<? extends GenericTarget> defaultFederationTargets() {
if (sources.getComponents().isEmpty()) {
- return Arrays.asList(this);
+ return List.of(this);
} else {
return sources.getComponents();
}
}
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
index 87406b8bc9a..b477587bcac 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
@@ -431,7 +431,6 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
// If the deployment contains certificate/private key reference, setup TLS port
HostedSslConnectorFactory connectorFactory;
- boolean enableHttp2 = deployState.featureFlags().enableJdiscHttp2();
if (deployState.endpointCertificateSecrets().isPresent()) {
boolean authorizeClient = deployState.zone().system().isPublic();
if (authorizeClient && deployState.tlsClientAuthority().isEmpty()) {
@@ -445,10 +444,10 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
.orElse(false);
connectorFactory = authorizeClient
- ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(serverName, endpointCertificateSecrets, deployState.tlsClientAuthority().get(), enableHttp2)
- : HostedSslConnectorFactory.withProvidedCertificate(serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, enableHttp2);
+ ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(serverName, endpointCertificateSecrets, deployState.tlsClientAuthority().get())
+ : HostedSslConnectorFactory.withProvidedCertificate(serverName, endpointCertificateSecrets, enforceHandshakeClientAuth);
} else {
- connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, enableHttp2);
+ connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName);
}
cluster.getHttp().getAccessControl().ifPresent(accessControl -> accessControl.configureHostedConnector(connectorFactory));
server.addConnector(connectorFactory);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java
index c7d56c9b4b5..70f2acd3c7b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java
@@ -1,6 +1,7 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
+import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import com.yahoo.vespa.model.content.cluster.DomResourceLimitsBuilder;
@@ -35,28 +36,31 @@ public class ClusterResourceLimits {
public static class Builder {
private final boolean enableFeedBlockInDistributor;
+ private final boolean hostedVespa;
+ private final DeployLogger deployLogger;
private ResourceLimits.Builder ctrlBuilder = new ResourceLimits.Builder();
private ResourceLimits.Builder nodeBuilder = new ResourceLimits.Builder();
- public Builder(boolean enableFeedBlockInDistributor) {
+ public Builder(boolean enableFeedBlockInDistributor, boolean hostedVespa, DeployLogger deployLogger) {
this.enableFeedBlockInDistributor = enableFeedBlockInDistributor;
+ this.hostedVespa = hostedVespa;
+ this.deployLogger = deployLogger;
}
public ClusterResourceLimits build(ModelElement clusterElem) {
- ModelElement tuningElem = clusterElem.childByPath("tuning");
- if (tuningElem != null) {
- ctrlBuilder = DomResourceLimitsBuilder.createBuilder(tuningElem);
- }
-
- ModelElement protonElem = clusterElem.childByPath("engine.proton");
- if (protonElem != null) {
- nodeBuilder = DomResourceLimitsBuilder.createBuilder(protonElem);
- }
+ ctrlBuilder = createBuilder(clusterElem.childByPath("tuning"));
+ nodeBuilder = createBuilder(clusterElem.childByPath("engine.proton"));
deriveLimits();
return new ClusterResourceLimits(this);
}
+ private ResourceLimits.Builder createBuilder(ModelElement element) {
+ return element == null
+ ? new ResourceLimits.Builder()
+ : DomResourceLimitsBuilder.createBuilder(element, hostedVespa, deployLogger);
+ }
+
public void setClusterControllerBuilder(ResourceLimits.Builder builder) {
ctrlBuilder = builder;
}
@@ -88,7 +92,7 @@ public class ClusterResourceLimits {
Optional<Double> contentNodeLimit,
Consumer<Double> setter) {
// TODO: remove this when feed block in distributor is default enabled.
- if (!clusterControllerLimit.isPresent() && !contentNodeLimit.isPresent()) {
+ if (clusterControllerLimit.isEmpty() && contentNodeLimit.isEmpty()) {
setter.accept(0.8);
}
}
@@ -96,7 +100,7 @@ public class ClusterResourceLimits {
private void deriveClusterControllerLimit(Optional<Double> clusterControllerLimit,
Optional<Double> contentNodeLimit,
Consumer<Double> setter) {
- if (!clusterControllerLimit.isPresent()) {
+ if (clusterControllerLimit.isEmpty()) {
contentNodeLimit.ifPresent(limit ->
// TODO: emit warning when feed block in distributor is default enabled.
setter.accept(Double.max(0.0, (limit - 0.01))));
@@ -106,7 +110,7 @@ public class ClusterResourceLimits {
private void deriveContentNodeLimit(Optional<Double> contentNodeLimit,
Optional<Double> clusterControllerLimit,
Consumer<Double> setter) {
- if (!contentNodeLimit.isPresent()) {
+ if (contentNodeLimit.isEmpty()) {
clusterControllerLimit.ifPresent(limit ->
setter.accept(calcContentNodeLimit(limit)));
}
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 f6a45842bd9..b5cae857ce0 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
@@ -25,7 +25,6 @@ import com.yahoo.vespa.config.content.core.BucketspacesConfig;
import com.yahoo.vespa.config.content.core.StorDistributormanagerConfig;
import com.yahoo.vespa.model.HostResource;
import com.yahoo.vespa.model.admin.Admin;
-import com.yahoo.vespa.model.admin.Configserver;
import com.yahoo.vespa.model.admin.clustercontroller.ClusterControllerCluster;
import com.yahoo.vespa.model.admin.clustercontroller.ClusterControllerComponent;
import com.yahoo.vespa.model.admin.clustercontroller.ClusterControllerConfigurer;
@@ -134,7 +133,10 @@ public class ContentCluster extends AbstractConfigProducer implements
globallyDistributedDocuments, routingSelection,
deployState.zone(), deployState.isHosted());
boolean enableFeedBlockInDistributor = deployState.getProperties().featureFlags().enableFeedBlockInDistributor();
- var resourceLimits = new ClusterResourceLimits.Builder(enableFeedBlockInDistributor).build(contentElement);
+ var resourceLimits = new ClusterResourceLimits.Builder(enableFeedBlockInDistributor,
+ stateIsHosted(deployState),
+ deployState.getDeployLogger())
+ .build(contentElement);
c.clusterControllerConfig = new ClusterControllerConfig.Builder(getClusterId(contentElement),
contentElement,
resourceLimits.getClusterControllerLimits()).build(deployState, c, contentElement.getXml());
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
index 210f062f9b2..9f4852629d0 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
@@ -1,9 +1,12 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content.cluster;
+import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import com.yahoo.vespa.model.content.ResourceLimits;
+import java.util.logging.Level;
+
/**
* Builder for feed block resource limits.
*
@@ -11,10 +14,16 @@ import com.yahoo.vespa.model.content.ResourceLimits;
*/
public class DomResourceLimitsBuilder {
- public static ResourceLimits.Builder createBuilder(ModelElement contentXml) {
+ public static ResourceLimits.Builder createBuilder(ModelElement contentXml, boolean hostedVespa, DeployLogger deployLogger) {
ResourceLimits.Builder builder = new ResourceLimits.Builder();
ModelElement resourceLimits = contentXml.child("resource-limits");
- if (resourceLimits == null) {
+ if (resourceLimits == null) { return builder; }
+
+ if (hostedVespa) {
+ deployLogger.logApplicationPackage(Level.WARNING, "Element " + resourceLimits +
+ " is not allowed, default limits will be used");
+ // TODO: Throw exception when we are sure nobody is using this
+ //throw new IllegalArgumentException("Element " + element + " is not allowed to be set, default limits will be used");
return builder;
}
if (resourceLimits.child("disk") != null) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java b/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java
index fb9402614f6..5bb57f4ff6c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/filedistribution/FileDistributor.java
@@ -8,7 +8,12 @@ import com.yahoo.config.application.api.FileRegistry;
import com.yahoo.vespa.model.ConfigProxy;
import com.yahoo.vespa.model.Host;
-import java.util.*;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
/**
* Sends RPC requests to hosts (tenant hosts and config servers) asking them to start download of files. This is used
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java b/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java
index 15e6c5993b3..fbba377c950 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/routing/DocumentProtocol.java
@@ -111,6 +111,8 @@ public final class DocumentProtocol implements Protocol,
addSelector(cluster.getConfigId(), cluster.getRoutingSelector(), clusterBuilder);
if (cluster.getSearch().hasIndexedCluster())
addRoutes(getDirectRouteName(cluster.getConfigId()), getIndexedRouteName(cluster.getConfigId()), clusterBuilder);
+ else
+ clusterBuilder.defaultRoute(cluster.getConfigId());
builder.cluster(cluster.getConfigId(), clusterBuilder);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java
index 705c48d1c9b..3ca5b9e3a0a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/AbstractSearchCluster.java
@@ -39,8 +39,7 @@ public abstract class AbstractSearchCluster extends AbstractConfigProducer<Abstr
public void prepareToDistributeFiles(List<SearchNode> backends) {
for (SchemaSpec sds : localSDS) {
- sds.getSearchDefinition().getSearch().rankingConstants().sendTo(backends);
- sds.getSearchDefinition().getSearch().onnxModels().sendTo(backends);
+ sds.getSearchDefinition().getSearch().sendTo(backends);
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java b/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java
index 8d4c1675bbf..9f0ff7db9f8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/DocumentDatabase.java
@@ -12,6 +12,7 @@ import com.yahoo.vespa.config.search.SummaryConfig;
import com.yahoo.vespa.config.search.SummarymapConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.vespa.config.search.summary.JuniperrcConfig;
import com.yahoo.vespa.configdefinition.IlscriptsConfig;
@@ -26,6 +27,7 @@ public class DocumentDatabase extends AbstractConfigProducer<DocumentDatabase> i
AttributesConfig.Producer,
RankProfilesConfig.Producer,
RankingConstantsConfig.Producer,
+ RankingExpressionsConfig.Producer,
OnnxModelsConfig.Producer,
IndexschemaConfig.Producer,
JuniperrcConfig.Producer,
@@ -75,6 +77,11 @@ public class DocumentDatabase extends AbstractConfigProducer<DocumentDatabase> i
}
@Override
+ public void getConfig(RankingExpressionsConfig.Builder builder) {
+ derivedCfg.getRankProfileList().getConfig(builder);
+ }
+
+ @Override
public void getConfig(RankingConstantsConfig.Builder builder) {
derivedCfg.getRankProfileList().getConfig(builder);
}
diff --git a/config-model/src/main/javacc/SDParser.jj b/config-model/src/main/javacc/SDParser.jj
index 8f99c388fb1..0cc1db5a2d7 100644
--- a/config-model/src/main/javacc/SDParser.jj
+++ b/config-model/src/main/javacc/SDParser.jj
@@ -79,16 +79,6 @@ public class SDParser {
private RankProfileRegistry rankProfileRegistry;
private boolean documentsOnly;
- /** For testing only */
- public SDParser(String input, DeployLogger deployLogger) {
- this(new SimpleCharStream(input), deployLogger);
- }
-
- /** For testing only */
- public SDParser(SimpleCharStream stream, DeployLogger deployLogger) {
- this(stream, deployLogger, MockApplicationPackage.createEmpty(), new RankProfileRegistry(), false);
- }
-
/**
* Creates a parser
*
@@ -435,7 +425,7 @@ Search rootSchema(String dir) :
Search search;
}
{
- ( ( <SCHEMA> | <SEARCH> ) name = identifier() { search = new Search(name, app);
+ ( ( <SCHEMA> | <SEARCH> ) name = identifier() { search = new Search(name, app, deployLogger);
rankProfileRegistry.add(new DefaultRankProfile(search, rankProfileRegistry));
rankProfileRegistry.add(new UnrankedRankProfile(search, rankProfileRegistry));}
lbrace() (rootSchemaItem(search) (<NL>)*)* <RBRACE> (<NL>)* <EOF>)
@@ -476,7 +466,7 @@ Object rootSchemaItem(Search search) : { }
*/
Search rootDocument(String dir) :
{
- Search search = new DocumentOnlySearch();
+ Search search = new DocumentOnlySearch(app, deployLogger);
}
{
( (rootDocumentItem(search) (<NL>)*)*<EOF> )
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 326bf577acc..2b72420614d 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
@@ -494,9 +494,8 @@ public class ModelProvisioningTest {
Set<HostResource> clusterControllerHosts = admin.getClusterControllers().getContainers()
.stream().map(cc -> cc.getHostResource()).collect(Collectors.toSet());
Set<HostResource> slobrokHosts = admin.getSlobroks().stream().map(Slobrok::getHost).collect(Collectors.toSet());
- assertEquals(6, slobrokHosts.size());
- assertTrue("Slobroks are assigned from container and cluster controller nodes",
- union(containerHosts, clusterControllerHosts).containsAll(slobrokHosts));
+ assertEquals(3, slobrokHosts.size());
+ assertTrue("Slobroks are assigned on cluster controller nodes", clusterControllerHosts.containsAll(slobrokHosts));
assertTrue("Logserver is assigned from container nodes", containerHosts.contains(admin.getLogserver().getHost()));
assertEquals("No in-cluster config servers in a hosted environment", 0, admin.getConfigservers().size());
assertEquals("Dedicated admin cluster controllers when hosted", 3, admin.getClusterControllers().getContainers().size());
@@ -560,6 +559,40 @@ public class ModelProvisioningTest {
}
@Test
+ public void testSlobroksOnContainersIfNoContentClusters() {
+ String services =
+ "<?xml version='1.0' encoding='utf-8' ?>\n" +
+ "<services>" +
+ " <admin version='4.0'/>" +
+ " <container version='1.0' id='foo'>" +
+ " <nodes count='10'/>" +
+ " </container>" +
+ "</services>";
+
+ int numberOfHosts = 10;
+ VespaModelTester tester = new VespaModelTester();
+ tester.addHosts(numberOfHosts);
+ VespaModel model = tester.createModel(services, true);
+ assertEquals(numberOfHosts, model.getRoot().hostSystem().getHosts().size());
+
+ // Check container cluster
+ assertEquals(1, model.getContainerClusters().size());
+ Set<HostResource> containerHosts = model.getContainerClusters().get("foo").getContainers().stream()
+ .map(Container::getHost)
+ .collect(Collectors.toSet());
+ assertEquals(10, containerHosts.size());
+
+ // Check admin clusters
+ Admin admin = model.getAdmin();
+ Set<HostResource> slobrokHosts = admin.getSlobroks().stream().map(Slobrok::getHost).collect(Collectors.toSet());
+ assertEquals(3, slobrokHosts.size());
+ assertTrue("Slobroks are assigned from container nodes",
+ containerHosts.containsAll(slobrokHosts));
+ assertTrue("Logserver is assigned from container nodes", containerHosts.contains(admin.getLogserver().getHost()));
+ assertEquals("No in-cluster config servers in a hosted environment", 0, admin.getConfigservers().size());
+ }
+
+ @Test
public void testUsingNodesAndGroupCountAttributesWithoutDedicatedClusterControllers() {
String services =
"<?xml version='1.0' encoding='utf-8' ?>\n" +
@@ -602,9 +635,8 @@ public class ModelProvisioningTest {
Set<HostResource> clusterControllerHosts = admin.getClusterControllers().getContainers()
.stream().map(cc -> cc.getHostResource()).collect(Collectors.toSet());
Set<HostResource> slobrokHosts = admin.getSlobroks().stream().map(Slobrok::getHost).collect(Collectors.toSet());
- assertEquals(6, slobrokHosts.size());
- assertTrue("Slobroks are assigned from container and cluster controller nodes",
- union(containerHosts, clusterControllerHosts).containsAll(slobrokHosts));
+ assertEquals(3, slobrokHosts.size());
+ assertTrue("Slobroks are assigned on cluster controller nodes", clusterControllerHosts.containsAll(slobrokHosts));
assertTrue("Logserver is assigned from container nodes", containerHosts.contains(admin.getLogserver().getHost()));
assertEquals("No in-cluster config servers in a hosted environment", 0, admin.getConfigservers().size());
assertEquals(3, admin.getClusterControllers().getContainers().size());
@@ -1613,7 +1645,7 @@ public class ModelProvisioningTest {
tester.addHosts(6);
VespaModel model = tester.createModel(services, true);
assertEquals(6, model.getRoot().hostSystem().getHosts().size());
- assertEquals(5, model.getAdmin().getSlobroks().size());
+ assertEquals(3, model.getAdmin().getSlobroks().size());
assertEquals(2, model.getContainerClusters().get("foo").getContainers().size());
assertEquals(1, model.getContentClusters().get("bar").getRootGroup().countNodes(true));
}
@@ -2056,9 +2088,6 @@ public class ModelProvisioningTest {
" </nodes>",
" <engine>",
" <proton>",
- " <resource-limits>",
- " <memory>0.92</memory>",
- " </resource-limits>",
" <tuning>",
" <searchnode>",
" <flushstrategy>",
@@ -2084,7 +2113,6 @@ public class ModelProvisioningTest {
assertEquals(2000, cfg.flush().memory().maxtlssize()); // from config override
assertEquals(1000, cfg.flush().memory().maxmemory()); // from explicit tuning
assertEquals((long) ((128 - reservedMemoryGb) * GB / 8), cfg.flush().memory().each().maxmemory()); // from default node flavor tuning
- assertEquals(0.92, cfg.writefilter().memorylimit(), 0.0001); // from explicit resource-limits
}
private static ProtonConfig getProtonConfig(VespaModel model, String configId) {
@@ -2138,11 +2166,4 @@ public class ModelProvisioningTest {
assertProvisioned(nodeCount, id, null, type, model);
}
- private Set<HostResource> union(Set<HostResource> a, Set<HostResource> b) {
- Set<HostResource> union = new HashSet<>();
- union.addAll(a);
- union.addAll(b);
- return union;
- }
-
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/DocumentGraphValidatorTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/DocumentGraphValidatorTest.java
index c2dd3e45dea..f475fbe6d78 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/DocumentGraphValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/DocumentGraphValidatorTest.java
@@ -135,7 +135,7 @@ public class DocumentGraphValidatorTest {
}
private static Search createSearchWithName(String name, Search... parents) {
- Search campaignSearch = new Search(name, null);
+ Search campaignSearch = new Search(name);
SDDocumentType document = new SDDocumentType(name);
campaignSearch.addDocument(document);
document.setDocumentReferences(new DocumentReferences(Collections.emptyMap()));
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java
index e46208c770d..6b65303fe56 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java
@@ -23,14 +23,16 @@ import static org.junit.Assert.assertTrue;
*/
public class DocumentReferenceResolverTest {
+ private static final String BAR = "bar";
+ private static final String FOO = "foo";
@Rule
public final ExpectedException exceptionRule = ExpectedException.none();
@Test
public void reference_from_one_document_to_another_is_resolved() {
// Create bar document with no fields
- Search barSearch = new Search();
- SDDocumentType barDocument = new SDDocumentType("bar", barSearch);
+ Search barSearch = new Search(BAR);
+ SDDocumentType barDocument = new SDDocumentType(BAR, barSearch);
barSearch.addDocument(barDocument);
// Create foo document with document reference to bar and add another field
@@ -38,7 +40,7 @@ public class DocumentReferenceResolverTest {
("bar_ref", ReferenceDataType.createWithInferredId(barDocument.getDocumentType()));
AttributeUtils.addAttributeAspect(fooRefToBarField);
SDField irrelevantField = new SDField("irrelevant_stuff", DataType.INT);
- Search fooSearch = new Search();
+ Search fooSearch = new Search(FOO);
SDDocumentType fooDocument = new SDDocumentType("foo", fooSearch);
fooDocument.addField(fooRefToBarField);
fooDocument.addField(irrelevantField);
@@ -60,7 +62,7 @@ public class DocumentReferenceResolverTest {
SDField fooRefToBarField = new SDField(
"bar_ref", ReferenceDataType.createWithInferredId(TemporaryStructuredDataType.create("bar")));
AttributeUtils.addAttributeAspect(fooRefToBarField);
- Search fooSearch = new Search();
+ Search fooSearch = new Search(FOO);
SDDocumentType fooDocument = new SDDocumentType("foo", fooSearch);
fooDocument.addField(fooRefToBarField);
fooSearch.addDocument(fooDocument);
@@ -76,14 +78,14 @@ public class DocumentReferenceResolverTest {
@Test
public void throws_exception_if_reference_is_not_an_attribute() {
// Create bar document with no fields
- Search barSearch = new Search();
+ Search barSearch = new Search(BAR);
SDDocumentType barDocument = new SDDocumentType("bar", barSearch);
barSearch.addDocument(barDocument);
// Create foo document with document reference to bar
SDField fooRefToBarField = new SDField
("bar_ref", ReferenceDataType.createWithInferredId(barDocument.getDocumentType()));
- Search fooSearch = new Search();
+ Search fooSearch = new Search(FOO);
SDDocumentType fooDocument = new SDDocumentType("foo", fooSearch);
fooDocument.addField(fooRefToBarField);
fooSearch.addDocument(fooDocument);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/ImportedFieldsEnumeratorTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/ImportedFieldsEnumeratorTest.java
index fcbb89b5c42..f12129155b1 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/ImportedFieldsEnumeratorTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/ImportedFieldsEnumeratorTest.java
@@ -18,30 +18,33 @@ public class ImportedFieldsEnumeratorTest {
@Test
public void imported_fields_are_enumerated_and_copied_from_correct_search_instance() {
- Search parentSearch = new Search();
- SDDocumentType parentDocument = new SDDocumentType("parent", parentSearch);
+ String PARENT = "parent";
+ Search parentSearch = new Search(PARENT);
+ SDDocumentType parentDocument = new SDDocumentType(PARENT, parentSearch);
var parentField = new SDField("their_field", DataType.INT);
AttributeUtils.addAttributeAspect(parentField);
parentDocument.addField(parentField);
parentSearch.addDocument(parentDocument);
- Search fooSearch = new Search();
+ String FOO = "foo";
+ Search fooSearch = new Search(FOO);
SDField fooRefToParent = new SDField(
"foo_ref", ReferenceDataType.createWithInferredId(parentDocument.getDocumentType()));
AttributeUtils.addAttributeAspect(fooRefToParent);
var fooImports = fooSearch.temporaryImportedFields().get();
fooImports.add(new TemporaryImportedField("my_first_import", "foo_ref", "their_field"));
fooImports.add(new TemporaryImportedField("my_second_import", "foo_ref", "their_field"));
- SDDocumentType fooDocument = new SDDocumentType("foo", fooSearch);
+ SDDocumentType fooDocument = new SDDocumentType(FOO, fooSearch);
fooSearch.addDocument(fooDocument);
- Search barSearch = new Search();
+ String BAR = "bar";
+ Search barSearch = new Search(BAR);
SDField barRefToParent = new SDField(
"bar_ref", ReferenceDataType.createWithInferredId(parentDocument.getDocumentType()));
AttributeUtils.addAttributeAspect(barRefToParent);
var barImports = barSearch.temporaryImportedFields().get();
barImports.add(new TemporaryImportedField("my_cool_import", "my_ref", "their_field"));
- SDDocumentType barDocument = new SDDocumentType("bar", barSearch);
+ SDDocumentType barDocument = new SDDocumentType(BAR, barSearch);
barSearch.addDocument(barDocument);
var enumerator = new ImportedFieldsEnumerator(List.of(parentSearch, fooSearch, barSearch));
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileRegistryTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileRegistryTest.java
index 02ec597c3ed..84819439048 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileRegistryTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileRegistryTest.java
@@ -33,7 +33,7 @@ public class RankProfileRegistryTest {
@Test(expected = IllegalArgumentException.class)
public void testRankProfileDuplicateNameIsIllegal() {
- Search search = new Search("foo", null);
+ Search search = new Search("foo");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
RankProfile barRankProfile = new RankProfile("bar", search, rankProfileRegistry);
rankProfileRegistry.add(barRankProfile);
@@ -42,7 +42,7 @@ public class RankProfileRegistryTest {
@Test
public void testRankProfileDuplicateNameLegalForOverridableRankProfiles() {
- Search search = new Search("foo", null);
+ Search search = new Search("foo");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
for (String rankProfileName : RankProfileRegistry.overridableRankProfileNames) {
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java
index 0f83a4db98f..91e8640308a 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java
@@ -38,7 +38,7 @@ public class RankProfileTestCase extends SchemaTestCase {
@Test
public void testRankProfileInheritance() {
- Search search = new Search("test", null);
+ Search search = new Search("test");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
SDDocumentType document = new SDDocumentType("test");
SDField a = document.addField("a", DataType.STRING);
@@ -173,7 +173,7 @@ public class RankProfileTestCase extends SchemaTestCase {
" rank-profile p1 {}\n" +
" rank-profile p2 {}\n" +
"}");
- builder.build(true, new BaseDeployLogger());
+ builder.build(true);
Search search = builder.getSearch();
assertEquals(4, registry.all().size());
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/EmptyRankProfileTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/EmptyRankProfileTestCase.java
index 47862a2611b..139dae65c63 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/EmptyRankProfileTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/EmptyRankProfileTestCase.java
@@ -21,7 +21,7 @@ public class EmptyRankProfileTestCase extends SchemaTestCase {
@Test
public void testDeriving() {
- Search search = new Search("test", null);
+ Search search = new Search("test");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
SDDocumentType doc = new SDDocumentType("test");
search.addDocument(doc);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
index 770bea55af2..370315ad08b 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.derived;
+import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
@@ -103,7 +104,8 @@ public class ExportingTestCase extends AbstractExportingTestCase {
@Test
public void testRankExpression() throws IOException, ParseException {
- assertCorrectDeriving("rankexpression");
+ assertCorrectDeriving("rankexpression", null,
+ new TestProperties().useExternalRankExpression(true), new TestableDeployLogger());
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/IdTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/IdTestCase.java
index 130450e483d..b2cac8cd4c2 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/IdTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/IdTestCase.java
@@ -23,7 +23,7 @@ public class IdTestCase extends AbstractExportingTestCase {
@Test
public void testExplicitUpperCaseIdField() {
- Search search = new Search("test", null);
+ Search search = new Search("test");
SDDocumentType document = new SDDocumentType("test");
search.addDocument(document);
SDField uri = new SDField("URI", DataType.URI);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java
index 1b51fd354f3..c0a8a47e4ee 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/InheritanceTestCase.java
@@ -143,7 +143,7 @@ public class InheritanceTestCase extends AbstractExportingTestCase {
@Test
public void testIndexSettingInheritance() {
SDDocumentType parent = new SDDocumentType("parent");
- Search parentSearch = new Search("parent", null);
+ Search parentSearch = new Search("parent");
parentSearch.addDocument(parent);
SDField prefixed = parent.addField("prefixed", DataType.STRING);
prefixed.parseIndexingScript("{ index }");
@@ -151,7 +151,7 @@ public class InheritanceTestCase extends AbstractExportingTestCase {
SDDocumentType child = new SDDocumentType("child");
child.inherit(parent);
- Search childSearch = new Search("child", null);
+ Search childSearch = new Search("child");
childSearch.addDocument(child);
prefixed = (SDField)child.getField("prefixed");
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/LiteralBoostTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/LiteralBoostTestCase.java
index be720cbe4dd..2e36319bd8c 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/LiteralBoostTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/LiteralBoostTestCase.java
@@ -30,7 +30,7 @@ public class LiteralBoostTestCase extends AbstractExportingTestCase {
*/
@Test
public void testLiteralBoost() {
- Search search=new Search("literalboost", null);
+ Search search=new Search("literalboost");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
SDDocumentType document=new SDDocumentType("literalboost");
search.addDocument(document);
@@ -62,7 +62,7 @@ public class LiteralBoostTestCase extends AbstractExportingTestCase {
*/
@Test
public void testNonDefaultRankLiteralBoost() {
- Search search=new Search("literalboost", null);
+ Search search=new Search("literalboost");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
SDDocumentType document=new SDDocumentType("literalboost");
search.addDocument(document);
@@ -88,7 +88,7 @@ public class LiteralBoostTestCase extends AbstractExportingTestCase {
/** Tests literal boosts in two fields going to the same index */
@Test
public void testTwoLiteralBoostFields() {
- Search search=new Search("msb", null);
+ Search search=new Search("msb");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
SDDocumentType document=new SDDocumentType("msb");
search.addDocument(document);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SearchOrdererTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SearchOrdererTestCase.java
index 0c677456a87..f72f67f3711 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SearchOrdererTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SearchOrdererTestCase.java
@@ -62,7 +62,7 @@ public class SearchOrdererTestCase extends SchemaTestCase {
}
private static Search createSchema(String name, Map<String, Search> schemas) {
- Search search = new Search(name, null);
+ Search search = new Search(name);
SDDocumentType document = new SDDocumentType(name);
document.setDocumentReferences(new DocumentReferences(emptyMap()));
search.addDocument(document);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java
index 07d7405b1db..24d694cd442 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryMapTestCase.java
@@ -72,7 +72,7 @@ public class SummaryMapTestCase extends SchemaTestCase {
}
@Test
public void testPositionDeriving() {
- Search search = new Search("store", null);
+ Search search = new Search("store");
SDDocumentType document = new SDDocumentType("store");
search.addDocument(document);
String fieldName = "location";
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/TypeConversionTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/TypeConversionTestCase.java
index c03e915aa8b..364eb9dc014 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/TypeConversionTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/TypeConversionTestCase.java
@@ -25,7 +25,7 @@ public class TypeConversionTestCase extends SchemaTestCase {
/** Tests that exact-string stuff is not spilled over to the default index */
@Test
public void testExactStringToStringTypeConversion() {
- Search search = new Search("test", null);
+ Search search = new Search("test");
RankProfileRegistry rankProfileRegistry = RankProfileRegistry.createRankProfileRegistryWithBuiltinRankProfiles(search);
SDDocumentType document = new SDDocumentType("test");
search.addDocument(document);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/VsmFieldsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/VsmFieldsTestCase.java
index 37b49955bbf..b813f08d49e 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/VsmFieldsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/VsmFieldsTestCase.java
@@ -20,7 +20,7 @@ public class VsmFieldsTestCase {
@Test
public void reference_type_field_is_unsearchable() {
- Search search = new Search("test", MockApplicationPackage.createEmpty());
+ Search search = new Search("test", MockApplicationPackage.createEmpty(), new TestableDeployLogger());
search.addDocument(new SDDocumentType("test"));
SDField refField = new TemporarySDField("ref_field", ReferenceDataType.createWithInferredId(TemporaryStructuredDataType.create("parent_type")));
refField.parseIndexingScript("{ summary }");
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java
index defaf565a8b..83949ea6f8d 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java
@@ -6,6 +6,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.Field;
import com.yahoo.searchdefinition.DocumentReference;
import com.yahoo.searchdefinition.Search;
+import com.yahoo.searchdefinition.derived.TestableDeployLogger;
import com.yahoo.searchdefinition.document.ImportedField;
import com.yahoo.searchdefinition.document.ImportedFields;
import com.yahoo.searchdefinition.document.ImportedSimpleField;
@@ -44,14 +45,14 @@ public class AddAttributeTransformToSummaryOfImportedFieldsTest {
}
private static Search createSearchWithDocument(String documentName) {
- Search search = new Search(documentName, MockApplicationPackage.createEmpty());
+ Search search = new Search(documentName, MockApplicationPackage.createEmpty(), new TestableDeployLogger());
SDDocumentType document = new SDDocumentType(documentName, search);
search.addDocument(document);
return search;
}
private static ImportedFields createSingleImportedField(String fieldName) {
- Search targetSearch = new Search("target_doc", MockApplicationPackage.createEmpty());
+ Search targetSearch = new Search("target_doc", MockApplicationPackage.createEmpty(), new TestableDeployLogger());
SDField targetField = new SDField("target_field", DataType.INT);
DocumentReference documentReference = new DocumentReference(new Field("reference_field"), targetSearch);
ImportedField importedField = new ImportedSimpleField(fieldName, documentReference, targetField);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java
index 7863c544b60..833dbc14a53 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/IndexingScriptRewriterTestCase.java
@@ -153,7 +153,7 @@ public class IndexingScriptRewriterTestCase extends SchemaTestCase {
private static ScriptExpression processField(SDField unprocessedField) {
SDDocumentType sdoc = new SDDocumentType("test");
sdoc.addField(unprocessedField);
- Search search = new Search("test", null);
+ Search search = new Search("test");
search.addDocument(sdoc);
new Processing().process(search, new BaseDeployLogger(), new RankProfileRegistry(), new QueryProfiles(), true, false);
return unprocessedField.getIndexingScript();
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ParentChildSearchModel.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ParentChildSearchModel.java
index 7d6a8507dd3..ecafd7a4222 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ParentChildSearchModel.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ParentChildSearchModel.java
@@ -10,6 +10,7 @@ import com.yahoo.document.TemporaryStructuredDataType;
import com.yahoo.searchdefinition.DocumentReference;
import com.yahoo.searchdefinition.DocumentReferences;
import com.yahoo.searchdefinition.Search;
+import com.yahoo.searchdefinition.derived.TestableDeployLogger;
import com.yahoo.searchdefinition.document.SDDocumentType;
import com.yahoo.searchdefinition.document.SDField;
import com.yahoo.searchdefinition.document.TemporaryImportedField;
@@ -29,7 +30,7 @@ public class ParentChildSearchModel {
}
protected Search createSearch(String name) {
- Search result = new Search(name, app);
+ Search result = new Search(name, app, new TestableDeployLogger());
result.addDocument(new SDDocumentType(name));
return result;
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankProfileSearchFixture.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankProfileSearchFixture.java
index e6616ce0dd1..683242c1333 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankProfileSearchFixture.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankProfileSearchFixture.java
@@ -4,6 +4,7 @@ package com.yahoo.searchdefinition.processing;
import com.google.common.collect.ImmutableList;
import com.yahoo.config.application.api.ApplicationPackage;
import ai.vespa.rankingexpression.importer.configmodelview.MlModelImporter;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.path.Path;
import com.yahoo.search.query.profile.QueryProfileRegistry;
@@ -62,7 +63,7 @@ class RankProfileSearchFixture {
String rankProfiles, String constant, String field)
throws ParseException {
this.queryProfileRegistry = queryProfileRegistry;
- SearchBuilder builder = new SearchBuilder(applicationpackage, rankProfileRegistry, queryProfileRegistry);
+ SearchBuilder builder = new SearchBuilder(applicationpackage, new BaseDeployLogger(), rankProfileRegistry, queryProfileRegistry);
String sdContent = "search test {\n" +
" " + (constant != null ? constant : "") + "\n" +
" document test {\n" +
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolverTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolverTestCase.java
index 12fe7e151c0..e3d81be6743 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolverTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionTypeResolverTestCase.java
@@ -384,7 +384,7 @@ public class RankingExpressionTypeResolverTestCase {
@Test
public void undeclaredQueryFeaturesAreAccepted() throws Exception {
InspectableDeployLogger logger = new InspectableDeployLogger();
- SearchBuilder builder = new SearchBuilder();
+ SearchBuilder builder = new SearchBuilder(logger);
builder.importString(joinLines(
"search test {",
" document test { ",
@@ -401,8 +401,8 @@ public class RankingExpressionTypeResolverTestCase {
" }",
" }",
"}"
- ), logger);
- builder.build(true, logger);
+ ));
+ builder.build(true);
String message = logger.findMessage("The following query features");
assertNull(message);
}
@@ -410,7 +410,7 @@ public class RankingExpressionTypeResolverTestCase {
@Test
public void undeclaredQueryFeaturesAreAcceptedWithWarningWhenUsingTensors() throws Exception {
InspectableDeployLogger logger = new InspectableDeployLogger();
- SearchBuilder builder = new SearchBuilder();
+ SearchBuilder builder = new SearchBuilder(logger);
builder.importString(joinLines(
"search test {",
" document test { ",
@@ -427,8 +427,8 @@ public class RankingExpressionTypeResolverTestCase {
" }",
" }",
"}"
- ), logger);
- builder.build(true, logger);
+ ));
+ builder.build(true);
String message = logger.findMessage("The following query features");
assertNotNull(message);
assertEquals("WARNING: The following query features used in 'my_rank_profile' are not declared in query profile types and " +
@@ -439,7 +439,7 @@ public class RankingExpressionTypeResolverTestCase {
@Test
public void noWarningWhenUsingTensorsWhenQueryFeaturesAreDeclared() throws Exception {
InspectableDeployLogger logger = new InspectableDeployLogger();
- SearchBuilder builder = new SearchBuilder();
+ SearchBuilder builder = new SearchBuilder(logger);
QueryProfileType myType = new QueryProfileType("mytype");
myType.addField(new FieldDescription("rank.feature.query(foo)",
new TensorFieldType(TensorType.fromSpec("tensor(d[2])"))),
@@ -467,8 +467,8 @@ public class RankingExpressionTypeResolverTestCase {
" }",
" }",
"}"
- ), logger);
- builder.build(true, logger);
+ ));
+ builder.build(true);
String message = logger.findMessage("The following query features");
assertNull(message);
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTransformerTokensTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTransformerTokensTestCase.java
index c64dbcdef03..2ae6ffbf343 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTransformerTokensTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTransformerTokensTestCase.java
@@ -1,6 +1,7 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.processing;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.searchdefinition.RankProfile;
@@ -84,7 +85,7 @@ public class RankingExpressionWithTransformerTokensTestCase {
" document test {}\n" +
" rank-profile my_profile inherits default {}\n" +
"}";
- SearchBuilder searchBuilder = new SearchBuilder(application, rankProfileRegistry, queryProfileRegistry);
+ SearchBuilder searchBuilder = new SearchBuilder(application, new BaseDeployLogger(), rankProfileRegistry, queryProfileRegistry);
searchBuilder.importString(sdContent);
searchBuilder.build();
Search search = searchBuilder.getSearch();
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionsTestCase.java
index 8861432d97b..0695ae6a249 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionsTestCase.java
@@ -2,16 +2,22 @@
package com.yahoo.searchdefinition.processing;
import com.yahoo.collections.Pair;
+import com.yahoo.config.FileReference;
import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.search.query.profile.QueryProfileRegistry;
-import com.yahoo.searchdefinition.*;
+import com.yahoo.searchdefinition.RankProfile;
+import com.yahoo.searchdefinition.RankProfileRegistry;
+import com.yahoo.searchdefinition.SchemaTestCase;
+import com.yahoo.searchdefinition.Search;
+import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.derived.DerivedConfiguration;
import com.yahoo.searchdefinition.derived.AttributeFields;
import com.yahoo.searchdefinition.derived.RawRankProfile;
import com.yahoo.searchdefinition.derived.TestableDeployLogger;
import com.yahoo.searchdefinition.parser.ParseException;
import ai.vespa.rankingexpression.importer.configmodelview.ImportedMlModels;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import org.junit.Test;
import java.io.IOException;
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java
index eecab3c03d7..b0c65b3ea76 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReservedRankingExpressionFunctionNamesTestCase.java
@@ -21,7 +21,7 @@ public class ReservedRankingExpressionFunctionNamesTestCase {
public void requireThatFunctionsWithReservedNamesIssueAWarning() throws ParseException {
TestDeployLogger deployLogger = new TestDeployLogger();
RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
- SearchBuilder builder = new SearchBuilder(rankProfileRegistry);
+ SearchBuilder builder = new SearchBuilder(deployLogger, rankProfileRegistry);
builder.importString(
"search test {\n" +
" document test { \n" +
@@ -50,7 +50,7 @@ public class ReservedRankingExpressionFunctionNamesTestCase {
" }\n" +
" }\n" +
"}\n");
- builder.build(true, deployLogger);
+ builder.build(true);
assertTrue(deployLogger.log.contains("sigmoid") && deployLogger.log.contains("test_rank_profile"));
assertTrue(deployLogger.log.contains("sigmoid") && deployLogger.log.contains("test_rank_profile_2"));
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java
index 8308b638497..6ab74ef2eae 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/TensorTransformTestCase.java
@@ -195,7 +195,7 @@ public class TensorTransformTestCase extends SchemaTestCase {
" }\n" +
" }\n" +
"}\n");
- builder.build(true, new BaseDeployLogger());
+ builder.build(true);
Search s = builder.getSearch();
RankProfile test = rankProfileRegistry.get(s, "test").compile(queryProfiles, new ImportedMlModels());
List<Pair<String, String>> testRankProperties = new RawRankProfile(test,
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java
index cec313f98d8..28e70130e75 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java
@@ -6,6 +6,7 @@ import com.yahoo.document.DataType;
import com.yahoo.document.Field;
import com.yahoo.searchdefinition.DocumentReference;
import com.yahoo.searchdefinition.Search;
+import com.yahoo.searchdefinition.derived.TestableDeployLogger;
import com.yahoo.searchdefinition.document.ImportedField;
import com.yahoo.searchdefinition.document.ImportedFields;
import com.yahoo.searchdefinition.document.ImportedSimpleField;
@@ -45,14 +46,14 @@ public class ValidateFieldTypesTest {
}
private static Search createSearchWithDocument(String documentName) {
- Search search = new Search(documentName, MockApplicationPackage.createEmpty());
+ Search search = new Search(documentName, MockApplicationPackage.createEmpty(), new TestableDeployLogger());
SDDocumentType document = new SDDocumentType(documentName, search);
search.addDocument(document);
return search;
}
private static ImportedFields createSingleImportedField(String fieldName, DataType dataType) {
- Search targetSearch = new Search("target_doc", MockApplicationPackage.createEmpty());
+ Search targetSearch = new Search("target_doc", MockApplicationPackage.createEmpty(), new TestableDeployLogger());
SDField targetField = new SDField("target_field", dataType);
DocumentReference documentReference = new DocumentReference(new Field("reference_field"), targetSearch);
ImportedField importedField = new ImportedSimpleField(fieldName, documentReference, targetField);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
index 27a32f3e754..f9ccdb1a2aa 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.model.application.validation.change.search;
import com.yahoo.config.application.api.ValidationId;
-import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.model.application.validation.change.VespaConfigChangeAction;
import org.junit.Test;
@@ -11,9 +10,9 @@ import java.time.Instant;
import java.util.Arrays;
import java.util.List;
+import static com.yahoo.vespa.model.application.validation.change.ConfigChangeTestUtils.newRefeedAction;
import static com.yahoo.vespa.model.application.validation.change.ConfigChangeTestUtils.newReindexAction;
import static com.yahoo.vespa.model.application.validation.change.ConfigChangeTestUtils.newRestartAction;
-import static com.yahoo.vespa.model.application.validation.change.ConfigChangeTestUtils.newRefeedAction;
public class DocumentDatabaseChangeValidatorTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java
index da2d5e2e3a0..469e4649c14 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java
@@ -1,6 +1,11 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
+import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
+import com.yahoo.searchdefinition.derived.TestableDeployLogger;
+import com.yahoo.text.XML;
+import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import org.junit.Test;
import java.util.Optional;
@@ -43,7 +48,7 @@ public class ClusterResourceLimitsTest {
return this;
}
public ClusterResourceLimits build() {
- var builder = new ClusterResourceLimits.Builder(enableFeedBlockInDistributor);
+ var builder = new ClusterResourceLimits.Builder(enableFeedBlockInDistributor, false, new BaseDeployLogger());
builder.setClusterControllerBuilder(ctrlBuilder);
builder.setContentNodeBuilder(nodeBuilder);
return builder.build();
@@ -114,6 +119,29 @@ public class ClusterResourceLimitsTest {
new Fixture(true));
}
+ @Test
+ // TODO: Change to expect exception being thrown when no one uses this in hosted
+ public void default_resource_limits_when_hosted_and_warning_is_logged() {
+ TestableDeployLogger logger = new TestableDeployLogger();
+ final boolean hosted = true;
+
+ ClusterResourceLimits.Builder builder = new ClusterResourceLimits.Builder(true, hosted, logger);
+ ClusterResourceLimits limits = builder.build(new ModelElement(XML.getDocument("<cluster id=\"test\">" +
+ " <tuning>\n" +
+ " <resource-limits>\n" +
+ " <memory>0.92</memory>\n" +
+ " </resource-limits>\n" +
+ " </tuning>\n" +
+ "</cluster>")
+ .getDocumentElement()));
+
+ assertLimits(0.8, 0.8, limits.getClusterControllerLimits());
+ assertLimits(0.9, 0.9, limits.getContentNodeLimits());
+
+ assertEquals(1, logger.warnings.size());
+ assertEquals("Element resource-limits is not allowed, default limits will be used", logger.warnings.get(0));
+ }
+
private void assertLimits(Double expCtrlDisk, Double expCtrlMemory, Double expNodeDisk, Double expNodeMemory, Fixture f) {
var limits = f.build();
assertLimits(expCtrlDisk, expCtrlMemory, limits.getClusterControllerLimits());
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
index b1bd44d93b4..22e38b30959 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
@@ -1,6 +1,7 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.vespa.config.content.FleetcontrollerConfig;
@@ -22,9 +23,12 @@ public class FleetControllerClusterTest {
MockRoot root = new MockRoot("", deployState);
var clusterElement = new ModelElement(doc.getDocumentElement());
return new ClusterControllerConfig.Builder("storage",
- clusterElement,
- new ClusterResourceLimits.Builder(enableFeedBlockInDistributor).build(clusterElement).getClusterControllerLimits()).
- build(root.getDeployState(), root, clusterElement.getXml());
+ clusterElement,
+ new ClusterResourceLimits.Builder(enableFeedBlockInDistributor,
+ false,
+ new BaseDeployLogger())
+ .build(clusterElement).getClusterControllerLimits())
+ .build(root.getDeployState(), root, clusterElement.getXml());
}
private ClusterControllerConfig parse(String xml) {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/ml/ModelEvaluationTest.java b/config-model/src/test/java/com/yahoo/vespa/model/ml/ModelEvaluationTest.java
index bbf59fc66e4..bdae01d5e09 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/ml/ModelEvaluationTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/ml/ModelEvaluationTest.java
@@ -14,6 +14,7 @@ import com.yahoo.tensor.TensorType;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import org.junit.Test;
@@ -96,6 +97,10 @@ public class ModelEvaluationTest {
cluster.getConfig(cb);
RankingConstantsConfig constantsConfig = new RankingConstantsConfig(cb);
+ RankingExpressionsConfig.Builder ce = new RankingExpressionsConfig.Builder();
+ cluster.getConfig(ce);
+ RankingExpressionsConfig expressionsConfig = ce.build();
+
OnnxModelsConfig.Builder ob = new OnnxModelsConfig.Builder();
cluster.getConfig(ob);
OnnxModelsConfig onnxModelsConfig = new OnnxModelsConfig(ob);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java
index ea4b3db5ebb..66b60ec3782 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/search/test/SearchClusterTest.java
@@ -40,7 +40,7 @@ public class SearchClusterTest {
public void testSdConfigLogical() {
// sd1
SDDocumentType sdt1 = new SDDocumentType("s1");
- Search search1 = new Search("s1", null);
+ Search search1 = new Search("s1");
SDField f1 = new SDField("f1", DataType.STRING);
f1.addAttribute(new Attribute("f1", DataType.STRING));
f1.setIndexingScript(new ScriptExpression(new StatementExpression(new AttributeExpression("f1"))));
@@ -49,7 +49,7 @@ public class SearchClusterTest {
// sd2
SDDocumentType sdt2 = new SDDocumentType("s2");
- Search search2 = new Search("s2", null);
+ Search search2 = new Search("s2");
SDField f2=new SDField("f2", DataType.STRING);
f2.addAttribute(new Attribute("f2", DataType.STRING));
f2.setIndexingScript(new ScriptExpression(new StatementExpression(new AttributeExpression("f2"))));
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
index eb462c86f4f..c3b2bd9f69a 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
@@ -7,6 +7,7 @@ import com.yahoo.config.provisioning.FlavorsConfig;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java
index 0548bc7520f..4454517d5ec 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java
@@ -280,6 +280,17 @@ public class NodeResources {
public boolean isUnspecified() { return this.equals(unspecified); }
+ // Returns squared euclidean distance of the relevant numerical values of two node resources
+ public double distanceTo(NodeResources other) {
+ if ( ! this.diskSpeed().compatibleWith(other.diskSpeed())) return Double.MAX_VALUE;
+ if ( ! this.storageType().compatibleWith(other.storageType())) return Double.MAX_VALUE;
+
+ double distance = Math.pow(this.vcpu() - other.vcpu(), 2) + Math.pow(this.memoryGb() - other.memoryGb(), 2);
+ if (this.storageType() == StorageType.local || other.storageType() == StorageType.local)
+ distance += Math.pow(this.diskGb() - other.diskGb(), 2);
+ return distance;
+ }
+
/** Returns this.isUnspecified() ? Optional.empty() : Optional.of(this) */
public Optional<NodeResources> asOptional() {
return this.isUnspecified() ? Optional.empty() : Optional.of(this);
diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterResourcesTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterResourcesTest.java
new file mode 100644
index 00000000000..2ae5347da12
--- /dev/null
+++ b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterResourcesTest.java
@@ -0,0 +1,20 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.provision;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author bratseth
+ */
+public class ClusterResourcesTest {
+
+ @Test
+ public void testCost() {
+ ClusterResources r1 = new ClusterResources(3, 1, new NodeResources(2, 8, 50, 1));
+ ClusterResources r2 = new ClusterResources(3, 1, new NodeResources(2, 16, 50, 1));
+ assertEquals(1.818, r1.cost() + r2.cost(), 0.01);
+ }
+
+}
diff --git a/configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java b/configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java
index 608e9fe18b4..910f7a1402b 100644
--- a/configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java
+++ b/configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java
@@ -1,7 +1,6 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.hosted.client;
-import org.apache.hc.client5.http.classic.methods.ClassicHttpRequests;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.ClassicHttpRequest;
@@ -12,6 +11,7 @@ import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.HttpEntities;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.net.URIBuilder;
import java.io.IOException;
@@ -49,7 +49,9 @@ public abstract class AbstractConfigServerClient implements ConfigServerClient {
Throwable thrown = null;
for (URI host : builder.hosts) {
- ClassicHttpRequest request = ClassicHttpRequests.create(builder.method, concat(host, builder.uriBuilder));
+ ClassicHttpRequest request = ClassicRequestBuilder.create(builder.method.name())
+ .setUri(concat(host, builder.uriBuilder))
+ .build();
request.setEntity(builder.entity);
try {
try {
diff --git a/configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java b/configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java
index b2f17f43f5c..956bc90380f 100644
--- a/configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java
+++ b/configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java
@@ -89,7 +89,7 @@ class HttpConfigServerClientTest {
() -> client.send(HostStrategy.repeating(URI.create("http://localhost:" + server.port()), 10),
Method.GET)
.read(String::new));
- assertEquals("GET / failed with status 409 and body 'hi'", thrown.getMessage());
+ assertEquals("GET http://localhost:" + server.port() + "/ failed with status 409 and body 'hi'", thrown.getMessage());
server.verify(1, getRequestedFor(urlEqualTo("/")));
server.verify(1, anyRequestedFor(anyUrl()));
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
index 2dd655c91d8..a6e15223f74 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
@@ -13,14 +13,13 @@ import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.config.server.http.JSONResponse;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
-import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.TimeValue;
@@ -152,7 +151,7 @@ public class ConfigConvergenceChecker extends AbstractComponent {
/** Get service generation of service at given URL */
private CompletableFuture<Long> getServiceGeneration(CloseableHttpAsyncClient client, URI serviceUrl, Duration timeout) {
- SimpleHttpRequest request = SimpleHttpRequests.get(createApiUri(serviceUrl));
+ SimpleHttpRequest request = SimpleRequestBuilder.get(createApiUri(serviceUrl)).build();
request.setConfig(createRequestConfig(timeout));
// Ignoring returned Future object as we want to use the more flexible CompletableFuture instead
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/DefaultClusterReindexingStatusClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/DefaultClusterReindexingStatusClient.java
index 1887bf1db9d..65a960e38f4 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/DefaultClusterReindexingStatusClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/DefaultClusterReindexingStatusClient.java
@@ -10,8 +10,8 @@ import com.yahoo.config.model.api.PortInfo;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.config.server.modelfactory.ModelResult;
-import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.core5.concurrent.FutureCallback;
@@ -80,7 +80,7 @@ public class DefaultClusterReindexingStatusClient implements ClusterReindexingSt
private CompletableFuture<Map<String, ClusterReindexing>> getReindexingStatus(ServiceInfo service) {
URI uri = URI.create(String.format("http://%s:%d/reindexing/v1/status", service.getHostName(), getStatePort(service)));
CompletableFuture<SimpleHttpResponse> responsePromise = new CompletableFuture<>();
- httpClient.execute(SimpleHttpRequests.get(uri), new FutureCallback<>() {
+ httpClient.execute(SimpleRequestBuilder.get(uri).build(), new FutureCallback<>() {
@Override public void completed(SimpleHttpResponse result) { responsePromise.complete(result); }
@Override public void failed(Exception ex) { responsePromise.completeExceptionally(ex); }
@Override public void cancelled() { responsePromise.cancel(false); }
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/InfraDeployerProvider.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/InfraDeployerProvider.java
index bc15121e190..1631774112b 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/InfraDeployerProvider.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/InfraDeployerProvider.java
@@ -1,4 +1,4 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.deploy;
import com.yahoo.cloud.config.ConfigserverConfig;
@@ -50,10 +50,7 @@ public class InfraDeployerProvider {
/** Creates either an empty provider or a provider having the given infrastructure deployer */
public static InfraDeployerProvider from(Optional<InfraDeployer> infraDeployer) {
- if (infraDeployer.isPresent())
- return withInfraDeployer(infraDeployer.get());
- else
- return empty();
+ return infraDeployer.map(InfraDeployerProvider::withInfraDeployer).orElseGet(InfraDeployerProvider::empty);
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index 16bcca2ea9c..f44694f5066 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -174,8 +174,8 @@ public class ModelContextImpl implements ModelContext {
private final boolean tenantIamRole;
private final int maxActivationInhibitedOutOfSyncGroups;
private final ToIntFunction<ClusterSpec.Type> jvmOmitStackTraceInFastThrow;
- private final boolean enableJdiscHttp2;
private final boolean enableCustomAclMapping;
+ private final boolean useExternalRankExpression;
private final int numDistributorStripes;
public FeatureFlags(FlagSource source, ApplicationId appId) {
@@ -197,9 +197,9 @@ public class ModelContextImpl implements ModelContext {
this.tenantIamRole = flagValue(source, appId.tenant(), Flags.TENANT_IAM_ROLE);
this.maxActivationInhibitedOutOfSyncGroups = flagValue(source, appId, Flags.MAX_ACTIVATION_INHIBITED_OUT_OF_SYNC_GROUPS);
this.jvmOmitStackTraceInFastThrow = type -> flagValueAsInt(source, appId, type, PermanentFlags.JVM_OMIT_STACK_TRACE_IN_FAST_THROW);
- this.enableJdiscHttp2 = flagValue(source, appId, Flags.ENABLE_JDISC_HTTP2);
this.enableCustomAclMapping = flagValue(source, appId, Flags.ENABLE_CUSTOM_ACL_MAPPING);
this.numDistributorStripes = flagValue(source, appId, Flags.NUM_DISTRIBUTOR_STRIPES);
+ this.useExternalRankExpression = flagValue(source, appId, Flags.USE_EXTERNAL_RANK_EXPRESSION);;
}
@Override public Optional<NodeResources> dedicatedClusterControllerFlavor() { return Optional.ofNullable(dedicatedClusterControllerFlavor); }
@@ -222,9 +222,9 @@ public class ModelContextImpl implements ModelContext {
@Override public String jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type type) {
return translateJvmOmitStackTraceInFastThrowIntToString(jvmOmitStackTraceInFastThrow, type);
}
- @Override public boolean enableJdiscHttp2() { return enableJdiscHttp2; }
@Override public boolean enableCustomAclMapping() { return enableCustomAclMapping; }
@Override public int numDistributorStripes() { return numDistributorStripes; }
+ @Override public boolean useExternalRankExpressions() { return useExternalRankExpression; }
private static <V> V flagValue(FlagSource source, ApplicationId appId, UnboundFlag<? extends V, ?, ?> flag) {
return flag.bindTo(source)
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
index 858a2c84e02..1ab99db8985 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.deploy;
import com.yahoo.component.Version;
@@ -17,6 +17,7 @@ import com.yahoo.path.Path;
import com.yahoo.vespa.config.ConfigDefinitionKey;
import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
import com.yahoo.vespa.config.server.zookeeper.ZKApplicationPackage;
+import com.yahoo.yolean.Exceptions;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -27,6 +28,12 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
+import static com.yahoo.config.application.api.ApplicationPackage.*;
+import static com.yahoo.vespa.config.server.zookeeper.ConfigCurator.DEFCONFIGS_ZK_SUBPATH;
+import static com.yahoo.vespa.config.server.zookeeper.ConfigCurator.META_ZK_PATH;
+import static com.yahoo.vespa.config.server.zookeeper.ConfigCurator.USERAPP_ZK_SUBPATH;
+import static com.yahoo.vespa.config.server.zookeeper.ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH;
+
/**
* A class used for reading and writing application data to zookeeper.
*
@@ -36,62 +43,30 @@ public class ZooKeeperClient {
private final ConfigCurator configCurator;
private final DeployLogger logger;
- /* This is the generation that will be used for reading and writing application data. (1 more than last deployed application) */
- private final Path rootPath;
+ private final Path sessionPath; // session id
private static final ApplicationFile.PathFilter xmlFilter = path -> path.getName().endsWith(".xml");
- public ZooKeeperClient(ConfigCurator configCurator, DeployLogger logger, Path rootPath) {
+ public ZooKeeperClient(ConfigCurator configCurator, DeployLogger logger, Path sessionPath) {
this.configCurator = configCurator;
this.logger = logger;
- this.rootPath = rootPath;
+ this.sessionPath = sessionPath;
}
/**
* Sets up basic node structure in ZooKeeper and purges old data.
- * This is the first operation on ZK during deploy-application.
- *
- * We have retries in this method because there have been cases of stray connection loss to ZK,
- * even though the user has started the config server.
- *
+ * This is the first operation on ZK during deploy.
*/
- void setupZooKeeper() {
- int retries = 5;
- try {
- while (retries > 0) {
- try {
- createZooKeeperNodes();
- break;
- } catch (RuntimeException e) {
- logger.log(Level.FINE, "ZK init failed, retrying: " + e);
- retries--;
- if (retries == 0) {
- throw e;
- }
- Thread.sleep(100);
- // Not reconnecting, ZK is supposed to handle that automatically
- // as long as the session doesn't expire. We'll see.
- }
- }
- } catch (Exception e) {
- throw new IllegalStateException("Unable to initialize vespa model writing to config server(s) " +
- System.getProperty("configsources") + "\n" +
- "Please ensure that cloudconfig_server is started on the config server node(s), " +
- "and check the vespa log for configserver errors. ", e);
- }
- }
-
- /** Sets the app id and attempts to set up zookeeper. The app id must be ordered for purge to work OK. */
- private void createZooKeeperNodes() {
- if ( ! configCurator.exists(rootPath.getAbsolute()))
- configCurator.createNode(rootPath.getAbsolute());
+ void initialize() {
+ if ( ! configCurator.exists(sessionPath.getAbsolute()))
+ configCurator.createNode(sessionPath.getAbsolute());
- for (String subPath : Arrays.asList(ConfigCurator.DEFCONFIGS_ZK_SUBPATH,
- ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH,
- ConfigCurator.USERAPP_ZK_SUBPATH,
+ for (String subPath : Arrays.asList(DEFCONFIGS_ZK_SUBPATH,
+ USER_DEFCONFIGS_ZK_SUBPATH,
+ USERAPP_ZK_SUBPATH,
ZKApplicationPackage.fileRegistryNode)) {
// TODO: The replaceFirst below is hackish.
- configCurator.createNode(getZooKeeperAppPath(null).getAbsolute(),
+ configCurator.createNode(getZooKeeperAppPath().getAbsolute(),
subPath.replaceFirst("/", ""));
}
}
@@ -110,7 +85,7 @@ public class ZooKeeperClient {
write(app.getMetaData());
} catch (Exception e) {
throw new IllegalStateException("Unable to write vespa model to config server(s) " + System.getProperty("configsources") + "\n" +
- "Please ensure that cloudconfig_server is started on the config server node(s), " +
+ "Please ensure that config server is started " +
"and check the vespa log for configserver errors. ", e);
}
}
@@ -120,7 +95,8 @@ public class ZooKeeperClient {
if (sds.isEmpty()) return;
// TODO: Change to SCHEMAS_DIR after March 2020
- Path zkPath = getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.SEARCH_DEFINITIONS_DIR);
+ // TODO: When it does also check RankExpressionFile.sendTo
+ Path zkPath = getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(ApplicationPackage.SEARCH_DEFINITIONS_DIR);
configCurator.createNode(zkPath.getAbsolute());
// Ensures that ranking expressions and other files are also written
writeDir(app.getFile(ApplicationPackage.SEARCH_DEFINITIONS_DIR), zkPath, false);
@@ -139,38 +115,34 @@ public class ZooKeeperClient {
*/
private void writeSomeOf(ApplicationPackage app) throws IOException {
// TODO: We should have a way of doing this which doesn't require repeating all the content
- writeFile(app.getFile(Path.fromString(ApplicationPackage.SERVICES)),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH));
- writeFile(app.getFile(Path.fromString(ApplicationPackage.HOSTS)),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH));
- writeFile(app.getFile(Path.fromString(ApplicationPackage.DEPLOYMENT_FILE.getName())),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH));
- writeFile(app.getFile(Path.fromString(ApplicationPackage.VALIDATION_OVERRIDES.getName())),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH));
- writeDir(app.getFile(ApplicationPackage.RULES_DIR),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.RULES_DIR),
+ writeFile(app.getFile(Path.fromString(SERVICES)), getZooKeeperAppPath(USERAPP_ZK_SUBPATH));
+ writeFile(app.getFile(Path.fromString(HOSTS)), getZooKeeperAppPath(USERAPP_ZK_SUBPATH));
+ writeFile(app.getFile(Path.fromString(DEPLOYMENT_FILE.getName())), getZooKeeperAppPath(USERAPP_ZK_SUBPATH));
+ writeFile(app.getFile(Path.fromString(VALIDATION_OVERRIDES.getName())), getZooKeeperAppPath(USERAPP_ZK_SUBPATH));
+ writeDir(app.getFile(RULES_DIR),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(RULES_DIR),
(path) -> path.getName().endsWith(ApplicationPackage.RULES_NAME_SUFFIX),
true);
- writeDir(app.getFile(ApplicationPackage.QUERY_PROFILES_DIR),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.QUERY_PROFILES_DIR),
+ writeDir(app.getFile(QUERY_PROFILES_DIR),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(QUERY_PROFILES_DIR),
xmlFilter, true);
- writeDir(app.getFile(ApplicationPackage.PAGE_TEMPLATES_DIR),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.PAGE_TEMPLATES_DIR),
+ writeDir(app.getFile(PAGE_TEMPLATES_DIR),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(PAGE_TEMPLATES_DIR),
xmlFilter, true);
- writeDir(app.getFile(Path.fromString(ApplicationPackage.SEARCHCHAINS_DIR)),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.SEARCHCHAINS_DIR),
+ writeDir(app.getFile(Path.fromString(SEARCHCHAINS_DIR)),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(SEARCHCHAINS_DIR),
xmlFilter, true);
- writeDir(app.getFile(Path.fromString(ApplicationPackage.DOCPROCCHAINS_DIR)),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.DOCPROCCHAINS_DIR),
+ writeDir(app.getFile(Path.fromString(DOCPROCCHAINS_DIR)),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(DOCPROCCHAINS_DIR),
xmlFilter, true);
- writeDir(app.getFile(Path.fromString(ApplicationPackage.ROUTINGTABLES_DIR)),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.ROUTINGTABLES_DIR),
+ writeDir(app.getFile(Path.fromString(ROUTINGTABLES_DIR)),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(ROUTINGTABLES_DIR),
xmlFilter, true);
- writeDir(app.getFile(ApplicationPackage.MODELS_GENERATED_REPLICATED_DIR),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.MODELS_GENERATED_REPLICATED_DIR),
+ writeDir(app.getFile(MODELS_GENERATED_REPLICATED_DIR),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(MODELS_GENERATED_REPLICATED_DIR),
true);
- writeDir(app.getFile(ApplicationPackage.SECURITY_DIR),
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.SECURITY_DIR),
+ writeDir(app.getFile(SECURITY_DIR),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH).append(SECURITY_DIR),
true);
}
@@ -233,10 +205,10 @@ public class ZooKeeperClient {
ApplicationFile dir = applicationPackage.getFile(Path.fromString(userInclude));
final List<ApplicationFile> files = dir.listFiles();
if (files == null || files.isEmpty()) {
- configCurator.createNode(getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + userInclude).getAbsolute());
+ configCurator.createNode(getZooKeeperAppPath(USERAPP_ZK_SUBPATH + "/" + userInclude).getAbsolute());
}
writeDir(dir,
- getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + userInclude),
+ getZooKeeperAppPath(USERAPP_ZK_SUBPATH + "/" + userInclude),
xmlFilter, true);
}
}
@@ -250,8 +222,8 @@ public class ZooKeeperClient {
for (Map.Entry<ConfigDefinitionKey, UnparsedConfigDefinition> entry : configDefs.entrySet()) {
ConfigDefinitionKey key = entry.getKey();
String contents = entry.getValue().getUnparsedContent();
- writeConfigDefinition(key.getName(), key.getNamespace(), getZooKeeperAppPath(ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
- writeConfigDefinition(key.getName(), key.getNamespace(), getZooKeeperAppPath(ConfigCurator.DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
+ writeConfigDefinition(key.getName(), key.getNamespace(), getZooKeeperAppPath(USER_DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
+ writeConfigDefinition(key.getName(), key.getNamespace(), getZooKeeperAppPath(DEFCONFIGS_ZK_SUBPATH).getAbsolute(), contents);
}
logger.log(Level.FINE, configDefs.size() + " user config definitions");
}
@@ -262,8 +234,7 @@ public class ZooKeeperClient {
private void write(Version vespaVersion, FileRegistry fileRegistry) {
String exportedRegistry = PreGeneratedFileRegistry.exportRegistry(fileRegistry);
-
- configCurator.putData(getZooKeeperAppPath(null).append(ZKApplicationPackage.fileRegistryNode).getAbsolute(),
+ configCurator.putData(getZooKeeperAppPath(ZKApplicationPackage.fileRegistryNode).getAbsolute(),
vespaVersion.toFullString(),
exportedRegistry);
}
@@ -275,40 +246,42 @@ public class ZooKeeperClient {
* @param metaData The application metadata.
*/
private void write(ApplicationMetaData metaData) {
- configCurator.putData(getZooKeeperAppPath(ConfigCurator.META_ZK_PATH).getAbsolute(), metaData.asJsonBytes());
+ configCurator.putData(getZooKeeperAppPath(META_ZK_PATH).getAbsolute(), metaData.asJsonBytes());
}
void cleanupZooKeeper() {
try {
- for (String subPath : Arrays.asList(
- ConfigCurator.DEFCONFIGS_ZK_SUBPATH,
- ConfigCurator.USER_DEFCONFIGS_ZK_SUBPATH,
- ConfigCurator.USERAPP_ZK_SUBPATH)) {
- configCurator.deleteRecurse(getZooKeeperAppPath(null).append(subPath).getAbsolute());
- }
+ List.of(DEFCONFIGS_ZK_SUBPATH, USER_DEFCONFIGS_ZK_SUBPATH, USERAPP_ZK_SUBPATH)
+ .forEach(path -> configCurator.deleteRecurse(getZooKeeperAppPath(path).getAbsolute()));
} catch (Exception e) {
- logger.log(Level.WARNING, "Could not clean up in zookeeper");
+ logger.log(Level.WARNING, "Could not clean up in zookeeper: " + Exceptions.toMessageString(e));
//Might be called in an exception handler before re-throw, so do not throw here.
}
}
/**
- * Gets a full ZK app path based on id set in Admin object
+ * Gets a full ZK application path
*
+ * @return a String with the full ZK application path
+ */
+ private Path getZooKeeperAppPath() {
+ return getZooKeeperAppPath(null);
+ }
+
+ /**
+ * Gets a full ZK application path
*
* @param trailingPath trailing part of path to be appended to ZK app path
* @return a String with the full ZK application path including trailing path, if set
*/
private Path getZooKeeperAppPath(String trailingPath) {
- if (trailingPath != null) {
- return rootPath.append(trailingPath);
- } else {
- return rootPath;
- }
+ if (trailingPath == null) return sessionPath;
+
+ return sessionPath.append(trailingPath);
}
public void write(AllocatedHosts hosts) throws IOException {
- configCurator.putData(rootPath.append(ZKApplicationPackage.allocatedHostsNode).getAbsolute(),
+ configCurator.putData(sessionPath.append(ZKApplicationPackage.allocatedHostsNode).getAbsolute(),
AllocatedHostsSerializer.toJson(hosts));
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployer.java
index e24d24f0c3f..12aa5b7cc35 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperDeployer.java
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.deploy;
import com.yahoo.config.application.api.ApplicationPackage;
@@ -11,7 +11,6 @@ import java.util.Map;
/**
* Interface for initializing zookeeper and deploying an application package to zookeeper.
- * Initialize must be called before each deploy.
*
* @author Ulf Lilleengen
*/
@@ -24,16 +23,16 @@ public class ZooKeeperDeployer {
}
/**
- * Deploys an application package to zookeeper. initialize() must be called before calling this method.
+ * Deploys an application package to zookeeper
*
* @param applicationPackage the application package to persist.
* @param fileRegistryMap the file registries to persist.
- * @param allocatedHosts the provisioning info to persist.
+ * @param allocatedHosts the allocated hosts to persist.
* @throws IOException if deploying fails
*/
public void deploy(ApplicationPackage applicationPackage, Map<Version, FileRegistry> fileRegistryMap,
AllocatedHosts allocatedHosts) throws IOException {
- zooKeeperClient.setupZooKeeper();
+ zooKeeperClient.initialize();
zooKeeperClient.write(applicationPackage);
zooKeeperClient.write(fileRegistryMap);
zooKeeperClient.write(allocatedHosts);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java
index 8bc792a179d..5b520b10fcf 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationApiHandler.java
@@ -16,6 +16,8 @@ import com.yahoo.vespa.config.server.http.SessionHandler;
import com.yahoo.vespa.config.server.http.Utils;
import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
+import com.yahoo.vespa.model.content.Content;
+import org.apache.hc.core5.http.ContentType;
import org.eclipse.jetty.http.MultiPartFormInputStream;
import javax.servlet.http.Part;
@@ -70,7 +72,8 @@ public class ApplicationApiHandler extends SessionHandler {
PrepareParams prepareParams;
CompressedApplicationInputStream compressedStream;
boolean multipartRequest = Optional.ofNullable(request.getHeader(HttpHeaders.Names.CONTENT_TYPE))
- .map(val -> val.equalsIgnoreCase(MULTIPART_FORM_DATA))
+ .map(ContentType::parse)
+ .map(contentType -> contentType.getMimeType().equalsIgnoreCase(MULTIPART_FORM_DATA))
.orElse(false);
if(multipartRequest) {
try {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java
index a3e82c51dfa..61f099fb8ea 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandler.java
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.jdisc.HeaderFields;
import com.yahoo.jdisc.application.UriPattern;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.deploy.DeployHandlerLogger;
@@ -16,6 +17,8 @@ import com.yahoo.vespa.config.server.TimeoutBudget;
import com.yahoo.vespa.config.server.http.BadRequestException;
import com.yahoo.vespa.config.server.http.SessionHandler;
import com.yahoo.vespa.config.server.http.Utils;
+import com.yahoo.vespa.model.content.Content;
+import org.apache.hc.core5.http.ContentType;
import java.net.URI;
import java.time.Duration;
@@ -93,9 +96,12 @@ public class SessionCreateHandler extends SessionHandler {
String header = request.getHeader(ApplicationApiHandler.contentTypeHeader);
if (header == null) {
throw new BadRequestException("Request contains no " + ApplicationApiHandler.contentTypeHeader + " header");
- } else if (!supportedContentTypes.contains(header)) {
- throw new BadRequestException("Request contains invalid " + ApplicationApiHandler.contentTypeHeader + " header, only '["
- + String.join(", ", supportedContentTypes) + "' are supported");
+ } else {
+ ContentType contentType = ContentType.parse(header);
+ if (!supportedContentTypes.contains(contentType.getMimeType())) {
+ throw new BadRequestException("Request contains invalid " + ApplicationApiHandler.contentTypeHeader + " header (" + contentType.getMimeType() + "), only '["
+ + String.join(", ", supportedContentTypes) + "]' are supported");
+ }
}
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
index 5c0207878f1..f878831b8c5 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
@@ -76,8 +76,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
ConfigserverConfig configserverConfig,
Zone zone,
ModelFactoryRegistry modelFactoryRegistry,
- ConfigDefinitionRepo configDefinitionRepo,
- TenantListener tenantListener) {
+ ConfigDefinitionRepo configDefinitionRepo) {
super(modelFactoryRegistry,
configserverConfig,
zone,
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
index 30cdc0f6e8a..50b9ac55bda 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
@@ -27,7 +27,6 @@ import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.lang.SettableOptional;
import com.yahoo.path.Path;
import com.yahoo.vespa.config.server.TimeoutBudget;
-import com.yahoo.vespa.config.server.application.ApplicationCuratorDatabase;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.application.PermanentApplicationPackage;
import com.yahoo.vespa.config.server.configchange.ConfigChangeActions;
@@ -44,7 +43,6 @@ import com.yahoo.vespa.config.server.tenant.ApplicationRolesStore;
import com.yahoo.vespa.config.server.tenant.ContainerEndpointsCache;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataStore;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateRetriever;
-import com.yahoo.vespa.config.server.tenant.SecretStoreExternalIdRetriever;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
index cb46d65c4c5..7d74b53fdff 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
@@ -121,7 +121,6 @@ public class SessionRepository {
private final Zone zone;
private final ModelFactoryRegistry modelFactoryRegistry;
private final ConfigDefinitionRepo configDefinitionRepo;
- private final TenantListener tenantListener;
public SessionRepository(TenantName tenantName,
TenantApplications applicationRepo,
@@ -139,8 +138,7 @@ public class SessionRepository {
Zone zone,
Clock clock,
ModelFactoryRegistry modelFactoryRegistry,
- ConfigDefinitionRepo configDefinitionRepo,
- TenantListener tenantListener) {
+ ConfigDefinitionRepo configDefinitionRepo) {
this.tenantName = tenantName;
this.configCurator = configCurator;
sessionCounter = new SessionCounter(configCurator, tenantName);
@@ -163,7 +161,6 @@ public class SessionRepository {
this.zone = zone;
this.modelFactoryRegistry = modelFactoryRegistry;
this.configDefinitionRepo = configDefinitionRepo;
- this.tenantListener = tenantListener;
loadSessions(); // Needs to be done before creating cache below
this.directoryCache = curator.createDirectoryCache(sessionsPath.getAbsolute(), false, false, zkCacheExecutor);
@@ -502,8 +499,7 @@ public class SessionRepository {
configserverConfig,
zone,
modelFactoryRegistry,
- configDefinitionRepo,
- tenantListener);
+ configDefinitionRepo);
// Read hosts allocated on the config server instance which created this
SettableOptional<AllocatedHosts> allocatedHosts = new SettableOptional<>(applicationPackage.getAllocatedHosts());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
index 1c1d2534341..21db290d5e8 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
@@ -357,8 +357,7 @@ public class TenantRepository {
zone,
clock,
modelFactoryRegistry,
- configDefinitionRepo,
- tenantListener);
+ configDefinitionRepo);
log.log(Level.INFO, "Adding tenant '" + tenantName + "'" + ", created " + created +
". Bootstrapping in " + Duration.between(start, Instant.now()));
Tenant tenant = new Tenant(tenantName, sessionRepository, applicationRepo, created);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java
index 665f37759b4..f01bf1fe6ef 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/zookeeper/ZKApplicationPackage.java
@@ -45,7 +45,6 @@ public class ZKApplicationPackage implements ApplicationPackage {
private final Map<Version, PreGeneratedFileRegistry> fileRegistryMap = new HashMap<>();
private final Optional<AllocatedHosts> allocatedHosts;
- private static final Version legacyVersion = new Version(0, 0, 0);
public static final String fileRegistryNode = "fileregistry";
public static final String allocatedHostsNode = "allocatedHosts";
@@ -60,7 +59,7 @@ public class ZKApplicationPackage implements ApplicationPackage {
}
private Optional<AllocatedHosts> importAllocatedHosts() {
- if ( ! zkApplication.exists(ZKApplicationPackage.allocatedHostsNode)) return Optional.empty();
+ if ( ! zkApplication.exists(allocatedHostsNode)) return Optional.empty();
return Optional.of(readAllocatedHosts());
}
@@ -71,21 +70,18 @@ public class ZKApplicationPackage implements ApplicationPackage {
*/
private AllocatedHosts readAllocatedHosts() {
try {
- return AllocatedHostsSerializer.fromJson(zkApplication.getBytes(ZKApplicationPackage.allocatedHostsNode));
+ return AllocatedHostsSerializer.fromJson(zkApplication.getBytes(allocatedHostsNode));
} catch (Exception e) {
throw new RuntimeException("Unable to read allocated hosts", e);
}
}
private void importFileRegistries() {
- List<String> fileRegistryNodes = zkApplication.getChildren(ZKApplicationPackage.fileRegistryNode);
- if (fileRegistryNodes.isEmpty()) {
- fileRegistryMap.put(legacyVersion, importFileRegistry(ZKApplicationPackage.fileRegistryNode));
- } else {
- fileRegistryNodes.forEach(version ->
- fileRegistryMap.put(Version.fromString(version),
- importFileRegistry(Joiner.on("/").join(ZKApplicationPackage.fileRegistryNode, version))));
- }
+ List<String> perVersionFileRegistryNodes = zkApplication.getChildren(fileRegistryNode);
+ perVersionFileRegistryNodes
+ .forEach(version ->
+ fileRegistryMap.put(Version.fromString(version),
+ importFileRegistry(Joiner.on("/").join(fileRegistryNode, version))));
}
private PreGeneratedFileRegistry importFileRegistry(String fileRegistryNode) {
@@ -139,11 +135,11 @@ public class ZKApplicationPackage implements ApplicationPackage {
public List<NamedReader> searchDefinitionContents() {
List<NamedReader> schemas = new ArrayList<>();
for (String sd : zkApplication.getChildren(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + SEARCH_DEFINITIONS_DIR)) {
- if (sd.endsWith(ApplicationPackage.SD_NAME_SUFFIX))
+ if (sd.endsWith(SD_NAME_SUFFIX))
schemas.add(new NamedReader(sd, new StringReader(zkApplication.getData(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + SEARCH_DEFINITIONS_DIR, sd))));
}
for (String sd : zkApplication.getChildren(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + SCHEMAS_DIR)) {
- if (sd.endsWith(ApplicationPackage.SD_NAME_SUFFIX))
+ if (sd.endsWith(SD_NAME_SUFFIX))
schemas.add(new NamedReader(sd, new StringReader(zkApplication.getData(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + SCHEMAS_DIR, sd))));
}
return schemas;
@@ -251,7 +247,7 @@ public class ZKApplicationPackage implements ApplicationPackage {
List<ComponentInfo> components = new ArrayList<>();
PreGeneratedFileRegistry fileRegistry = getPreGeneratedFileRegistry(vespaVersion).get();
for (String path : fileRegistry.getPaths()) {
- if (path.startsWith(ApplicationPackage.COMPONENT_DIR + File.separator) && path.endsWith(".jar")) {
+ if (path.startsWith(COMPONENT_DIR + File.separator) && path.endsWith(".jar")) {
ComponentInfo component = new ComponentInfo(path);
components.add(component);
}
@@ -266,9 +262,7 @@ public class ZKApplicationPackage implements ApplicationPackage {
@Override
public Reader getRankingExpression(String name) {
Optional<Reader> reader = zkApplication.getOptionalDataReader(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + SCHEMAS_DIR, name);
- if (reader.isPresent())
- return reader.get();
- return zkApplication.getDataReader(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + SEARCH_DEFINITIONS_DIR, name);
+ return reader.orElseGet(() -> zkApplication.getDataReader(ConfigCurator.USERAPP_ZK_SUBPATH + "/" + SEARCH_DEFINITIONS_DIR, name));
}
@Override
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
index deff0aba376..7d14b1996b0 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClientTest.java
@@ -63,7 +63,7 @@ public class ZooKeeperClientTest {
2L));
Map<Version, FileRegistry> fileRegistries = createFileRegistries();
app.writeMetaData();
- zkc.setupZooKeeper();
+ zkc.initialize();
zkc.write(app);
zkc.write(fileRegistries);
}
@@ -85,7 +85,7 @@ public class ZooKeeperClientTest {
BaseDeployLogger logger = new BaseDeployLogger();
long generation = 1L;
ZooKeeperClient zooKeeperClient = new ZooKeeperClient(zk, logger, Path.fromString("/1"));
- zooKeeperClient.setupZooKeeper();
+ zooKeeperClient.initialize();
String appPath = "/";
assertThat(zk.getChildren(appPath).size(), is(1));
assertTrue(zk.exists("/" + generation));
@@ -157,7 +157,7 @@ public class ZooKeeperClientTest {
BaseDeployLogger logger = new BaseDeployLogger();
Path app = Path.fromString("/1");
ZooKeeperClient zooKeeperClient = new ZooKeeperClient(zk, logger, app);
- zooKeeperClient.setupZooKeeper();
+ zooKeeperClient.initialize();
HostSpec host1 = new HostSpec("host1.yahoo.com", Collections.emptyList(), Optional.empty());
HostSpec host2 = new HostSpec("host2.yahoo.com", Collections.emptyList(), Optional.empty());
ImmutableSet<HostSpec> hosts = ImmutableSet.of(host1, host2);
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
index 83d6ac5b288..5d7322070e7 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/SessionCreateHandlerTest.java
@@ -16,19 +16,24 @@ import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.session.Session;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.config.server.tenant.TestTenantRepository;
+import org.apache.hc.core5.http.ContentType;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
@@ -43,6 +48,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* @author hmusum
@@ -171,6 +177,22 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
assertIllegalFromParameter("http://host:4013/application/v2/tenant/" + tenant + "/application/foo/environment/prod/region/baz/instance");
}
+ @Test
+ public void require_that_content_type_is_parsed_correctly() throws FileNotFoundException {
+ HttpRequest request = post(new ByteArrayInputStream("foo".getBytes(StandardCharsets.UTF_8)),
+ Map.of("Content-Type", "multipart/form-data; charset=ISO-8859-1; boundary=g5gJAzUWl_t6"),
+ Collections.emptyMap());
+
+ // Valid header should validate ok
+ SessionCreateHandler.validateDataAndHeader(request, List.of(ContentType.MULTIPART_FORM_DATA.getMimeType()));
+
+ // Accepting only application/json should fail:
+ try {
+ SessionCreateHandler.validateDataAndHeader(request, List.of(ContentType.APPLICATION_JSON.getMimeType()));
+ fail("Request contained invalid content type, but validated ok");
+ } catch (Exception expected) {}
+ }
+
private SessionCreateHandler createHandler() {
return new SessionCreateHandler(SessionCreateHandler.testOnlyContext(),
applicationRepository,
@@ -178,7 +200,7 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
}
private HttpRequest post() throws FileNotFoundException {
- return post(null, postHeaders, new HashMap<>());
+ return post((InputStream) null, postHeaders, new HashMap<>());
}
private HttpRequest post(File file) throws FileNotFoundException {
@@ -186,10 +208,12 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
}
private HttpRequest post(File file, Map<String, String> headers, Map<String, String> parameters) throws FileNotFoundException {
+ return post(file == null ? null : new FileInputStream(file), headers, parameters);
+ }
+
+ private HttpRequest post(InputStream data, Map <String, String > headers, Map < String, String > parameters) throws FileNotFoundException {
HttpRequest request = HttpRequest.createTestRequest("http://" + hostname + ":" + port + "/application/v2/tenant/" + tenant + "/session",
- POST,
- file == null ? null : new FileInputStream(file),
- parameters);
+ POST, data, parameters);
for (Map.Entry<String, String> entry : headers.entrySet()) {
request.getJDiscRequest().headers().put(entry.getKey(), entry.getValue());
}
@@ -197,6 +221,6 @@ public class SessionCreateHandlerTest extends SessionHandlerTest {
}
private HttpRequest post(Map<String, String> parameters) throws FileNotFoundException {
- return post(null, new HashMap<>(), parameters);
+ return post((InputStream) null, new HashMap<>(), parameters);
}
}
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java
index 11898381f0a..08b89094486 100644
--- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java
@@ -8,6 +8,10 @@ import com.yahoo.container.logging.RequestLog;
import com.yahoo.container.logging.RequestLogEntry;
import com.yahoo.jdisc.http.ServerConfig;
import com.yahoo.jdisc.http.servlet.ServletRequest;
+import org.eclipse.jetty.http2.HTTP2Stream;
+import org.eclipse.jetty.http2.server.HttpTransportOverHTTP2;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpTransport;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
@@ -117,6 +121,7 @@ class AccessLogRequestLog extends AbstractLifeCycle implements org.eclipse.jetty
addNonNullValue(builder, accessLogEntry.getHitCounts(), RequestLogEntry.Builder::hitCounts);
addNonNullValue(builder, accessLogEntry.getTrace(), RequestLogEntry.Builder::traceNode);
}
+ http2StreamId(request).ifPresent(streamId -> builder.addExtraAttribute("http2-stream-id", Integer.toString(streamId)));
requestLog.log(builder.build());
} catch (Exception e) {
@@ -158,6 +163,15 @@ class AccessLogRequestLog extends AbstractLifeCycle implements org.eclipse.jetty
}
}
+ private static OptionalInt http2StreamId(Request request) {
+ HttpChannel httpChannel = request.getHttpChannel();
+ if (httpChannel == null) return OptionalInt.empty();
+ HttpTransport transport = httpChannel.getHttpTransport();
+ if (!(transport instanceof HttpTransportOverHTTP2)) return OptionalInt.empty();
+ HTTP2Stream stream = (HTTP2Stream) ((HttpTransportOverHTTP2) transport).getStream();
+ return OptionalInt.of(stream.getId());
+ }
+
private static <T> void addNonNullValue(
RequestLogEntry.Builder builder, T value, BiConsumer<RequestLogEntry.Builder, T> setter) {
if (value != null) {
diff --git a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def
index cb1e366f843..09b883a620e 100644
--- a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def
+++ b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def
@@ -127,4 +127,4 @@ maxRequestsPerConnection int default=0
maxConnectionLife double default=0.0
# Enable HTTP/2 (in addition to HTTP/1.1 using ALPN)
-http2Enabled bool default=false
+http2Enabled bool default=true
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
index 5056cf91d79..0dec711f4c0 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
@@ -35,8 +35,8 @@ import com.yahoo.security.SslContextBuilder;
import com.yahoo.security.X509CertificateBuilder;
import com.yahoo.security.X509CertificateUtils;
import com.yahoo.security.tls.TlsContext;
-import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.entity.mime.FormBodyPart;
import org.apache.hc.client5.http.entity.mime.FormBodyPartBuilder;
import org.apache.hc.client5.http.entity.mime.StringBody;
@@ -523,9 +523,9 @@ public class HttpServerTest {
MetricConsumerMock metricConsumer = new MetricConsumerMock();
InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
JettyTestDriver driver = createSslTestDriver(certificateFile, privateKeyFile, metricConsumer, connectionLog);
- try (CloseableHttpAsyncClient client = createHttp2Client(certificateFile, privateKeyFile)) {
+ try (CloseableHttpAsyncClient client = createHttp2Client(driver)) {
String uri = "https://localhost:" + driver.server().getListenPort() + "/status.html";
- SimpleHttpResponse response = client.execute(SimpleHttpRequests.get(uri), null).get();
+ SimpleHttpResponse response = client.execute(SimpleRequestBuilder.get(uri).build(), null).get();
assertNull(response.getBodyText());
assertEquals(OK, response.getCode());
}
@@ -967,8 +967,7 @@ public class HttpServerTest {
return client;
}
- private static CloseableHttpAsyncClient createHttp2Client(Path certificateFile, Path privateKeyFile) {
- JettyTestDriver driver = JettyTestDriver.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.WANT);
+ private static CloseableHttpAsyncClient createHttp2Client(JettyTestDriver driver) {
TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create()
.setSslContext(driver.sslContext())
.build();
diff --git a/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java
index f6bf91f5f85..8d0e4944ab8 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java
@@ -338,9 +338,8 @@ public class FederationSearcher extends ForkingSearcher {
private List<String> allSourceRefDescriptions() {
List<String> descriptions = new ArrayList<>();
- for (com.yahoo.search.federation.sourceref.Target target : searchChainResolver.allTopLevelTargets()) {
+ for (com.yahoo.search.federation.sourceref.Target target : searchChainResolver.allTopLevelTargets())
descriptions.add(target.searchRefDescription());
- }
return descriptions;
}
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java
index 6cb8d2ef174..59b4e521a56 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainInvocationSpec.java
@@ -9,7 +9,7 @@ import java.util.List;
import java.util.Objects;
/**
- * Specifices which search chain should be run and how it should be run.
+ * Specifies which search chain should be run and how it should be run.
* This is a value object.
*
* @author Tony Vaagenes
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java
index 36f6fe424a0..6626c1b3cc4 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java
@@ -92,7 +92,6 @@ public class SearchChainResolver {
public Builder addSourceForProvider(ComponentId sourceId, ComponentId providerId, ComponentId searchChainId,
boolean isDefaultProviderForSource, FederationOptions federationOptions,
List<String> documentTypes) {
-
SearchChainInvocationSpec searchChainInvocationSpec =
new SearchChainInvocationSpec(searchChainId, sourceId, providerId, federationOptions, documentTypes);
@@ -133,7 +132,6 @@ public class SearchChainResolver {
this.defaultTargets = Collections.unmodifiableSortedSet(defaultTargets);
}
-
public SearchChainInvocationSpec resolve(ComponentSpecification sourceRef, Properties sourceToProviderMap)
throws UnresolvedSearchChainException {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java
index 9c7e1024518..4613c73c4b4 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SingleTarget.java
@@ -5,8 +5,6 @@ import com.yahoo.component.ComponentId;
import com.yahoo.processing.request.Properties;
/**
- * TODO: What is this?
- *
* @author Tony Vaagenes
*/
public class SingleTarget extends Target {
diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java
index f23e24525bb..1b11e588f11 100644
--- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java
+++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/Target.java
@@ -6,8 +6,6 @@ import com.yahoo.component.ComponentId;
import com.yahoo.processing.request.Properties;
/**
- * TODO: What's this?
- *
* @author Tony Vaagenes
*/
public abstract class Target extends AbstractComponent {
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java b/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java
index c264425cb9c..2f680a8f3bd 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/model/VespaSearchers.java
@@ -60,8 +60,9 @@ public class VespaSearchers {
private static FederationSearcherModel federationSearcherModel() {
return new FederationSearcherModel(new ComponentSpecification("federation"),
- Dependencies.emptyDependencies(),
- Collections.emptyList(), true);
+ Dependencies.emptyDependencies(),
+ Collections.emptyList(),
+ true);
}
private static boolean allAdded(Collection<ChainedComponentModel> searcherModels, Set<ComponentId> componentIds) {
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java
index 2778f7e97db..01dccee5c7f 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/model/federation/FederationSearcherModel.java
@@ -5,7 +5,6 @@ import java.util.List;
import com.google.common.collect.ImmutableList;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
-import net.jcip.annotations.Immutable;
import com.yahoo.component.ComponentSpecification;
import com.yahoo.component.chain.dependencies.Dependencies;
@@ -17,23 +16,8 @@ import com.yahoo.search.federation.FederationSearcher;
*
* @author Tony Vaagenes
*/
-@Immutable
public class FederationSearcherModel extends ChainedComponentModel {
- /**
- * Specifies one or more search chains that can be addressed
- * as a single source.
- */
- public static class TargetSpec {
- public final ComponentSpecification sourceSpec;
- public final FederationOptions federationOptions;
-
- public TargetSpec(ComponentSpecification sourceSpec, FederationOptions federationOptions) {
- this.sourceSpec = sourceSpec;
- this.federationOptions = federationOptions;
- }
- }
-
private static final ComponentSpecification federationSearcherComponentSpecification =
new ComponentSpecification(FederationSearcher.class.getName());
@@ -48,4 +32,16 @@ public class FederationSearcherModel extends ChainedComponentModel {
this.targets = ImmutableList.copyOf(targets);
}
+ /** Specifies one or more search chains that can be addressed as a single source. */
+ public static class TargetSpec {
+
+ public final ComponentSpecification sourceSpec;
+ public final FederationOptions federationOptions;
+
+ public TargetSpec(ComponentSpecification sourceSpec, FederationOptions federationOptions) {
+ this.sourceSpec = sourceSpec;
+ this.federationOptions = federationOptions;
+ }
+ }
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java
index 5216c1421fa..63701ca9547 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java
@@ -18,18 +18,18 @@ public class EndpointCertificateMetadata {
private final String certName;
private final int version;
private final long lastRequested;
- private final String request_id;
+ private final String requestId;
private final List<String> requestedDnsSans;
private final String issuer;
private final Optional<Long> expiry;
private final Optional<Long> lastRefreshed;
- public EndpointCertificateMetadata(String keyName, String certName, int version, long lastRequested, String request_id, List<String> requestedDnsSans, String issuer, Optional<Long> expiry, Optional<Long> lastRefreshed) {
+ public EndpointCertificateMetadata(String keyName, String certName, int version, long lastRequested, String requestId, List<String> requestedDnsSans, String issuer, Optional<Long> expiry, Optional<Long> lastRefreshed) {
this.keyName = keyName;
this.certName = certName;
this.version = version;
this.lastRequested = lastRequested;
- this.request_id = request_id;
+ this.requestId = requestId;
this.requestedDnsSans = requestedDnsSans;
this.issuer = issuer;
this.expiry = expiry;
@@ -52,8 +52,8 @@ public class EndpointCertificateMetadata {
return lastRequested;
}
- public String request_id() {
- return request_id;
+ public String requestId() {
+ return requestId;
}
public List<String> requestedDnsSans() {
@@ -78,7 +78,7 @@ public class EndpointCertificateMetadata {
this.certName,
version,
this.lastRequested,
- this.request_id,
+ this.requestId,
this.requestedDnsSans,
this.issuer,
this.expiry,
@@ -91,7 +91,7 @@ public class EndpointCertificateMetadata {
this.certName,
this.version,
lastRequested,
- this.request_id,
+ this.requestId,
this.requestedDnsSans,
this.issuer,
this.expiry,
@@ -104,7 +104,7 @@ public class EndpointCertificateMetadata {
this.certName,
this.version,
this.lastRequested,
- this.request_id,
+ this.requestId,
this.requestedDnsSans,
this.issuer,
this.expiry,
@@ -127,16 +127,16 @@ public class EndpointCertificateMetadata {
@Override
public String toString() {
return "EndpointCertificateMetadata{" +
- "keyName='" + keyName + '\'' +
- ", certName='" + certName + '\'' +
- ", version=" + version +
- ", lastRequested=" + lastRequested +
- ", request_id=" + request_id +
- ", requestedDnsSans=" + requestedDnsSans +
- ", issuer=" + issuer +
- ", expiry=" + expiry +
- ", lastRefreshed=" + lastRefreshed +
- '}';
+ "keyName='" + keyName + '\'' +
+ ", certName='" + certName + '\'' +
+ ", version=" + version +
+ ", lastRequested=" + lastRequested +
+ ", requestId=" + requestId +
+ ", requestedDnsSans=" + requestedDnsSans +
+ ", issuer=" + issuer +
+ ", expiry=" + expiry +
+ ", lastRefreshed=" + lastRefreshed +
+ '}';
}
@Override
@@ -146,17 +146,18 @@ public class EndpointCertificateMetadata {
EndpointCertificateMetadata that = (EndpointCertificateMetadata) o;
return version == that.version &&
lastRequested == that.lastRequested &&
- keyName.equals(that.keyName) &&
- certName.equals(that.certName) &&
- request_id.equals(that.request_id) &&
- requestedDnsSans.equals(that.requestedDnsSans) &&
- issuer.equals(that.issuer) &&
- expiry.equals(that.expiry) &&
- lastRefreshed.equals(that.lastRefreshed);
+ keyName.equals(that.keyName) &&
+ certName.equals(that.certName) &&
+ requestId.equals(that.requestId) &&
+ requestedDnsSans.equals(that.requestedDnsSans) &&
+ issuer.equals(that.issuer) &&
+ expiry.equals(that.expiry) &&
+ lastRefreshed.equals(that.lastRefreshed);
}
@Override
public int hashCode() {
- return Objects.hash(keyName, certName, version, lastRequested, request_id, requestedDnsSans, issuer, expiry, lastRefreshed);
+ return Objects.hash(keyName, certName, version, lastRequested, requestId, requestedDnsSans, issuer, expiry, lastRefreshed);
}
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMock.java
index b5ee78251f0..07da6969b64 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMock.java
@@ -34,11 +34,12 @@ public class EndpointCertificateMock implements EndpointCertificateProvider {
@Override
public List<EndpointCertificateMetadata> listCertificates() {
- return Collections.emptyList();
+ return List.of();
}
@Override
public void deleteCertificate(ApplicationId applicationId, EndpointCertificateMetadata endpointCertificateMetadata) {
dnsNames.remove(applicationId);
}
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorImpl.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorImpl.java
index ea92f84f72f..9ce4fccc375 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorImpl.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorImpl.java
@@ -16,6 +16,9 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
+/**
+ * @author andreer
+ */
public class EndpointCertificateValidatorImpl implements EndpointCertificateValidator {
private final SecretStore secretStore;
private final Clock clock;
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorMock.java
index 7e89a3becd5..780701b3b77 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateValidatorMock.java
@@ -5,7 +5,11 @@ import com.yahoo.config.provision.zone.ZoneId;
import java.util.List;
+/**
+ * @author andreer
+ */
public class EndpointCertificateValidatorMock implements EndpointCertificateValidator {
+
@Override
public void validate(
EndpointCertificateMetadata endpointCertificateMetadata,
@@ -14,4 +18,5 @@ public class EndpointCertificateValidatorMock implements EndpointCertificateVali
List<String> requiredNamesForZone) {
// Mock does no validation - for unit tests only!
}
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
index 0f9e12d8cf2..5f46b949844 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
@@ -548,6 +548,11 @@ public class Node {
return this;
}
+ public Builder reports(Map<String, JsonNode> reports) {
+ this.reports = reports;
+ return this;
+ }
+
public Node build() {
return new Node(hostname, parentHostname, state, type, resources, owner, currentVersion, wantedVersion,
currentOsVersion, wantedOsVersion, currentFirmwareCheck, wantedFirmwareCheck, serviceState,
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/vcmr/VCMRReport.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/vcmr/VCMRReport.java
new file mode 100644
index 00000000000..a3c0af95053
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/vcmr/VCMRReport.java
@@ -0,0 +1,148 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.vcmr;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.yahoo.yolean.Exceptions.uncheck;
+
+/**
+ * @author olaa
+ *
+ * Node repository report containing list of upcoming VCMRs impacting a node
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class VCMRReport {
+
+ private static final String REPORT_ID = "vcmr";
+ private static final ObjectMapper objectMapper = new ObjectMapper()
+ .registerModule(new JavaTimeModule());
+
+ @JsonProperty("upcoming")
+ private Set<VCMR> vcmrs;
+
+ public VCMRReport() {
+ this(new HashSet<>());
+ }
+
+ public VCMRReport(Set<VCMR> vcmrs) {
+ this.vcmrs = vcmrs;
+ }
+
+ public Set<VCMR> getVcmrs() {
+ return vcmrs;
+ }
+
+ /**
+ * @return true if list of VCMRs is changed
+ */
+ public boolean addVcmr(String id, ZonedDateTime plannedStartTime, ZonedDateTime plannedEndtime) {
+ var vcmr = new VCMR(id, plannedStartTime, plannedEndtime);
+ if (vcmrs.contains(vcmr))
+ return false;
+
+ // Remove to catch any changes in start/end time
+ removeVcmr(id);
+ return vcmrs.add(vcmr);
+ }
+
+ public boolean removeVcmr(String id) {
+ return vcmrs.removeIf(vcmr -> id.equals(vcmr.getId()));
+ }
+
+ public static String getReportId() {
+ return REPORT_ID;
+ }
+
+ /**
+ * Serialization functions - mapped to {@link Node#reports()}
+ */
+ public static VCMRReport fromReports(Map<String, JsonNode> reports) {
+ var serialized = reports.get(REPORT_ID);
+ if (serialized == null)
+ return new VCMRReport();
+
+ return uncheck(() -> objectMapper.treeToValue(serialized, VCMRReport.class));
+ }
+
+ /**
+ * Set report to 'null' if list is empty - clearing the report
+ * See NodePatcher in node-repository
+ */
+ public Map<String, JsonNode> toNodeReports() {
+ Map<String, JsonNode> reports = new HashMap<>();
+ JsonNode jsonNode = vcmrs.isEmpty() ?
+ null : uncheck(() -> objectMapper.valueToTree(this));
+ reports.put(REPORT_ID, jsonNode);
+ return reports;
+ }
+
+ @Override
+ public String toString() {
+ return "VCMRReport{" + vcmrs + "}";
+ }
+
+ public static class VCMR {
+
+ private String id;
+ private ZonedDateTime plannedStartTime;
+ private ZonedDateTime plannedEndTime;
+
+ VCMR(@JsonProperty("id") String id,
+ @JsonProperty("plannedStartTime") ZonedDateTime plannedStartTime,
+ @JsonProperty("plannedEndTime") ZonedDateTime plannedEndTime) {
+ this.id = id;
+ this.plannedStartTime = plannedStartTime;
+ this.plannedEndTime = plannedEndTime;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public ZonedDateTime getPlannedStartTime() {
+ return plannedStartTime;
+ }
+
+ public ZonedDateTime getPlannedEndTime() {
+ return plannedEndTime;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ VCMR vcmr = (VCMR) o;
+ return Objects.equals(id, vcmr.id) &&
+ Objects.equals(plannedStartTime, vcmr.plannedStartTime) &&
+ Objects.equals(plannedEndTime, vcmr.plannedEndTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, plannedStartTime, plannedEndTime);
+ }
+
+ @Override
+ public String toString() {
+ return "VCMR{" +
+ "id='" + id + '\'' +
+ ", plannedStartTime=" + plannedStartTime +
+ ", plannedEndTime=" + plannedEndTime +
+ '}';
+ }
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index e6c96134ca7..0f9188d1f65 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -52,7 +52,7 @@ import com.yahoo.vespa.hosted.controller.application.QuotaUsage;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade;
-import com.yahoo.vespa.hosted.controller.certificate.EndpointCertificateManager;
+import com.yahoo.vespa.hosted.controller.certificate.EndpointCertificates;
import com.yahoo.vespa.hosted.controller.concurrent.Once;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger;
import com.yahoo.vespa.hosted.controller.deployment.JobStatus;
@@ -118,7 +118,7 @@ public class ApplicationController {
private final Clock clock;
private final DeploymentTrigger deploymentTrigger;
private final ApplicationPackageValidator applicationPackageValidator;
- private final EndpointCertificateManager endpointCertificateManager;
+ private final EndpointCertificates endpointCertificates;
private final StringFlag dockerImageRepoFlag;
private final BillingController billingController;
@@ -137,12 +137,9 @@ public class ApplicationController {
deploymentTrigger = new DeploymentTrigger(controller, clock);
applicationPackageValidator = new ApplicationPackageValidator(controller);
- endpointCertificateManager = new EndpointCertificateManager(
- controller.zoneRegistry(),
- curator,
- controller.serviceRegistry().endpointCertificateProvider(),
- controller.serviceRegistry().endpointCertificateValidator(),
- clock);
+ endpointCertificates = new EndpointCertificates(controller,
+ controller.serviceRegistry().endpointCertificateProvider(),
+ controller.serviceRegistry().endpointCertificateValidator());
// Update serialization format of all applications
Once.after(Duration.ofMinutes(1), () -> {
@@ -382,7 +379,7 @@ public class ApplicationController {
&& run.testerCertificate().isPresent())
applicationPackage = applicationPackage.withTrustedCertificate(run.testerCertificate().get());
- endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(instance, zone, applicationPackage.deploymentSpec().instance(instance.name()));
+ endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec().instance(instance.name()));
containerEndpoints = controller.routing().containerEndpointsOf(application.get(), job.application().instance(), zone);
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 433b2b340d5..abdb394c278 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
@@ -3,12 +3,16 @@ package com.yahoo.vespa.hosted.controller;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hashing;
+import com.google.common.io.BaseEncoding;
import com.yahoo.component.Version;
import com.yahoo.config.application.api.DeploymentInstanceSpec;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.flags.BooleanFlag;
@@ -34,6 +38,7 @@ import com.yahoo.vespa.hosted.controller.routing.RoutingId;
import com.yahoo.vespa.hosted.controller.routing.RoutingPolicies;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -67,6 +72,7 @@ public class RoutingController {
private final RoutingPolicies routingPolicies;
private final RotationRepository rotationRepository;
private final BooleanFlag hideSharedRoutingEndpoint;
+ private final BooleanFlag vespaAppDomainInCertificate;
public RoutingController(Controller controller, RotationsConfig rotationsConfig) {
this.controller = Objects.requireNonNull(controller, "controller must be non-null");
@@ -74,6 +80,7 @@ public class RoutingController {
this.rotationRepository = new RotationRepository(rotationsConfig, controller.applications(),
controller.curator());
this.hideSharedRoutingEndpoint = Flags.HIDE_SHARED_ROUTING_ENDPOINT.bindTo(controller.flagSource());
+ this.vespaAppDomainInCertificate = Flags.VESPA_APP_DOMAIN_IN_CERTIFICATE.bindTo(controller.flagSource());
}
public RoutingPolicies policies() {
@@ -149,6 +156,39 @@ public class RoutingController {
return Collections.unmodifiableMap(endpoints);
}
+ /** Returns certificate DNS names (CN and SAN values) for given deployment */
+ public List<String> certificateDnsNames(DeploymentId deployment) {
+ List<String> endpointDnsNames = new ArrayList<>();
+
+ // We add first an endpoint name based on a hash of the application ID,
+ // as the certificate provider requires the first CN to be < 64 characters long.
+ endpointDnsNames.add(commonNameHashOf(deployment.applicationId(), controller.system()));
+
+ // Add wildcard names for global endpoints when deploying to production
+ List<Endpoint.EndpointBuilder> builders = new ArrayList<>();
+ if (deployment.zoneId().environment().isProduction()) {
+ builders.add(Endpoint.of(deployment.applicationId()).target(EndpointId.defaultId()));
+ builders.add(Endpoint.of(deployment.applicationId()).wildcard());
+ }
+
+ // Add wildcard names for zone endpoints
+ builders.add(Endpoint.of(deployment.applicationId()).target(ClusterSpec.Id.from("default"), deployment.zoneId()));
+ builders.add(Endpoint.of(deployment.applicationId()).wildcard(deployment.zoneId()));
+
+ // Build all endpoints
+ for (var builder : builders) {
+ builder = builder.routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls());
+ Endpoint endpoint = builder.in(controller.system());
+ endpointDnsNames.add(endpoint.dnsName());
+ if (controller.system().isPublic() && vespaAppDomainInCertificate.with(FetchVector.Dimension.APPLICATION_ID, deployment.applicationId().serializedForm()).value()) {
+ Endpoint legacyEndpoint = builder.legacy().in(controller.system());
+ endpointDnsNames.add(legacyEndpoint.dnsName());
+ }
+ }
+ return Collections.unmodifiableList(endpointDnsNames);
+ }
+
/** Change status of all global endpoints for given deployment */
public void setGlobalRotationStatus(DeploymentId deployment, EndpointStatus status) {
endpointsOf(deployment.applicationId()).requiresRotation().primary().ifPresent(endpoint -> {
@@ -349,6 +389,13 @@ public class RoutingController {
.isPresent();
}
+ /** Create a common name based on a hash of given application. This must be less than 64 characters long. */
+ private static String commonNameHashOf(ApplicationId application, SystemName system) {
+ HashCode sha1 = Hashing.sha1().hashString(application.serializedForm(), StandardCharsets.UTF_8);
+ String base32 = BaseEncoding.base32().omitPadding().lowerCase().encode(sha1.asBytes());
+ return 'v' + base32 + Endpoint.dnsSuffix(system);
+ }
+
/** Returns direct routing endpoints if any exist and feature flag is set for given application */
// TODO: Remove this when feature flag is removed, and in-line .direct() filter where relevant
public EndpointList directEndpoints(EndpointList endpoints, ApplicationId application) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java
deleted file mode 100644
index 6f964999fba..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.certificate;
-
-import com.google.common.hash.Hashing;
-import com.google.common.io.BaseEncoding;
-import com.yahoo.config.application.api.DeploymentInstanceSpec;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.zone.RoutingMethod;
-import com.yahoo.config.provision.zone.ZoneApi;
-import com.yahoo.config.provision.zone.ZoneId;
-import com.yahoo.vespa.hosted.controller.Instance;
-import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
-import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateProvider;
-import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateValidator;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
-import com.yahoo.vespa.hosted.controller.application.Endpoint;
-import com.yahoo.vespa.hosted.controller.application.EndpointId;
-import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
-import org.jetbrains.annotations.NotNull;
-
-import java.nio.charset.Charset;
-import java.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-/**
- * Looks up stored endpoint certificate metadata, provisions new certificates if none is found,
- * re-provisions if zone is not covered, and uses refreshed certificates if a newer version is available.
- * <p>
- * See also EndpointCertificateMaintainer, which handles refreshes, deletions and triggers deployments
- *
- * @author andreer
- */
-public class EndpointCertificateManager {
-
- private static final Logger log = Logger.getLogger(EndpointCertificateManager.class.getName());
-
- private final ZoneRegistry zoneRegistry;
- private final CuratorDb curator;
- private final EndpointCertificateProvider endpointCertificateProvider;
- private final Clock clock;
- private final EndpointCertificateValidator endpointCertificateValidator;
-
- public EndpointCertificateManager(ZoneRegistry zoneRegistry,
- CuratorDb curator,
- EndpointCertificateProvider endpointCertificateProvider,
- EndpointCertificateValidator endpointCertificateValidator,
- Clock clock) {
- this.zoneRegistry = zoneRegistry;
- this.curator = curator;
- this.endpointCertificateProvider = endpointCertificateProvider;
- this.clock = clock;
- this.endpointCertificateValidator = endpointCertificateValidator;
- }
-
- public Optional<EndpointCertificateMetadata> getEndpointCertificateMetadata(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
- var t0 = Instant.now();
- Optional<EndpointCertificateMetadata> metadata = getOrProvision(instance, zone, instanceSpec);
- metadata.ifPresent(m -> curator.writeEndpointCertificateMetadata(instance.id(), m.withLastRequested(clock.instant().getEpochSecond())));
- Duration duration = Duration.between(t0, Instant.now());
- if (duration.toSeconds() > 30)
- log.log(Level.INFO, String.format("Getting endpoint certificate metadata for %s took %d seconds!", instance.id().serializedForm(), duration.toSeconds()));
- return metadata;
- }
-
- @NotNull
- private Optional<EndpointCertificateMetadata> getOrProvision(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
- final var currentCertificateMetadata = curator.readEndpointCertificateMetadata(instance.id());
-
- if (currentCertificateMetadata.isEmpty()) {
- var provisionedCertificateMetadata = provisionEndpointCertificate(instance, Optional.empty(), zone, instanceSpec);
- // We do not verify the certificate if one has never existed before - because we do not want to
- // wait for it to be available before we deploy. This allows the config server to start
- // provisioning nodes ASAP, and the risk is small for a new deployment.
- curator.writeEndpointCertificateMetadata(instance.id(), provisionedCertificateMetadata);
- return Optional.of(provisionedCertificateMetadata);
- }
-
- // Re-provision certificate if it is missing SANs for the zone we are deploying to
- var requiredSansForZone = dnsNamesOf(instance.id(), zone);
- if (!currentCertificateMetadata.get().requestedDnsSans().containsAll(requiredSansForZone)) {
- var reprovisionedCertificateMetadata =
- provisionEndpointCertificate(instance, currentCertificateMetadata, zone, instanceSpec)
- .withRequestId(currentCertificateMetadata.get().request_id()); // We're required to keep the original request_id
- curator.writeEndpointCertificateMetadata(instance.id(), reprovisionedCertificateMetadata);
- // Verification is unlikely to succeed in this case, as certificate must be available first - controller will retry
- endpointCertificateValidator.validate(reprovisionedCertificateMetadata, instance.id().serializedForm(), zone, requiredSansForZone);
- return Optional.of(reprovisionedCertificateMetadata);
- }
-
- endpointCertificateValidator.validate(currentCertificateMetadata.get(), instance.id().serializedForm(), zone, requiredSansForZone);
- return currentCertificateMetadata;
- }
-
- private EndpointCertificateMetadata provisionEndpointCertificate(Instance instance, Optional<EndpointCertificateMetadata> currentMetadata, ZoneId deploymentZone, Optional<DeploymentInstanceSpec> instanceSpec) {
-
- List<String> currentlyPresentNames = currentMetadata.isPresent() ?
- currentMetadata.get().requestedDnsSans() : Collections.emptyList();
-
- var requiredZones = new LinkedHashSet<>(Set.of(deploymentZone));
-
- var zoneCandidateList = zoneRegistry.zones().controllerUpgraded().zones().stream().map(ZoneApi::getId).collect(Collectors.toList());
-
- // If not deploying to a dev or perf zone, require all prod zones in deployment spec + test and staging
- if (!deploymentZone.environment().isManuallyDeployed()) {
- zoneCandidateList.stream()
- .filter(z -> z.environment().isTest() || instanceSpec.isPresent() && instanceSpec.get().deploysTo(z.environment(), z.region()))
- .forEach(requiredZones::add);
- }
-
- var requiredNames = requiredZones.stream()
- .flatMap(zone -> dnsNamesOf(instance.id(), zone).stream())
- .collect(Collectors.toCollection(LinkedHashSet::new));
-
- // Make sure all currently present names will remain present.
- // Instead of just adding "currently present names", we regenerate them in case the names for a zone have changed.
- zoneCandidateList.stream()
- .map(zone -> dnsNamesOf(instance.id(), zone))
- .filter(zoneNames -> zoneNames.stream().anyMatch(currentlyPresentNames::contains))
- .filter(currentlyPresentNames::containsAll)
- .forEach(requiredNames::addAll);
-
- // This check must be relaxed if we ever remove from the set of names generated.
- if (!requiredNames.containsAll(currentlyPresentNames))
- throw new RuntimeException("SANs to be requested do not cover all existing names! Missing names: "
- + currentlyPresentNames.stream().filter(s -> !requiredNames.contains(s)).collect(Collectors.joining(", ")));
-
- return endpointCertificateProvider.requestCaSignedCertificate(instance.id(), List.copyOf(requiredNames), currentMetadata);
- }
-
-
- private List<String> dnsNamesOf(ApplicationId applicationId, ZoneId zone) {
- List<String> endpointDnsNames = new ArrayList<>();
-
- // We add first an endpoint name based on a hash of the applicationId,
- // as the certificate provider requires the first CN to be < 64 characters long.
- endpointDnsNames.add(commonNameHashOf(applicationId, zoneRegistry.system()));
-
- List<Endpoint.EndpointBuilder> endpoints = new ArrayList<>();
-
- if (zone.environment().isProduction()) {
- endpoints.add(Endpoint.of(applicationId).target(EndpointId.defaultId()));
- endpoints.add(Endpoint.of(applicationId).wildcard());
- }
-
- endpoints.add(Endpoint.of(applicationId).target(ClusterSpec.Id.from("default"), zone));
- endpoints.add(Endpoint.of(applicationId).wildcard(zone));
-
- endpoints.stream()
- .map(endpoint -> endpoint.routingMethod(RoutingMethod.exclusive))
- .map(endpoint -> endpoint.on(Endpoint.Port.tls()))
- .map(endpointBuilder -> endpointBuilder.in(zoneRegistry.system()))
- .map(Endpoint::dnsName).forEach(endpointDnsNames::add);
-
- return Collections.unmodifiableList(endpointDnsNames);
- }
-
- /** Create a common name based on a hash of the ApplicationId. This should always be less than 64 characters long. */
- @SuppressWarnings("UnstableApiUsage")
- private static String commonNameHashOf(ApplicationId application, SystemName system) {
- var hashCode = Hashing.sha1().hashString(application.serializedForm(), Charset.defaultCharset());
- var base32encoded = BaseEncoding.base32().omitPadding().lowerCase().encode(hashCode.asBytes());
- return 'v' + base32encoded + Endpoint.dnsSuffix(system);
- }
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
new file mode 100644
index 00000000000..0391cb05bb7
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
@@ -0,0 +1,123 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.certificate;
+
+import com.yahoo.config.application.api.DeploymentInstanceSpec;
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.Instance;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
+import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateProvider;
+import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateValidator;
+import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ * Looks up stored endpoint certificate metadata, provisions new certificates if none is found,
+ * re-provisions if zone is not covered, and uses refreshed certificates if a newer version is available.
+ *
+ * See also {@link com.yahoo.vespa.hosted.controller.maintenance.EndpointCertificateMaintainer}, which handles
+ * refreshes, deletions and triggers deployments.
+ *
+ * @author andreer
+ */
+public class EndpointCertificates {
+
+ private static final Logger log = Logger.getLogger(EndpointCertificates.class.getName());
+
+ private final Controller controller;
+ private final CuratorDb curator;
+ private final Clock clock;
+ private final EndpointCertificateProvider certificateProvider;
+ private final EndpointCertificateValidator certificateValidator;
+
+ public EndpointCertificates(Controller controller, EndpointCertificateProvider certificateProvider,
+ EndpointCertificateValidator certificateValidator) {
+ this.controller = controller;
+ this.curator = controller.curator();
+ this.clock = controller.clock();
+ this.certificateProvider = certificateProvider;
+ this.certificateValidator = certificateValidator;
+ }
+
+ /** Returns certificate metadata for endpoints of given instance and zone */
+ public Optional<EndpointCertificateMetadata> getMetadata(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
+ Instant start = clock.instant();
+ Optional<EndpointCertificateMetadata> metadata = getOrProvision(instance, zone, instanceSpec);
+ metadata.ifPresent(m -> curator.writeEndpointCertificateMetadata(instance.id(), m.withLastRequested(clock.instant().getEpochSecond())));
+ Duration duration = Duration.between(start, clock.instant());
+ if (duration.toSeconds() > 30)
+ log.log(Level.INFO, String.format("Getting endpoint certificate metadata for %s took %d seconds!", instance.id().serializedForm(), duration.toSeconds()));
+ return metadata;
+ }
+
+ private Optional<EndpointCertificateMetadata> getOrProvision(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
+ final var currentCertificateMetadata = curator.readEndpointCertificateMetadata(instance.id());
+
+ DeploymentId deployment = new DeploymentId(instance.id(), zone);
+
+ if (currentCertificateMetadata.isEmpty()) {
+ var provisionedCertificateMetadata = provisionEndpointCertificate(deployment, Optional.empty(), instanceSpec);
+ // We do not verify the certificate if one has never existed before - because we do not want to
+ // wait for it to be available before we deploy. This allows the config server to start
+ // provisioning nodes ASAP, and the risk is small for a new deployment.
+ curator.writeEndpointCertificateMetadata(instance.id(), provisionedCertificateMetadata);
+ return Optional.of(provisionedCertificateMetadata);
+ }
+
+ // Re-provision certificate if it is missing SANs for the zone we are deploying to
+ var requiredSansForZone = controller.routing().certificateDnsNames(deployment);
+ if (!currentCertificateMetadata.get().requestedDnsSans().containsAll(requiredSansForZone)) {
+ var reprovisionedCertificateMetadata =
+ provisionEndpointCertificate(deployment, currentCertificateMetadata, instanceSpec)
+ .withRequestId(currentCertificateMetadata.get().requestId()); // We're required to keep the original request ID
+ curator.writeEndpointCertificateMetadata(instance.id(), reprovisionedCertificateMetadata);
+ // Verification is unlikely to succeed in this case, as certificate must be available first - controller will retry
+ certificateValidator.validate(reprovisionedCertificateMetadata, instance.id().serializedForm(), zone, requiredSansForZone);
+ return Optional.of(reprovisionedCertificateMetadata);
+ }
+
+ certificateValidator.validate(currentCertificateMetadata.get(), instance.id().serializedForm(), zone, requiredSansForZone);
+ return currentCertificateMetadata;
+ }
+
+ private EndpointCertificateMetadata provisionEndpointCertificate(DeploymentId deployment,
+ Optional<EndpointCertificateMetadata> currentMetadata,
+ Optional<DeploymentInstanceSpec> instanceSpec) {
+ List<ZoneId> zonesInSystem = controller.zoneRegistry().zones().controllerUpgraded().ids();
+ Set<ZoneId> requiredZones = new LinkedHashSet<>();
+ requiredZones.add(deployment.zoneId());
+ if (!deployment.zoneId().environment().isManuallyDeployed()) {
+ // If not deploying to a dev or perf zone, require all prod zones in deployment spec + test and staging
+ zonesInSystem.stream()
+ .filter(zone -> zone.environment().isTest() ||
+ (instanceSpec.isPresent() &&
+ instanceSpec.get().deploysTo(zone.environment(), zone.region())))
+ .forEach(requiredZones::add);
+ }
+ Set<String> requiredNames = requiredZones.stream()
+ .flatMap(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone)).stream())
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+
+ // Preserve any currently present names that are still valid
+ List<String> currentNames = currentMetadata.map(EndpointCertificateMetadata::requestedDnsSans)
+ .orElseGet(List::of);
+ zonesInSystem.stream()
+ .map(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone)))
+ .filter(currentNames::containsAll)
+ .forEach(requiredNames::addAll);
+
+ return certificateProvider.requestCaSignedCertificate(deployment.applicationId(), List.copyOf(requiredNames), currentMetadata);
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ConvergenceSummary.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ConvergenceSummary.java
index d874a8042f2..1c204159d2c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ConvergenceSummary.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/ConvergenceSummary.java
@@ -22,9 +22,10 @@ public class ConvergenceSummary {
private final long restarting;
private final long services;
private final long needNewConfig;
+ private final long retiring;
public ConvergenceSummary(long nodes, long down, long upgradingOs, long upgradingFirmware, long needPlatformUpgrade, long upgradingPlatform,
- long needReboot, long rebooting, long needRestart, long restarting, long services, long needNewConfig) {
+ long needReboot, long rebooting, long needRestart, long restarting, long services, long needNewConfig, long retiring) {
this.nodes = nodes;
this.down = down;
this.upgradingOs = upgradingOs;
@@ -37,6 +38,7 @@ public class ConvergenceSummary {
this.restarting = restarting;
this.services = services;
this.needNewConfig = needNewConfig;
+ this.retiring = retiring;
}
/** Number of nodes in the application. */
@@ -99,6 +101,11 @@ public class ConvergenceSummary {
return needNewConfig;
}
+ /** Number of nodes that are retiring. */
+ public long retiring() {
+ return retiring;
+ }
+
/** Whether the convergence is done. */
public boolean converged() {
return nodes > 0
@@ -125,12 +132,13 @@ public class ConvergenceSummary {
needRestart == that.needRestart &&
restarting == that.restarting &&
services == that.services &&
- needNewConfig == that.needNewConfig;
+ needNewConfig == that.needNewConfig &&
+ retiring == that.retiring;
}
@Override
public int hashCode() {
- return Objects.hash(nodes, down, upgradingOs, upgradingFirmware, needPlatformUpgrade, upgradingPlatform, needReboot, rebooting, needRestart, restarting, services, needNewConfig);
+ return Objects.hash(nodes, down, upgradingOs, upgradingFirmware, needPlatformUpgrade, upgradingPlatform, needReboot, rebooting, needRestart, restarting, services, needNewConfig, retiring);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
index 5bd43dfd695..e9755b7fd8b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
@@ -426,21 +426,20 @@ public class InternalStepRunner implements StepRunner {
return Optional.empty();
}
- /** Returns true iff all containers in the deployment give 100 consecutive 200 OK responses on /status.html. */
+ /** Returns true iff all calls to endpoint in the deployment give 100 consecutive 200 OK responses on /status.html. */
private boolean containersAreUp(ApplicationId id, ZoneId zoneId, DualLogger logger) {
var endpoints = controller.routing().zoneEndpointsOf(Set.of(new DeploymentId(id, zoneId)));
if ( ! endpoints.containsKey(zoneId))
return false;
- for (var endpoint : endpoints.get(zoneId)) {
+ return endpoints.get(zoneId).parallelStream().map(endpoint -> {
boolean ready = controller.jobController().cloud().ready(endpoint.url());
if ( ! ready) {
logger.log("Failed to get 100 consecutive OKs from " + endpoint);
- return false;
+ return Boolean.FALSE;
}
- }
-
- return true;
+ return Boolean.TRUE;
+ }).allMatch(Boolean.TRUE::equals);
}
/** Returns true iff all containers in the tester deployment give 100 consecutive 200 OK responses on /status.html. */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/NodeList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/NodeList.java
index 080d600005f..3c3cbfe2b2a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/NodeList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/NodeList.java
@@ -87,6 +87,12 @@ public class NodeList extends AbstractFilteringList<NodeWithServices, NodeList>
return matching(NodeWithServices::needsNewConfig);
}
+ /** The nodes that are retiring. */
+ public NodeList retiring() {
+ return matching(node -> node.node().retired());
+ }
+
+
/** Returns a summary of the convergence status of the nodes in this list. */
public ConvergenceSummary summary() {
NodeList allowedDown = expectedDown();
@@ -101,7 +107,8 @@ public class NodeList extends AbstractFilteringList<NodeWithServices, NodeList>
needsRestart().size(),
allowedDown.needsRestart().size(),
asList().stream().mapToLong(node -> node.services().size()).sum(),
- asList().stream().mapToLong(node -> node.services().stream().filter(service -> wantedConfigGeneration > service.currentGeneration()).count()).sum());
+ asList().stream().mapToLong(node -> node.services().stream().filter(service -> wantedConfigGeneration > service.currentGeneration()).count()).sum(),
+ retiring().size());
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
index 6bda6dc8a25..a8214ac8a09 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java
@@ -61,7 +61,6 @@ public class DeploymentMetricsMaintainer extends ControllerMaintainer {
DeploymentId deploymentId = new DeploymentId(instance.id(), deployment.zone());
List<ClusterMetrics> clusterMetrics = controller().serviceRegistry().configServer().getDeploymentMetrics(deploymentId);
Instant now = controller().clock().instant();
-
applications.lockApplicationIfPresent(application.id(), locked -> {
Deployment existingDeployment = locked.get().require(instance.name()).deployments().get(deployment.zone());
if (existingDeployment == null) return; // Deployment removed since we started collecting metrics
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainer.java
index 111a332bc81..fedf3d90760 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainer.java
@@ -15,6 +15,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequest.Impa
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestClient;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction.State;
+import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VCMRReport;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest.Status;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
@@ -144,12 +145,15 @@ public class VCMRMaintainer extends ControllerMaintainer {
if (changeRequest.getChangeRequestSource().isClosed()) {
logger.fine(() -> changeRequest.getChangeRequestSource().getId() + " is closed, recycling " + node.hostname());
recycleNode(changeRequest.getZoneId(), node, hostAction);
+ removeReport(changeRequest, node);
return hostAction.withState(State.COMPLETE);
}
if (isLowImpact(changeRequest))
return hostAction;
+ addReport(changeRequest, node);
+
if (isPostponed(changeRequest, hostAction)) {
logger.fine(() -> changeRequest.getChangeRequestSource().getId() + " is postponed, recycling " + node.hostname());
recycleNode(changeRequest.getZoneId(), node, hostAction);
@@ -285,4 +289,28 @@ public class VCMRMaintainer extends ControllerMaintainer {
logger.info("Approving " + changeRequest.getChangeRequestSource().getId());
changeRequestClient.approveChangeRequest(changeRequest);
}
+
+ private void removeReport(VespaChangeRequest changeRequest, Node node) {
+ var report = VCMRReport.fromReports(node.reports());
+
+ if (report.removeVcmr(changeRequest.getChangeRequestSource().getId())) {
+ updateReport(changeRequest.getZoneId(), node, report);
+ }
+ }
+
+ private void addReport(VespaChangeRequest changeRequest, Node node) {
+ var report = VCMRReport.fromReports(node.reports());
+
+ var source = changeRequest.getChangeRequestSource();
+ if (report.addVcmr(source.getId(), source.getPlannedStartTime(), source.getPlannedEndTime())) {
+ updateReport(changeRequest.getZoneId(), node, report);
+ }
+ }
+
+ private void updateReport(ZoneId zoneId, Node node, VCMRReport report) {
+ logger.info(String.format("Updating report for %s: %s", node.hostname(), report));
+ var newNode = new NodeRepositoryNode();
+ newNode.setReports(report.toNodeReports());
+ nodeRepository.patchNode(zoneId, node.hostname().value(), newNode);
+ }
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java
index 7e4fc285793..3ced0d60453 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java
@@ -46,7 +46,7 @@ public class EndpointCertificateMetadataSerializer {
object.setString(certNameField, metadata.certName());
object.setLong(versionField, metadata.version());
object.setLong(lastRequestedField, metadata.lastRequested());
- object.setString(requestIdField, metadata.request_id());
+ object.setString(requestIdField, metadata.requestId());
var cursor = object.setArray(requestedDnsSansField);
metadata.requestedDnsSans().forEach(cursor::addString);
object.setString(issuerField, metadata.issuer());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java
index 0ecd86a4a38..91d85e62fcb 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java
@@ -95,7 +95,8 @@ class RunSerializer {
private static final String lastTestRecordField = "lastTestRecord";
private static final String lastVespaLogTimestampField = "lastVespaLogTimestamp";
private static final String noNodesDownSinceField = "noNodesDownSince";
- private static final String convergenceSummaryField = "convergenceSummary";
+ private static final String oldConvergenceSummaryField = "convergenceSummary"; // TODO (freva): Remove after 7.410
+ private static final String convergenceSummaryField = "convergenceSummaryV2";
private static final String testerCertificateField = "testerCertificate";
Run runFromSlime(Slime slime) {
@@ -136,7 +137,8 @@ class RunSerializer {
runObject.field(lastTestRecordField).asLong(),
Instant.EPOCH.plus(runObject.field(lastVespaLogTimestampField).asLong(), ChronoUnit.MICROS),
Serializers.optionalInstant(runObject.field(noNodesDownSinceField)),
- convergenceSummaryFrom(runObject.field(convergenceSummaryField)),
+ convergenceSummaryFrom(runObject.field(convergenceSummaryField))
+ .or(() ->convergenceSummaryFrom(runObject.field(oldConvergenceSummaryField))),
Optional.of(runObject.field(testerCertificateField))
.filter(Inspector::valid)
.map(certificate -> X509CertificateUtils.fromPem(certificate.asString())));
@@ -178,11 +180,10 @@ class RunSerializer {
// Don't change this — introduce a separate array instead.
private Optional<ConvergenceSummary> convergenceSummaryFrom(Inspector summaryArray) {
- if ( ! summaryArray.valid() || summaryArray.entries() == 11) // TODO jonmv: fix
- return Optional.empty();
+ if ( ! summaryArray.valid()) return Optional.empty();
- if (summaryArray.entries() != 12)
- throw new IllegalArgumentException("Convergence summary must have 12 entries");
+ if (summaryArray.entries() != 12 && summaryArray.entries() != 13)
+ throw new IllegalArgumentException("Convergence summary must have 13 entries");
return Optional.of(new ConvergenceSummary(summaryArray.entry(0).asLong(),
summaryArray.entry(1).asLong(),
@@ -195,7 +196,8 @@ class RunSerializer {
summaryArray.entry(8).asLong(),
summaryArray.entry(9).asLong(),
summaryArray.entry(10).asLong(),
- summaryArray.entry(11).asLong()));
+ summaryArray.entry(11).asLong(),
+ summaryArray.entry(12).asLong()));
}
Slime toSlime(Iterable<Run> runs) {
@@ -221,7 +223,10 @@ class RunSerializer {
runObject.setLong(lastTestRecordField, run.lastTestLogEntry());
runObject.setLong(lastVespaLogTimestampField, Instant.EPOCH.until(run.lastVespaLogTimestamp(), ChronoUnit.MICROS));
run.noNodesDownSince().ifPresent(noNodesDownSince -> runObject.setLong(noNodesDownSinceField, noNodesDownSince.toEpochMilli()));
- run.convergenceSummary().ifPresent(convergenceSummary -> toSlime(convergenceSummary, runObject.setArray(convergenceSummaryField)));
+ run.convergenceSummary().ifPresent(convergenceSummary -> {
+ toSlime(convergenceSummary, runObject.setArray(convergenceSummaryField), false);
+ toSlime(convergenceSummary, runObject.setArray(oldConvergenceSummaryField), true);
+ });
run.testerCertificate().ifPresent(certificate -> runObject.setString(testerCertificateField, X509CertificateUtils.toPem(certificate)));
Cursor stepsObject = runObject.setObject(stepsField);
@@ -258,7 +263,7 @@ class RunSerializer {
}
// Don't change this - introduce a separate array with new values if needed.
- private void toSlime(ConvergenceSummary summary, Cursor summaryArray) {
+ private void toSlime(ConvergenceSummary summary, Cursor summaryArray, boolean oldFormat) {
summaryArray.addLong(summary.nodes());
summaryArray.addLong(summary.down());
summaryArray.addLong(summary.upgradingOs());
@@ -271,6 +276,8 @@ class RunSerializer {
summaryArray.addLong(summary.restarting());
summaryArray.addLong(summary.services());
summaryArray.addLong(summary.needNewConfig());
+ if (!oldFormat)
+ summaryArray.addLong(summary.retiring());
}
static String valueOf(Step step) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
index 622a7033519..dba618d139c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
@@ -213,6 +213,7 @@ class JobControllerApiHandlerHelper {
summaryObject.setLong("upgradingFirmware", summary.upgradingFirmware());
summaryObject.setLong("services", summary.services());
summaryObject.setLong("needNewConfig", summary.needNewConfig());
+ summaryObject.setLong("retiring", summary.retiring());
}
private static void toSlime(Cursor entryArray, List<LogEntry> entries) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
index daa84f4700c..f0bcd2b4d03 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
@@ -250,7 +250,11 @@ public class BillingApiHandler extends LoggingRequestHandler {
var invoiceId = billingController.createInvoiceForPeriod(tenantName, startTime, endTime, userId);
- return new MessageResponse("Created invoice with ID " + invoiceId.value());
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ root.setString("message", "Created invoice with ID " + invoiceId.value());
+ root.setString("id", invoiceId.value());
+ return new SlimeJsonResponse(slime);
}
private HttpResponse getInstruments(String tenant, String userId) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java
index ff24253128b..e2922c040d2 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java
@@ -18,7 +18,12 @@ import com.yahoo.yolean.Exceptions;
import java.io.IOException;
import java.io.OutputStream;
+import java.time.Instant;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -35,6 +40,7 @@ public class BadgeApiHandler extends LoggingRequestHandler {
private final static Logger log = Logger.getLogger(BadgeApiHandler.class.getName());
private final Controller controller;
+ private final Map<Key, Value> badgeCache = new ConcurrentHashMap<>();
public BadgeApiHandler(Context parentCtx, Controller controller) {
super(parentCtx);
@@ -59,29 +65,44 @@ public class BadgeApiHandler extends LoggingRequestHandler {
private HttpResponse get(HttpRequest request) {
Path path = new Path(request.getUri());
- if (path.matches("/badge/v1/{tenant}/{application}/{instance}")) return badge(path.get("tenant"), path.get("application"), path.get("instance"));
- if (path.matches("/badge/v1/{tenant}/{application}/{instance}/{jobName}")) return badge(path.get("tenant"), path.get("application"), path.get("instance"), path.get("jobName"), request.getProperty("historyLength"));
+ if (path.matches("/badge/v1/{tenant}/{application}/{instance}")) return overviewBadge(path.get("tenant"), path.get("application"), path.get("instance"));
+ if (path.matches("/badge/v1/{tenant}/{application}/{instance}/{jobName}")) return historyBadge(path.get("tenant"), path.get("application"), path.get("instance"), path.get("jobName"), request.getProperty("historyLength"));
return ErrorResponse.notFoundError(String.format("No '%s' handler at '%s'", request.getMethod(),
request.getUri().getPath()));
}
/** Returns a URI which points to an overview badge for the given application. */
- private HttpResponse badge(String tenant, String application, String instance) {
+ private HttpResponse overviewBadge(String tenant, String application, String instance) {
ApplicationId id = ApplicationId.from(tenant, application, instance);
- DeploymentStatus status = controller.jobController().deploymentStatus(controller.applications().requireApplication(TenantAndApplicationId.from(id)));
- Predicate<JobStatus> isDeclaredJob = job -> status.jobSteps().get(job.id()) != null && status.jobSteps().get(job.id()).isDeclared();
- return svgResponse(Badges.overviewBadge(id,
- status.jobs().instance(id.instance()).matching(isDeclaredJob),
- controller.system()));
+ return cachedResponse(new Key(id, null, 0),
+ controller.clock().instant(),
+ () -> {
+ DeploymentStatus status = controller.jobController().deploymentStatus(controller.applications().requireApplication(TenantAndApplicationId.from(id)));
+ Predicate<JobStatus> isDeclaredJob = job -> status.jobSteps().get(job.id()) != null && status.jobSteps().get(job.id()).isDeclared();
+ return Badges.overviewBadge(id,
+ status.jobs().instance(id.instance()).matching(isDeclaredJob),
+ controller.system());
+ });
}
/** Returns a URI which points to a history badge for the given application and job type. */
- private HttpResponse badge(String tenant, String application, String instance, String jobName, String historyLength) {
+ private HttpResponse historyBadge(String tenant, String application, String instance, String jobName, String historyLength) {
ApplicationId id = ApplicationId.from(tenant, application, instance);
- return svgResponse(Badges.historyBadge(id,
- controller.jobController().jobStatus(new JobId(id, JobType.fromJobName(jobName))),
- historyLength == null ? 5 : Math.min(32, Math.max(0, Integer.parseInt(historyLength)))));
+ JobType type = JobType.fromJobName(jobName);
+ int length = historyLength == null ? 5 : Math.min(32, Math.max(0, Integer.parseInt(historyLength)));
+ return cachedResponse(new Key(id, type, length),
+ controller.clock().instant(),
+ () -> Badges.historyBadge(id,
+ controller.jobController().jobStatus(new JobId(id, type)),
+ length)
+ );
+ }
+
+ private HttpResponse cachedResponse(Key key, Instant now, Supplier<String> badge) {
+ return svgResponse(badgeCache.compute(key, (__, value) -> {
+ return value != null && value.expiry.isAfter(now) ? value : new Value(badge.get(), now);
+ }).badgeSvg);
}
private static HttpResponse svgResponse(String svg) {
@@ -95,4 +116,44 @@ public class BadgeApiHandler extends LoggingRequestHandler {
};
}
+
+ private static class Key {
+
+ private final ApplicationId id;
+ private final JobType type;
+ private final int historyLength;
+
+ private Key(ApplicationId id, JobType type, int historyLength) {
+ this.id = id;
+ this.type = type;
+ this.historyLength = historyLength;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Key key = (Key) o;
+ return historyLength == key.historyLength && id.equals(key.id) && type == key.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, type, historyLength);
+ }
+
+ }
+
+ private static class Value {
+
+ private final String badgeSvg;
+ private final Instant expiry;
+
+ private Value(String badgeSvg, Instant created) {
+ this.badgeSvg = badgeSvg;
+ this.expiry = created.plusSeconds(60);
+ }
+
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java
index 2b3f954c2ef..4951f2ba33b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java
@@ -111,13 +111,12 @@ public class Badges {
Run lastTriggered = status.lastTriggered().get();
List<Run> runs = status.runs().descendingMap().values().stream()
- .filter(Run::hasEnded)
- .limit(length + (lastTriggered.hasEnded() ? 0 : 1))
- .collect(toList());
+ .filter(Run::hasEnded)
+ .skip(1)
+ .limit(length + (lastTriggered.hasEnded() ? 0 : 1))
+ .collect(toList());
- boolean isOk = runs.isEmpty() || runs.get(0).status() == RunStatus.success;
- if ( ! lastTriggered.hasEnded())
- runs.remove(0);
+ boolean isOk = status.lastCompleted().map(run -> run.status() == RunStatus.success).orElse(true);
text = lastTriggered.id().type().jobName();
textWidth = widthOf(text);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
index e30f044c579..d8544ff3947 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
@@ -12,22 +12,22 @@ import com.yahoo.security.KeyUtils;
import com.yahoo.security.SignatureAlgorithm;
import com.yahoo.security.X509CertificateBuilder;
import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.test.ManualClock;
import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.InMemoryFlagSource;
+import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.Instance;
-import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
+import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateValidatorImpl;
import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock;
-import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock;
-import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
+import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import org.junit.Before;
import org.junit.Test;
import javax.security.auth.x500.X500Principal;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
-import java.time.Clock;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
@@ -41,17 +41,19 @@ import static org.junit.Assert.assertTrue;
/**
* @author andreer
*/
-public class EndpointCertificateManagerTest {
+public class EndpointCertificatesTest {
+ private final ControllerTester tester = new ControllerTester();
private final SecretStoreMock secretStore = new SecretStoreMock();
- private final ZoneRegistryMock zoneRegistryMock = new ZoneRegistryMock(SystemName.main);
- private final MockCuratorDb mockCuratorDb = new MockCuratorDb();
+ private final CuratorDb mockCuratorDb = tester.curator();
+ private final ManualClock clock = tester.clock();
private final EndpointCertificateMock endpointCertificateMock = new EndpointCertificateMock();
- private final InMemoryFlagSource inMemoryFlagSource = new InMemoryFlagSource();
- private static final Clock clock = Clock.fixed(Instant.EPOCH, java.time.ZoneId.systemDefault());
private final EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock);
- private final EndpointCertificateManager endpointCertificateManager =
- new EndpointCertificateManager(zoneRegistryMock, mockCuratorDb, endpointCertificateMock, endpointCertificateValidator, clock);
+ private final EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator);
+ private final KeyPair testKeyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 192);
+
+ private X509Certificate testCertificate;
+ private X509Certificate testCertificate2;
private static final List<String> expectedSans = List.of(
"vt2ktgkqme5zlnp4tj4ttyor7fj3v7q5o.vespa.oath.cloud",
@@ -81,11 +83,7 @@ public class EndpointCertificateManagerTest {
"*.default.default.us-east-1.dev.vespa.oath.cloud"
);
- private static final KeyPair testKeyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 192);
- private static final X509Certificate testCertificate = makeTestCert(expectedSans);
- private static final X509Certificate testCertificate2 = makeTestCert(expectedCombinedSans);
-
- private static X509Certificate makeTestCert(List<String> sans) {
+ private X509Certificate makeTestCert(List<String> sans) {
X509CertificateBuilder x509CertificateBuilder = X509CertificateBuilder
.fromKeypair(
testKeyPair,
@@ -104,14 +102,17 @@ public class EndpointCertificateManagerTest {
@Before
public void setUp() {
- zoneRegistryMock.exclusiveRoutingIn(zoneRegistryMock.zones().all().zones());
- testZone = zoneRegistryMock.zones().directlyRouted().in(Environment.prod).zones().stream().findFirst().orElseThrow().getId();
+ tester.zoneRegistry().exclusiveRoutingIn(tester.zoneRegistry().zones().all().zones());
+ testZone = tester.zoneRegistry().zones().directlyRouted().in(Environment.prod).zones().stream().findFirst().orElseThrow().getId();
+ clock.setInstant(Instant.EPOCH);
+ testCertificate = makeTestCert(expectedSans);
+ testCertificate2 = makeTestCert(expectedCombinedSans);
}
@Test
public void provisions_new_certificate_in_dev() {
- ZoneId testZone = zoneRegistryMock.zones().directlyRouted().in(Environment.dev).zones().stream().findFirst().orElseThrow().getId();
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone, Optional.empty());
+ ZoneId testZone = tester.zoneRegistry().zones().directlyRouted().in(Environment.dev).zones().stream().findFirst().orElseThrow().getId();
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -121,7 +122,40 @@ public class EndpointCertificateManagerTest {
@Test
public void provisions_new_certificate_in_prod() {
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ assertTrue(endpointCertificateMetadata.isPresent());
+ assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
+ assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
+ assertEquals(0, endpointCertificateMetadata.get().version());
+ assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans());
+ }
+
+ @Test
+ public void provisions_new_certificate_in_public_prod() {
+ ControllerTester tester = new ControllerTester(SystemName.Public);
+ ((InMemoryFlagSource) tester.controller().flagSource()).withBooleanFlag(Flags.VESPA_APP_DOMAIN_IN_CERTIFICATE.id(), true);
+ EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock);
+ EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator);
+ List<String> expectedSans = List.of(
+ "vt2ktgkqme5zlnp4tj4ttyor7fj3v7q5o.public.vespa.oath.cloud",
+ "default.default.global.public.vespa.oath.cloud",
+ "default.default.g.vespa-app.cloud",
+ "*.default.default.global.public.vespa.oath.cloud",
+ "*.default.default.g.vespa-app.cloud",
+ "default.default.aws-us-east-1a.public.vespa.oath.cloud",
+ "default.default.aws-us-east-1a.z.vespa-app.cloud",
+ "*.default.default.aws-us-east-1a.public.vespa.oath.cloud",
+ "*.default.default.aws-us-east-1a.z.vespa-app.cloud",
+ "default.default.aws-us-east-1c.test.public.vespa.oath.cloud",
+ "default.default.aws-us-east-1c.test.z.vespa-app.cloud",
+ "*.default.default.aws-us-east-1c.test.public.vespa.oath.cloud",
+ "*.default.default.aws-us-east-1c.test.z.vespa-app.cloud",
+ "default.default.aws-us-east-1c.staging.public.vespa.oath.cloud",
+ "default.default.aws-us-east-1c.staging.z.vespa-app.cloud",
+ "*.default.default.aws-us-east-1c.staging.public.vespa.oath.cloud",
+ "*.default.default.aws-us-east-1c.staging.z.vespa-app.cloud"
+ );
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -140,7 +174,7 @@ public class EndpointCertificateManagerTest {
"", Optional.empty(), Optional.empty()));
secretStore.setSecret(testKeyName, KeyUtils.toPem(testKeyPair.getPrivate()), 7);
secretStore.setSecret(testCertName, X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 7);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(testKeyName, endpointCertificateMetadata.get().keyName());
assertEquals(testCertName, endpointCertificateMetadata.get().certName());
@@ -152,7 +186,7 @@ public class EndpointCertificateManagerTest {
mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, 0, "uuid", List.of(), "issuer", Optional.empty(), Optional.empty()));
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 0);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(0, endpointCertificateMetadata.get().version());
assertEquals(endpointCertificateMetadata, mockCuratorDb.readEndpointCertificateMetadata(testInstance.id()));
@@ -160,7 +194,7 @@ public class EndpointCertificateManagerTest {
@Test
public void reprovisions_certificate_with_added_sans_when_deploying_to_new_zone() {
- ZoneId testZone = zoneRegistryMock.zones().directlyRouted().in(Environment.prod).zones().stream().skip(1).findFirst().orElseThrow().getId();
+ ZoneId testZone = tester.zoneRegistry().zones().directlyRouted().in(Environment.prod).zones().stream().skip(1).findFirst().orElseThrow().getId();
mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, 0, "original-request-uuid", expectedSans, "mockCa", Optional.empty(), Optional.empty()));
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), -1);
@@ -169,11 +203,11 @@ public class EndpointCertificateManagerTest {
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate2) + X509CertificateUtils.toPem(testCertificate2), 0);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(0, endpointCertificateMetadata.get().version());
assertEquals(endpointCertificateMetadata, mockCuratorDb.readEndpointCertificateMetadata(testInstance.id()));
- assertEquals("original-request-uuid", endpointCertificateMetadata.get().request_id());
+ assertEquals("original-request-uuid", endpointCertificateMetadata.get().requestId());
assertEquals(Set.copyOf(expectedCombinedSans), Set.copyOf(endpointCertificateMetadata.get().requestedDnsSans()));
}
@@ -190,12 +224,13 @@ public class EndpointCertificateManagerTest {
" </instance>\n" +
"</deployment>\n");
- ZoneId testZone = zoneRegistryMock.zones().controllerUpgraded().in(Environment.staging).zones().stream().findFirst().orElseThrow().getId();
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone, Optional.of(deploymentSpec.requireInstance("default")));
+ ZoneId testZone = tester.zoneRegistry().zones().all().in(Environment.staging).zones().stream().findFirst().orElseThrow().getId();
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.of(deploymentSpec.requireInstance("default")));
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
assertEquals(0, endpointCertificateMetadata.get().version());
assertEquals(Set.copyOf(expectedCombinedSans), Set.copyOf(endpointCertificateMetadata.get().requestedDnsSans()));
}
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
index fe241976d13..afb56f10c38 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
@@ -292,6 +292,8 @@ public class NodeRepositoryMock implements NodeRepository {
newNode.modelName(node.getModelName());
if (node.getWantToRetire() != null)
newNode.wantToRetire(node.getWantToRetire());
+ if (!node.getReports().isEmpty())
+ newNode.reports(node.getReports());
putNodes(zoneId, newNode.build());
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainerTest.java
index c2983b6343d..16ed6b7ef98 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VCMRMaintainerTest.java
@@ -9,6 +9,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequest;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestSource;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction.State;
+import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VCMRReport;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest.Status;
import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock;
@@ -39,22 +40,32 @@ public class VCMRMaintainerTest {
public void setup() {
tester = new ControllerTester();
maintainer = new VCMRMaintainer(tester.controller(), Duration.ofMinutes(1));
- nodeRepo = tester.serviceRegistry().configServer().nodeRepository();
+ nodeRepo = tester.serviceRegistry().configServer().nodeRepository().allowPatching(true);
}
@Test
public void recycle_hosts_after_completion() {
+ var vcmrReport = new VCMRReport();
+ vcmrReport.addVcmr("id123", ZonedDateTime.now(), ZonedDateTime.now());
var parkedNode = createNode(host1, NodeType.host, Node.State.parked, true);
var failedNode = createNode(host2, NodeType.host, Node.State.failed, false);
+ parkedNode = new Node.Builder(parkedNode)
+ .reports(vcmrReport.toNodeReports())
+ .build();
+
nodeRepo.putNodes(zoneId, List.of(parkedNode, failedNode));
tester.curator().writeChangeRequest(canceledChangeRequest());
maintainer.maintain();
- // Only the parked node is recycled
+ // Only the parked node is recycled, VCMR report is cleared
var nodeList = nodeRepo.list(zoneId, List.of(host1, host2));
assertEquals(Node.State.dirty, nodeList.get(0).state());
assertEquals(Node.State.failed, nodeList.get(1).state());
+
+ var report = nodeList.get(0).reports();
+ assertNull(report.get(VCMRReport.getReportId()));
+
var writtenChangeRequest = tester.curator().readChangeRequest(changeRequestId).get();
assertEquals(Status.COMPLETED, writtenChangeRequest.getStatus());
}
@@ -82,7 +93,7 @@ public class VCMRMaintainerTest {
var activeNode = createNode(host1, NodeType.host, Node.State.active, false);
var failedNode = createNode(host2, NodeType.host, Node.State.failed, false);
nodeRepo.putNodes(zoneId, List.of(activeNode, failedNode));
- nodeRepo.allowPatching(true).hasSpareCapacity(true);
+ nodeRepo.hasSpareCapacity(true);
tester.curator().writeChangeRequest(startingChangeRequest());
maintainer.maintain();
@@ -150,6 +161,13 @@ public class VCMRMaintainerTest {
var approvedChangeRequests = tester.serviceRegistry().changeRequestClient().getApprovedChangeRequests();
assertEquals(1, approvedChangeRequests.size());
+
+ activeNode = nodeRepo.list(zoneId, List.of(host2)).get(0);
+ var report = VCMRReport.fromReports(activeNode.reports());
+ var reportAdded = report.getVcmrs().stream()
+ .filter(vcmr -> vcmr.getId().equals(changeRequestId))
+ .count() == 1;
+ assertTrue(reportAdded);
}
@Test
@@ -157,7 +175,7 @@ public class VCMRMaintainerTest {
var parkedNode = createNode(host1, NodeType.host, Node.State.parked, false);
var retiringNode = createNode(host2, NodeType.host, Node.State.active, true);
nodeRepo.putNodes(zoneId, List.of(parkedNode, retiringNode));
- nodeRepo.allowPatching(true).hasSpareCapacity(true);
+ nodeRepo.hasSpareCapacity(true);
tester.curator().writeChangeRequest(postponedChangeRequest());
maintainer.maintain();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
index b0a0cdf6293..8df2fd87398 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
@@ -19,12 +19,14 @@ import com.yahoo.vespa.hosted.controller.deployment.StepInfo;
import org.junit.Test;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Collections;
import java.util.Optional;
+import java.util.function.BiConsumer;
import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.aborted;
import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.running;
@@ -101,7 +103,7 @@ public class RunSerializerTest {
"badb17"),
122),
run.versions().sourceApplication().get());
- assertEquals(Optional.of(new ConvergenceSummary(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144)),
+ assertEquals(Optional.of(new ConvergenceSummary(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233)),
run.convergenceSummary());
assertEquals(X509CertificateUtils.fromPem("-----BEGIN CERTIFICATE-----\n" +
"MIIBEzCBu6ADAgECAgEBMAoGCCqGSM49BAMEMBQxEjAQBgNVBAMTCW15c2Vydmlj\n" +
@@ -153,4 +155,18 @@ public class RunSerializerTest {
assertEquals(initial, serializer.runFromSlime(serializer.toSlime(initial)));
}
+ @Test
+ public void convergenceSummaryMigrationTest() throws IOException {
+ String data = Files.readString(runFile);
+ BiConsumer<String, ConvergenceSummary> replaceAndAssert = (replace, convergenceSummaryOrNull) -> {
+ byte[] newData = data.replace("\"convergenceSummaryV2\": [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233],", replace).getBytes(StandardCharsets.UTF_8);
+ assertEquals(convergenceSummaryOrNull, serializer.runsFromSlime(SlimeUtils.jsonToSlime(newData)).get(id).convergenceSummary().orElse(null));
+ };
+
+ replaceAndAssert.accept("", null);
+ replaceAndAssert.accept("\"convergenceSummary\": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],", new ConvergenceSummary(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0));
+ replaceAndAssert.accept("\"convergenceSummaryV2\": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],\n" +
+ "\"convergenceSummary\": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],", new ConvergenceSummary(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13));
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json
index a7e5d249a9d..0f40dd27664 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json
@@ -8,7 +8,7 @@
"lastTestRecord": 3,
"lastVespaLogTimestamp": 1196676930000432,
"noNodesDownSince": 321321321321,
- "convergenceSummary": [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144],
+ "convergenceSummaryV2": [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233],
"testerCertificate": "-----BEGIN CERTIFICATE-----\nMIIBEzCBu6ADAgECAgEBMAoGCCqGSM49BAMEMBQxEjAQBgNVBAMTCW15c2Vydmlj\nZTAeFw0xOTA5MDYwNzM3MDZaFw0xOTA5MDcwNzM3MDZaMBQxEjAQBgNVBAMTCW15\nc2VydmljZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM0JhD8fV2DlAkjQOGX3\nY50ryMBr3g2+v/uFiRoxJ1muuSOWYrW7HCQIGuzc04fa0QwtaX/voAZKCV51t6jF\n0fwwCgYIKoZIzj0EAwQDRwAwRAIgVbQ3Co1H4X0gmRrtXSyTU0HgBQu9PXHMmX20\n5MyyPSoCIBltOcmaPfdN03L3zqbqZ6PgUBWsvAHgiBzL3hrtJ+iy\n-----END CERTIFICATE-----",
"steps": {
"deployInitialReal": "unfinished",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json
index 895b49157db..72411d155c7 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json
@@ -70,7 +70,8 @@
"upgradingOs": 0,
"upgradingFirmware": 0,
"services": 1,
- "needNewConfig": 1
+ "needNewConfig": 1,
+ "retiring": 0
}
},
"copyVespaLogs": {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json
index 5aa13f1cf3c..2cf846ab6bb 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json
@@ -149,7 +149,8 @@
"upgradingOs": 0,
"upgradingFirmware": 0,
"services": 1,
- "needNewConfig": 1
+ "needNewConfig": 1,
+ "retiring": 0
}
},
"startStagingSetup": {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response
index 0a92229025b..fe9e8486e9b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response
@@ -1 +1 @@
-{"message":"Created invoice with ID id-123"} \ No newline at end of file
+{"message":"Created invoice with ID id-123","id":"id-123"} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java
index ebde4671859..766a84ca33c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java
@@ -13,6 +13,7 @@ import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.time.Duration;
/**
* @author jonmv
@@ -45,7 +46,7 @@ public class BadgeApiTest extends ControllerContainerTest {
application.submit(applicationPackage)
.runJob(JobType.systemTest)
.runJob(JobType.stagingTest);
- for (int i = 0; i < 32; i++)
+ for (int i = 0; i < 31; i++)
application.failDeployment(JobType.productionUsWest1);
application.triggerJobs();
tester.controller().applications().deploymentTrigger().reTrigger(application.instanceId(), JobType.testEuWest1);
@@ -54,6 +55,17 @@ public class BadgeApiTest extends ControllerContainerTest {
Files.readString(Paths.get(responseFiles + "overview.svg")), 200);
tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=32"),
Files.readString(Paths.get(responseFiles + "history.svg")), 200);
+
+ // New change not reflected before cache entry expires.
+ tester.serviceRegistry().clock().advance(Duration.ofSeconds(59));
+ application.runJob(JobType.productionUsWest1);
+ tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=32"),
+ Files.readString(Paths.get(responseFiles + "history.svg")), 200);
+
+ // Cached entry refreshed after a minute.
+ tester.serviceRegistry().clock().advance(Duration.ofSeconds(1));
+ tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=32"),
+ Files.readString(Paths.get(responseFiles + "history2.svg")), 200);
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg
index f466f38750c..0e30796bae2 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg
@@ -47,7 +47,7 @@
<rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='#00f244'/>
<rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='url(#shade)'/>
<rect x='646.4043039211865' rx='3' width='8' height='20' fill='url(#shadow)'/>
- <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='#bf103c'/>
+ <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='#00f244'/>
<rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='url(#shade)'/>
<rect x='639.3307808029524' rx='3' width='8' height='20' fill='url(#shadow)'/>
<rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='#bf103c'/>
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg
new file mode 100644
index 00000000000..73d65b08b69
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg
@@ -0,0 +1,177 @@
+<svg xmlns='http://www.w3.org/2000/svg' width='659.058159125822' height='20' role='img' aria-label='Deployment Status'>
+ <title>Deployment Status</title>
+ <linearGradient id='light' x2='0' y2='100%'>
+ <stop offset='0' stop-color='#fff' stop-opacity='.5'/>
+ <stop offset='.1' stop-color='#fff' stop-opacity='.15'/>
+ <stop offset='.9' stop-color='#000' stop-opacity='.15'/>
+ <stop offset='1' stop-color='#000' stop-opacity='.5'/>
+ </linearGradient>
+ <linearGradient id='left-light' x2='100%' y2='0'>
+ <stop offset='0' stop-color='#fff' stop-opacity='.3'/>
+ <stop offset='.5' stop-color='#fff' stop-opacity='.1'/>
+ <stop offset='1' stop-color='#fff' stop-opacity='.0'/>
+ </linearGradient>
+ <linearGradient id='right-shadow' x2='100%' y2='0'>
+ <stop offset='0' stop-color='#000' stop-opacity='.0'/>
+ <stop offset='.5' stop-color='#000' stop-opacity='.1'/>
+ <stop offset='1' stop-color='#000' stop-opacity='.3'/>
+ </linearGradient>
+ <linearGradient id='shadow' x2='100%' y2='0'>
+ <stop offset='0' stop-color='#222' stop-opacity='.3'/>
+ <stop offset='.625' stop-color='#555' stop-opacity='.3'/>
+ <stop offset='.9' stop-color='#555' stop-opacity='.05'/>
+ <stop offset='1' stop-color='#555' stop-opacity='.0'/>
+ </linearGradient>
+ <linearGradient id='shade' x2='100%' y2='0'>
+ <stop offset='0' stop-color='#000' stop-opacity='.20'/>
+ <stop offset='0.05' stop-color='#000' stop-opacity='.10'/>
+ <stop offset='1' stop-color='#000' stop-opacity='.0'/>
+ </linearGradient>
+ <linearGradient id='run-on-failure' x1='40%' x2='80%' y2='0%'>
+ <stop offset='0' stop-color='#ab83ff' />
+ <stop offset='1' stop-color='#bf103c' />
+ <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' />
+ <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' />
+ </linearGradient>
+ <linearGradient id='run-on-success' x1='40%' x2='80%' y2='0%'>
+ <stop offset='0' stop-color='#ab83ff' />
+ <stop offset='1' stop-color='#00f244' />
+ <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' />
+ <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' />
+ </linearGradient>
+ <clipPath id='rounded'>
+ <rect width='659.058159125822' height='20' rx='3' fill='#fff'/>
+ </clipPath>
+ <g clip-path='url(#rounded)'>
+ <rect x='653.26809109179' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='#00f244'/>
+ <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='url(#shade)'/>
+ <rect x='646.4043039211865' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='#bf103c'/>
+ <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='url(#shade)'/>
+ <rect x='639.3307808029524' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='#bf103c'/>
+ <rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='url(#shade)'/>
+ <rect x='632.0411128600877' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='624.2919036955889' rx='3' width='13.749209164498811' height='20' fill='#bf103c'/>
+ <rect x='624.2919036955889' rx='3' width='13.749209164498811' height='20' fill='url(#shade)'/>
+ <rect x='624.5286953802824' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='616.5426945310901' rx='3' width='13.986000849192376' height='20' fill='#bf103c'/>
+ <rect x='616.5426945310901' rx='3' width='13.986000849192376' height='20' fill='url(#shade)'/>
+ <rect x='616.7867218317995' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='608.5566936818977' rx='3' width='14.230028149901685' height='20' fill='#bf103c'/>
+ <rect x='608.5566936818977' rx='3' width='14.230028149901685' height='20' fill='url(#shade)'/>
+ <rect x='608.8081776965013' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='600.326665531996' rx='3' width='14.48151216450522' height='20' fill='#bf103c'/>
+ <rect x='600.326665531996' rx='3' width='14.48151216450522' height='20' fill='url(#shade)'/>
+ <rect x='600.5858341144344' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='591.8451533674908' rx='3' width='14.740680746943664' height='20' fill='#bf103c'/>
+ <rect x='591.8451533674908' rx='3' width='14.740680746943664' height='20' fill='url(#shade)'/>
+ <rect x='592.1122413342111' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='583.1044726205471' rx='3' width='15.007768713664104' height='20' fill='#bf103c'/>
+ <rect x='583.1044726205471' rx='3' width='15.007768713664104' height='20' fill='url(#shade)'/>
+ <rect x='583.3797219632555' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='574.096703906883' rx='3' width='15.283018056372542' height='20' fill='#bf103c'/>
+ <rect x='574.096703906883' rx='3' width='15.283018056372542' height='20' fill='url(#shade)'/>
+ <rect x='574.380364011798' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='564.8136858505105' rx='3' width='15.566678161287442' height='20' fill='#bf103c'/>
+ <rect x='564.8136858505105' rx='3' width='15.566678161287442' height='20' fill='url(#shade)'/>
+ <rect x='565.1060137243161' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='555.2470076892231' rx='3' width='15.859006035092985' height='20' fill='#bf103c'/>
+ <rect x='555.2470076892231' rx='3' width='15.859006035092985' height='20' fill='url(#shade)'/>
+ <rect x='555.5482681919269' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='545.3880016541301' rx='3' width='16.16026653779677' height='20' fill='#bf103c'/>
+ <rect x='545.3880016541301' rx='3' width='16.16026653779677' height='20' fill='url(#shade)'/>
+ <rect x='545.6984677390362' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='535.2277351163333' rx='3' width='16.470732622702883' height='20' fill='#bf103c'/>
+ <rect x='535.2277351163333' rx='3' width='16.470732622702883' height='20' fill='url(#shade)'/>
+ <rect x='535.5476880773482' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='524.7570024936304' rx='3' width='16.790685583717845' height='20' fill='#bf103c'/>
+ <rect x='524.7570024936304' rx='3' width='16.790685583717845' height='20' fill='url(#shade)'/>
+ <rect x='525.0867322201259' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='513.9663169099125' rx='3' width='17.12041531021341' height='20' fill='#bf103c'/>
+ <rect x='513.9663169099125' rx='3' width='17.12041531021341' height='20' fill='url(#shade)'/>
+ <rect x='514.3061221493763' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='502.84590159969906' rx='3' width='17.460220549677203' height='20' fill='#bf103c'/>
+ <rect x='502.84590159969906' rx='3' width='17.460220549677203' height='20' fill='url(#shade)'/>
+ <rect x='503.196090228411' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='491.38568105002184' rx='3' width='17.810409178389147' height='20' fill='#bf103c'/>
+ <rect x='491.38568105002184' rx='3' width='17.810409178389147' height='20' fill='url(#shade)'/>
+ <rect x='491.7465703520016' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='479.5752718716327' rx='3' width='18.171298480368904' height='20' fill='#bf103c'/>
+ <rect x='479.5752718716327' rx='3' width='18.171298480368904' height='20' fill='url(#shade)'/>
+ <rect x='479.9471888261109' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='467.40397339126383' rx='3' width='18.543215434847077' height='20' fill='#bf103c'/>
+ <rect x='467.40397339126383' rx='3' width='18.543215434847077' height='20' fill='url(#shade)'/>
+ <rect x='467.7872549689374' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='454.86075795641676' rx='3' width='18.92649701252067' height='20' fill='#bf103c'/>
+ <rect x='454.86075795641676' rx='3' width='18.92649701252067' height='20' fill='url(#shade)'/>
+ <rect x='455.25575142475725' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='441.9342609438961' rx='3' width='19.32149048086113' height='20' fill='#bf103c'/>
+ <rect x='441.9342609438961' rx='3' width='19.32149048086113' height='20' fill='url(#shade)'/>
+ <rect x='442.3413241817867' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='428.61277046303496' rx='3' width='19.728553718751726' height='20' fill='#bf103c'/>
+ <rect x='428.61277046303496' rx='3' width='19.728553718751726' height='20' fill='url(#shade)'/>
+ <rect x='429.03227228502243' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='414.8842167442832' rx='3' width='20.148055540739207' height='20' fill='#bf103c'/>
+ <rect x='414.8842167442832' rx='3' width='20.148055540739207' height='20' fill='url(#shade)'/>
+ <rect x='415.31653723473755' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='400.736161203544' rx='3' width='20.58037603119359' height='20' fill='#bf103c'/>
+ <rect x='400.736161203544' rx='3' width='20.58037603119359' height='20' fill='url(#shade)'/>
+ <rect x='401.1816920610293' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='386.1557851723504' rx='3' width='21.025906888678875' height='20' fill='#bf103c'/>
+ <rect x='386.1557851723504' rx='3' width='21.025906888678875' height='20' fill='url(#shade)'/>
+ <rect x='386.61493006451815' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='371.12987828367153' rx='3' width='21.485051780846597' height='20' fill='#bf103c'/>
+ <rect x='371.12987828367153' rx='3' width='21.485051780846597' height='20' fill='url(#shade)'/>
+ <rect x='371.60305321299876' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='355.6448265028249' rx='3' width='21.958226710173836' height='20' fill='#bf103c'/>
+ <rect x='355.6448265028249' rx='3' width='21.958226710173836' height='20' fill='url(#shade)'/>
+ <rect x='356.13246018352817' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='339.68659979265107' rx='3' width='22.445860390877083' height='20' fill='#bf103c'/>
+ <rect x='339.68659979265107' rx='3' width='22.445860390877083' height='20' fill='url(#shade)'/>
+ <rect x='340.18913403911733' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='323.24073940177396' rx='3' width='22.948394637343355' height='20' fill='#bf103c'/>
+ <rect x='323.24073940177396' rx='3' width='22.948394637343355' height='20' fill='url(#shade)'/>
+ <rect x='323.7586295288612' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='306.2923447644306' rx='3' width='23.466284764430597' height='20' fill='#bf103c'/>
+ <rect x='306.2923447644306' rx='3' width='23.466284764430597' height='20' fill='url(#shade)'/>
+ <rect x='306.82606' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='288.82606' rx='3' width='24.0' height='20' fill='#bf103c'/>
+ <rect x='288.82606' rx='3' width='24.0' height='20' fill='url(#shade)'/>
+ <rect x='288.82606' rx='3' width='8' height='20' fill='url(#shadow)'/>
+ <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='#00f244'/>
+ <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='url(#shade)'/>
+ <rect width='169.18729000000002' height='20' fill='#404040'/>
+ <rect x='-6.0' rx='3' width='175.18729000000002' height='20' fill='url(#shade)'/>
+ <rect width='2' height='20' fill='url(#left-light)'/>
+ <rect x='657.058159125822' width='2' height='20' fill='url(#right-shadow)'/>
+ <rect width='659.058159125822' height='20' fill='url(#light)'/>
+ </g>
+ <g fill='#fff' text-anchor='middle' font-family='Verdana,Geneva,DejaVu Sans,sans-serif' text-rendering='geometricPrecision' font-size='11'>
+ <svg x='6.5' y='3.0' width='16.0' height='16.0' viewBox='0 0 150 150'>
+ <polygon fill='#402a14' fill-opacity='0.5' points='84.84 10 34.1 44.46 34.1 103.78 84.84 68.02 135.57 103.78 135.57 44.46 84.84 10'/>
+ <polygon fill='#402a14' fill-opacity='0.5' points='84.84 68.02 84.84 10 135.57 44.46 135.57 103.78 84.84 68.02'/>
+ <polygon fill='#061a29' fill-opacity='0.5' points='65.07 81.99 14.34 46.22 14.34 105.54 65.07 140 115.81 105.54 115.81 46.22 65.07 81.99'/>
+ <polygon fill='#061a29' fill-opacity='0.5' points='65.07 81.99 65.07 140 14.34 105.54 14.34 46.22 65.07 81.99'/>
+ </svg>
+ <svg x='6.0' y='2.0' width='16.0' height='16.0' viewBox='0 0 150 150'>
+ <linearGradient id='yellow-shaded' x1='91.17' y1='44.83' x2='136.24' y2='73.4' gradientUnits='userSpaceOnUse'>
+ <stop offset='0.01' stop-color='#c6783e'/>
+ <stop offset='0.54' stop-color='#ff9750'/>
+ </linearGradient>
+ <linearGradient id='blue-shaded' x1='60.71' y1='104.56' x2='-15.54' y2='63' gradientUnits='userSpaceOnUse'>
+ <stop offset='0' stop-color='#005a8e'/>
+ <stop offset='0.54' stop-color='#1a7db6'/>
+ </linearGradient>
+ <polygon fill='#ff9d4b' points='84.84 10 34.1 44.46 34.1 103.78 84.84 68.02 135.57 103.78 135.57 44.46 84.84 10'/>
+ <polygon fill='url(#yellow-shaded)' points='84.84 68.02 84.84 10 135.57 44.46 135.57 103.78 84.84 68.02'/>
+ <polygon fill='#1a7db6' points='65.07 81.99 14.34 46.22 14.34 105.54 65.07 140 115.81 105.54 115.81 46.22 65.07 81.99'/>
+ <polygon fill='url(#blue-shaded)' points='65.07 81.99 65.07 140 14.34 105.54 14.34 46.22 65.07 81.99'/>
+ </svg>
+ <text font-size='11' x='96.09364500000001' y='15' fill='#000' fill-opacity='.4' textLength='135.18729000000002'>tenant.application.default</text>
+ <text font-size='11' x='95.59364500000001' y='14' fill='#fff' textLength='135.18729000000002'>tenant.application.default</text>
+ <text font-size='11' x='232.506675' y='15' fill='#000' fill-opacity='.4' textLength='113.63876999999998'>production-us-west-1</text>
+ <text font-size='11' x='232.006675' y='14' fill='#fff' textLength='113.63876999999998'>production-us-west-1</text>
+ </g>
+</svg>
diff --git a/dist/vespa.spec b/dist/vespa.spec
index 2c75319da58..de92077fdbd 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -437,6 +437,28 @@ Requires: %{name}-base-libs = %{version}-%{release}
Vespa - The open big data serving engine - tools
+%package ann-benchmark
+
+Summary: Vespa - The open big data serving engine - ann-benchmark
+
+Requires: %{name}-libs = %{version}-%{release}
+%if 0%{?el7}
+Requires: python3
+%endif
+%if 0%{?el8}
+Requires: python36
+%endif
+%if 0%{?fedora}
+Requires: python3
+%endif
+
+%description ann-benchmark
+
+Vespa - The open big data serving engine - ann-benchmark
+
+Python binding for the Vespa implementation of an HNSW index for
+nearest neighbor search used for low-level benchmarking.
+
%prep
%if 0%{?installdir:1}
%setup -c -D -T
@@ -622,6 +644,7 @@ fi
%{_prefix}/lib/jars/zookeeper-command-line-client-jar-with-dependencies.jar
%{_prefix}/lib/perl5
%{_prefix}/libexec
+%exclude %{_prefix}/libexec/vespa_ann_benchmark
%exclude %{_prefix}/libexec/vespa/common-env.sh
%exclude %{_prefix}/libexec/vespa/node-admin.sh
%exclude %{_prefix}/libexec/vespa/standalone-container.sh
@@ -703,7 +726,9 @@ fi
%dir %{_prefix}
%dir %{_prefix}/lib
%dir %{_prefix}/lib/jars
+%{_prefix}/bin/vespa-feed-client
%{_prefix}/lib/jars/vespa-http-client-jar-with-dependencies.jar
+%{_prefix}/lib/jars/vespa-feed-client-cli.jar
%files config-model-fat
%if %{_defattr_is_vespa_vespa}
@@ -817,4 +842,12 @@ fi
%dir %{_prefix}/lib/jars
%{_prefix}/lib/jars/vespaclient-java-jar-with-dependencies.jar
+%files ann-benchmark
+%if %{_defattr_is_vespa_vespa}
+%defattr(-,%{_vespa_user},%{_vespa_group},-)
+%endif
+%dir %{_prefix}
+%dir %{_prefix}/libexec
+%{_prefix}/libexec/vespa_ann_benchmark
+
%changelog
diff --git a/document/src/main/java/com/yahoo/document/DataType.java b/document/src/main/java/com/yahoo/document/DataType.java
index 104d63cae96..fd7ccfc5e96 100644
--- a/document/src/main/java/com/yahoo/document/DataType.java
+++ b/document/src/main/java/com/yahoo/document/DataType.java
@@ -54,7 +54,7 @@ public abstract class DataType extends Identifiable implements Serializable, Com
public final static NumericDataType BYTE = new NumericDataType("byte", 16, ByteFieldValue.class, ByteFieldValue.getFactory());
public final static PrimitiveDataType PREDICATE = new PrimitiveDataType("predicate", 20, PredicateFieldValue.class, PredicateFieldValue.getFactory());
public final static int tensorDataTypeCode = 21; // All TensorDataType instances have id=21 but carries additional type information serialized separately
- // ADDITIONAL parametrized types added at runtime: map, struct, array, weighted set, annotation reference, tensor
+ // ADDITIONAL parametrized types added at runtime: map, struct, array, weighted set, annotation reference, tensor
// Tags are converted to weightedset<string> when reading the search definition TODO: Remove it
public final static WeightedSetDataType TAG = new WeightedSetDataType(DataType.STRING, true, true);
diff --git a/document/src/main/java/com/yahoo/document/json/TokenBuffer.java b/document/src/main/java/com/yahoo/document/json/TokenBuffer.java
index 88353139b0f..9db80f3972b 100644
--- a/document/src/main/java/com/yahoo/document/json/TokenBuffer.java
+++ b/document/src/main/java/com/yahoo/document/json/TokenBuffer.java
@@ -29,7 +29,7 @@ public class TokenBuffer {
}
}
- private Deque<Token> buffer;
+ private final Deque<Token> buffer;
private int nesting = 0;
public TokenBuffer() {
diff --git a/document/src/main/java/com/yahoo/document/json/readers/TensorReader.java b/document/src/main/java/com/yahoo/document/json/readers/TensorReader.java
index ad016a40fca..27426f584bd 100644
--- a/document/src/main/java/com/yahoo/document/json/readers/TensorReader.java
+++ b/document/src/main/java/com/yahoo/document/json/readers/TensorReader.java
@@ -11,6 +11,7 @@ import com.yahoo.tensor.TensorAddress;
import com.yahoo.tensor.TensorType;
import static com.yahoo.document.json.readers.JsonParserHelpers.*;
+import static com.yahoo.tensor.serialization.JsonFormat.decodeHexString;
/**
* Reads the tensor format defined at
@@ -41,7 +42,7 @@ public class TensorReader {
else if (TENSOR_BLOCKS.equals(buffer.currentName()))
readTensorBlocks(buffer, builder);
else if (builder.type().dimensions().stream().anyMatch(d -> d.isIndexed())) // sparse can be empty
- throw new IllegalArgumentException("Expected a tensor value to contain either 'cells', 'values' or 'blocks'");
+ throw new IllegalArgumentException("Expected a tensor value to contain either 'cells', 'values' or 'blocks', but got: "+buffer.currentName());
}
expectObjectEnd(buffer.currentToken());
tensorFieldValue.assign(builder.build());
@@ -91,10 +92,18 @@ public class TensorReader {
throw new IllegalArgumentException("The 'values' field can only be used with dense tensors. " +
"Use 'cells' or 'blocks' instead");
IndexedTensor.BoundBuilder indexedBuilder = (IndexedTensor.BoundBuilder)builder;
+ if (buffer.currentToken() == JsonToken.VALUE_STRING) {
+ double[] decoded = decodeHexString(buffer.currentText(), builder.type().valueType());
+ for (int i = 0; i < decoded.length; i++) {
+ indexedBuilder.cellByDirectIndex(i, decoded[i]);
+ }
+ return;
+ }
int index = 0;
int initNesting = buffer.nesting();
- for (buffer.next(); buffer.nesting() >= initNesting; buffer.next())
+ for (buffer.next(); buffer.nesting() >= initNesting; buffer.next()) {
indexedBuilder.cellByDirectIndex(index++, readDouble(buffer));
+ }
expectCompositeEnd(buffer.currentToken());
}
@@ -167,17 +176,21 @@ public class TensorReader {
* @return the values read
*/
private static double[] readValues(TokenBuffer buffer, int size, TensorAddress address, TensorType type) {
- expectArrayStart(buffer.currentToken());
-
int index = 0;
- int initNesting = buffer.nesting();
double[] values = new double[size];
- for (buffer.next(); buffer.nesting() >= initNesting; buffer.next())
- values[index++] = readDouble(buffer);
+ if (buffer.currentToken() == JsonToken.VALUE_STRING) {
+ values = decodeHexString(buffer.currentText(), type.valueType());
+ index = values.length;
+ } else {
+ expectArrayStart(buffer.currentToken());
+ int initNesting = buffer.nesting();
+ for (buffer.next(); buffer.nesting() >= initNesting; buffer.next())
+ values[index++] = readDouble(buffer);
+ expectCompositeEnd(buffer.currentToken());
+ }
if (index != size)
throw new IllegalArgumentException((address != null ? "At " + address.toString(type) + ": " : "") +
"Expected " + size + " values, but got " + index);
- expectCompositeEnd(buffer.currentToken());
return values;
}
diff --git a/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java b/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
index da9ab4ea7bf..e50fd9734f7 100644
--- a/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
+++ b/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
@@ -164,10 +164,14 @@ public class JsonReaderTestCase {
new TensorDataType(new TensorType.Builder().mapped("x").mapped("y").build())));
x.addField(new Field("dense_tensor",
new TensorDataType(new TensorType.Builder().indexed("x", 2).indexed("y", 3).build())));
+ x.addField(new Field("dense_int8_tensor",
+ new TensorDataType(TensorType.fromSpec("tensor<int8>(x[2],y[3])"))));
x.addField(new Field("dense_unbound_tensor",
new TensorDataType(new TensorType.Builder().indexed("x").indexed("y").build())));
x.addField(new Field("mixed_tensor",
new TensorDataType(new TensorType.Builder().mapped("x").indexed("y", 3).build())));
+ x.addField(new Field("mixed_bfloat16_tensor",
+ new TensorDataType(TensorType.fromSpec("tensor<bfloat16>(x{},y[3])"))));
x.addField(new Field("mixed_tensor_adv",
new TensorDataType(new TensorType.Builder().mapped("x").mapped("y").mapped("z").indexed("a", 3).build())));
types.registerDocumentType(x);
@@ -1324,6 +1328,41 @@ public class JsonReaderTestCase {
}
@Test
+ public void testParsingOfDenseTensorHexFormat() {
+ Tensor.Builder builder = Tensor.Builder.of(TensorType.fromSpec("tensor<int8>(x[2],y[3])"));
+ builder.cell().label("x", 0).label("y", 0).value(2.0);
+ builder.cell().label("x", 0).label("y", 1).value(3.0);
+ builder.cell().label("x", 0).label("y", 2).value(4.0);
+ builder.cell().label("x", 1).label("y", 0).value(5.0);
+ builder.cell().label("x", 1).label("y", 1).value(6.0);
+ builder.cell().label("x", 1).label("y", 2).value(7.0);
+ Tensor expected = builder.build();
+ Tensor tensor = assertTensorField(expected,
+ createPutWithTensor(inputJson("{",
+ " 'values': \"020304050607\"",
+ "}"), "dense_int8_tensor"), "dense_int8_tensor");
+ assertTrue(tensor instanceof IndexedTensor); // this matters for performance
+ }
+
+ @Test
+ public void testParsingOfMixedTensorHexFormat() {
+ Tensor.Builder builder = Tensor.Builder.of(TensorType.fromSpec("tensor<bfloat16>(x{},y[3])"));
+ builder.cell().label("x", "foo").label("y", 0).value(2.0);
+ builder.cell().label("x", "foo").label("y", 1).value(3.0);
+ builder.cell().label("x", "foo").label("y", 2).value(4.0);
+ builder.cell().label("x", "bar").label("y", 0).value(5.0);
+ builder.cell().label("x", "bar").label("y", 1).value(6.0);
+ builder.cell().label("x", "bar").label("y", 2).value(7.0);
+ Tensor expected = builder.build();
+ String mixedJson = "{\"blocks\":[" +
+ "{\"address\":{\"x\":\"foo\"},\"values\":\"400040404080\"}," +
+ "{\"address\":{\"x\":\"bar\"},\"values\":\"40A040C040E0\"}" +
+ "]}";
+ var put = createPutWithTensor(inputJson(mixedJson), "mixed_bfloat16_tensor");
+ Tensor tensor = assertTensorField(expected, put, "mixed_bfloat16_tensor");
+ }
+
+ @Test
public void testParsingOfMixedTensorOnMixedForm() {
Tensor.Builder builder = Tensor.Builder.of(TensorType.fromSpec("tensor(x{},y[3])"));
builder.cell().label("x", 0).label("y", 0).value(2.0);
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/externslobrokpolicy.cpp b/documentapi/src/vespa/documentapi/messagebus/policies/externslobrokpolicy.cpp
index 9eb28432234..d12a46ed3a4 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/externslobrokpolicy.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/externslobrokpolicy.cpp
@@ -55,10 +55,11 @@ ExternSlobrokPolicy::~ExternSlobrokPolicy()
}
}
-string ExternSlobrokPolicy::init() {
+string
+ExternSlobrokPolicy::init() {
if (_slobroks.size() != 0) {
slobrok::ConfiguratorFactory config(_slobroks);
- _mirror.reset(new MirrorAPI(*_orb, config));
+ _mirror = std::make_unique<MirrorAPI>(*_orb, config);
} else if (_configSources.size() != 0) {
slobrok::ConfiguratorFactory config(
config::ConfigUri(_slobrokConfigId,
diff --git a/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp b/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp
index 9b44dd7519e..54f958f8111 100644
--- a/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp
+++ b/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp
@@ -436,4 +436,11 @@ TEST(OnnxTest, inspect_float_to_int8_conversion) {
//-------------------------------------------------------------------------
}
+TEST(OnnxTest, default_allocator_type) {
+ Ort::AllocatorWithDefaultOptions default_alloc;
+ OrtAllocatorType res = Invalid;
+ Ort::ThrowOnError(Ort::GetApi().MemoryInfoGetType(default_alloc.GetInfo(), &res));
+ fprintf(stderr, "default allocator type: %d\n", int(res));
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/vespa/eval/onnx/onnx_wrapper.cpp b/eval/src/vespa/eval/onnx/onnx_wrapper.cpp
index 3a593f491d8..f848c421c9d 100644
--- a/eval/src/vespa/eval/onnx/onnx_wrapper.cpp
+++ b/eval/src/vespa/eval/onnx/onnx_wrapper.cpp
@@ -414,7 +414,7 @@ struct Onnx::EvalContext::SelectConvertResult {
Onnx::EvalContext::EvalContext(const Onnx &model, const WireInfo &wire_info)
: _model(model),
_wire_info(wire_info),
- _cpu_memory(Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault)),
+ _cpu_memory(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeDefault)),
_param_values(),
_result_values(),
_results(),
@@ -535,6 +535,7 @@ Onnx::Onnx(const vespalib::string &model_file, Optimize optimize)
_options.SetIntraOpNumThreads(1);
_options.SetInterOpNumThreads(1);
_options.SetGraphOptimizationLevel(convert_optimize(optimize));
+ _options.DisableCpuMemArena();
_session = Ort::Session(_shared.env(), model_file.c_str(), _options);
extract_meta_data();
}
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
index 7c46c6e2873..0ce69c182ce 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
@@ -131,9 +131,9 @@ public class FileReferenceDownloader {
return false;
}
} else {
- log.log(logLevel, () -> "Request failed. Req: " + request + "\nSpec: " + connection.getAddress() +
- ", error code: " + request.errorCode() + ", will use another spec for next request" +
- ", retry count " + retryCount + ", rpc timeout " + rpcTimeout.getSeconds());
+ log.log(logLevel, () -> "Downloading file " + fileReference + " from " + connection.getAddress() + " failed: " +
+ request + ", error: " + request.errorMessage() + ", will use another config server for next request" +
+ " (retry count " + retryCount + ", rpc timeout " + rpcTimeout.getSeconds() + ")");
connectionPool.setError(connection, request.errorCode());
return false;
}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index e2a86a3755c..dff6833c015 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -152,7 +152,7 @@ public class Flags {
public static final UnboundBooleanFlag GROUP_SUSPENSION = defineFeatureFlag(
"group-suspension", true,
- List.of("hakon"), "2021-01-22", "2021-05-22",
+ List.of("hakon"), "2021-01-22", "2021-06-22",
"Allow all content nodes in a hierarchical group to suspend at the same time",
"Takes effect on the next suspension request to the Orchestrator.",
APPLICATION_ID);
@@ -184,7 +184,7 @@ public class Flags {
CLUSTER_TYPE);
public static final UnboundStringFlag DEDICATED_CLUSTER_CONTROLLER_FLAVOR = defineStringFlag(
- "dedicated-cluster-controller-flavor", "", List.of("jonmv"), "2021-02-25", "2021-05-25",
+ "dedicated-cluster-controller-flavor", "", List.of("jonmv"), "2021-02-25", "2021-08-25",
"Flavor as <vpu>-<memgb>-<diskgb> to use for dedicated cluster controller nodes",
"Takes effect immediately, for subsequent provisioning",
APPLICATION_ID);
@@ -210,13 +210,6 @@ public class Flags {
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
- public static final UnboundBooleanFlag ENABLE_JDISC_HTTP2 = defineFeatureFlag(
- "enable-jdisc-http2", false,
- List.of("bjorncs", "jonmv"), "2021-04-12", "2021-08-01",
- "Whether jdisc HTTPS connectors should allow HTTP/2",
- "Takes effect at redeployment",
- APPLICATION_ID);
-
public static final UnboundBooleanFlag ENABLE_CUSTOM_ACL_MAPPING = defineFeatureFlag(
"enable-custom-acl-mapping", false,
List.of("mortent","bjorncs"), "2021-04-13", "2021-08-01",
@@ -231,6 +224,13 @@ public class Flags {
"Takes effect after distributor restart",
ZONE_ID, APPLICATION_ID);
+ public static final UnboundBooleanFlag USE_EXTERNAL_RANK_EXPRESSION = defineFeatureFlag(
+ "use-external-rank-expression", false,
+ List.of("baldersheim"), "2021-05-24", "2021-07-01",
+ "Whether to use distributed external rank expression or inline in rankproperties",
+ "Takes effect on next internal redeployment",
+ APPLICATION_ID);
+
public static final UnboundBooleanFlag ENABLE_ROUTING_CORE_DUMP = defineFeatureFlag(
"enable-routing-core-dumps", false,
List.of("tokle"), "2021-04-16", "2021-08-01",
@@ -241,10 +241,17 @@ public class Flags {
public static final UnboundBooleanFlag CFG_DEPLOY_MULTIPART = defineFeatureFlag(
"cfg-deploy-multipart", false,
List.of("tokle"), "2021-05-19", "2021-08-01",
- "Wheter to deploy applications using multipart form data (instead of url params)",
+ "Whether to deploy applications using multipart form data (instead of url params)",
"Takes effect immediately",
APPLICATION_ID);
+ public static final UnboundBooleanFlag VESPA_APP_DOMAIN_IN_CERTIFICATE = defineFeatureFlag(
+ "new-domain-in-certificate", false,
+ List.of("mpolden"), "2021-05-25", "2021-09-01",
+ "Whether to include the vespa-app.cloud names in certificate requests",
+ "Takes effect on next deployment through controller",
+ APPLICATION_ID);
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List<String> owners,
String createdAt, String expiresAt, String description,
diff --git a/model-evaluation/abi-spec.json b/model-evaluation/abi-spec.json
index 63882525808..fac4baf4682 100644
--- a/model-evaluation/abi-spec.json
+++ b/model-evaluation/abi-spec.json
@@ -67,6 +67,7 @@
"public"
],
"methods": [
+ "public void <init>(com.yahoo.vespa.config.search.RankProfilesConfig, com.yahoo.vespa.config.search.core.RankingConstantsConfig, com.yahoo.vespa.config.search.core.RankingExpressionsConfig, com.yahoo.vespa.config.search.core.OnnxModelsConfig, com.yahoo.filedistribution.fileacquirer.FileAcquirer)",
"public void <init>(com.yahoo.vespa.config.search.RankProfilesConfig, com.yahoo.vespa.config.search.core.RankingConstantsConfig, com.yahoo.vespa.config.search.core.OnnxModelsConfig, com.yahoo.filedistribution.fileacquirer.FileAcquirer)",
"public void <init>(java.util.Map)",
"public java.util.Map models()",
@@ -83,6 +84,7 @@
],
"methods": [
"public void <init>(com.yahoo.filedistribution.fileacquirer.FileAcquirer)",
+ "public java.util.Map importFrom(com.yahoo.vespa.config.search.RankProfilesConfig, com.yahoo.vespa.config.search.core.RankingConstantsConfig, com.yahoo.vespa.config.search.core.RankingExpressionsConfig, com.yahoo.vespa.config.search.core.OnnxModelsConfig)",
"public java.util.Map importFrom(com.yahoo.vespa.config.search.RankProfilesConfig, com.yahoo.vespa.config.search.core.RankingConstantsConfig, com.yahoo.vespa.config.search.core.OnnxModelsConfig)",
"protected com.yahoo.tensor.Tensor readTensorFromFile(java.lang.String, com.yahoo.tensor.TensorType, com.yahoo.config.FileReference)"
],
diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java
index 88766da67fc..2d1a0d069a8 100644
--- a/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java
+++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java
@@ -9,9 +9,9 @@ import com.yahoo.filedistribution.fileacquirer.FileAcquirer;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import java.util.Map;
-import java.util.stream.Collectors;
/**
* Evaluates machine-learned models added to Vespa applications and available as config form.
@@ -28,9 +28,19 @@ public class ModelsEvaluator extends AbstractComponent {
@Inject
public ModelsEvaluator(RankProfilesConfig config,
RankingConstantsConfig constantsConfig,
+ RankingExpressionsConfig expressionsConfig,
OnnxModelsConfig onnxModelsConfig,
FileAcquirer fileAcquirer) {
- this(new RankProfilesConfigImporter(fileAcquirer).importFrom(config, constantsConfig, onnxModelsConfig));
+ this(new RankProfilesConfigImporter(fileAcquirer)
+ .importFrom(config, constantsConfig, expressionsConfig, onnxModelsConfig));
+ }
+
+ public ModelsEvaluator(RankProfilesConfig config,
+ RankingConstantsConfig constantsConfig,
+ OnnxModelsConfig onnxModelsConfig,
+ FileAcquirer fileAcquirer) {
+ this(new RankProfilesConfigImporter(fileAcquirer)
+ .importFrom(config, constantsConfig, new RankingExpressionsConfig.Builder().build(), onnxModelsConfig));
}
public ModelsEvaluator(Map<String, Model> models) {
diff --git a/model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java b/model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java
index 1bdb2810ddf..06ca7a60f4c 100644
--- a/model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java
+++ b/model-evaluation/src/main/java/ai/vespa/models/evaluation/RankProfilesConfigImporter.java
@@ -15,6 +15,7 @@ import com.yahoo.tensor.serialization.TypedBinaryFormat;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import java.io.File;
import java.io.IOException;
@@ -51,11 +52,12 @@ public class RankProfilesConfigImporter {
*/
public Map<String, Model> importFrom(RankProfilesConfig config,
RankingConstantsConfig constantsConfig,
+ RankingExpressionsConfig expressionsConfig,
OnnxModelsConfig onnxModelsConfig) {
try {
Map<String, Model> models = new HashMap<>();
for (RankProfilesConfig.Rankprofile profile : config.rankprofile()) {
- Model model = importProfile(profile, constantsConfig, onnxModelsConfig);
+ Model model = importProfile(profile, constantsConfig, expressionsConfig, onnxModelsConfig);
models.put(model.name(), model);
}
return models;
@@ -65,8 +67,16 @@ public class RankProfilesConfigImporter {
}
}
+ @Deprecated
+ public Map<String, Model> importFrom(RankProfilesConfig config,
+ RankingConstantsConfig constantsConfig,
+ OnnxModelsConfig onnxModelsConfig) {
+ return importFrom(config, constantsConfig, new RankingExpressionsConfig.Builder().build(), onnxModelsConfig);
+ }
+
private Model importProfile(RankProfilesConfig.Rankprofile profile,
RankingConstantsConfig constantsConfig,
+ RankingExpressionsConfig expressionsConfig,
OnnxModelsConfig onnxModelsConfig)
throws ParseException {
diff --git a/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelTester.java b/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelTester.java
index d252594e729..b6878f4ea1a 100644
--- a/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelTester.java
+++ b/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelTester.java
@@ -16,6 +16,7 @@ import com.yahoo.tensor.serialization.TypedBinaryFormat;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import java.io.IOException;
import java.util.Map;
@@ -46,10 +47,12 @@ public class ModelTester {
RankProfilesConfig.class).getConfig("");
RankingConstantsConfig constantsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-constants.cfg").toFile()),
RankingConstantsConfig.class).getConfig("");
+ RankingExpressionsConfig expresionsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-expressions.cfg").toFile()),
+ RankingExpressionsConfig.class).getConfig("");
OnnxModelsConfig onnxModelsConfig = new ConfigGetter<>(new FileSource(configDir.append("onnx-models.cfg").toFile()),
- OnnxModelsConfig.class).getConfig("");
+ OnnxModelsConfig.class).getConfig("");
return new RankProfilesConfigImporterWithMockedConstants(Path.fromString(path).append("constants"), MockFileAcquirer.returnFile(null))
- .importFrom(config, constantsConfig, onnxModelsConfig);
+ .importFrom(config, constantsConfig, expresionsConfig, onnxModelsConfig);
}
public ExpressionFunction assertFunction(String name, String expression, Model model) {
diff --git a/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java b/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java
index dce033c79b0..2a8b7479b00 100644
--- a/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java
+++ b/model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java
@@ -12,6 +12,7 @@ import com.yahoo.tensor.TensorType;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.yolean.Exceptions;
import org.junit.Test;
@@ -132,9 +133,11 @@ public class ModelsEvaluatorTest {
RankProfilesConfig.class).getConfig("");
RankingConstantsConfig constantsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-constants.cfg").toFile()),
RankingConstantsConfig.class).getConfig("");
+ RankingExpressionsConfig expressionsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-expressions.cfg").toFile()),
+ RankingExpressionsConfig.class).getConfig("");
OnnxModelsConfig onnxModelsConfig = new ConfigGetter<>(new FileSource(configDir.append("onnx-models.cfg").toFile()),
OnnxModelsConfig.class).getConfig("");
- return new ModelsEvaluator(config, constantsConfig, onnxModelsConfig, MockFileAcquirer.returnFile(null));
+ return new ModelsEvaluator(config, constantsConfig, expressionsConfig, onnxModelsConfig, MockFileAcquirer.returnFile(null));
}
}
diff --git a/model-evaluation/src/test/java/ai/vespa/models/evaluation/OnnxEvaluatorTest.java b/model-evaluation/src/test/java/ai/vespa/models/evaluation/OnnxEvaluatorTest.java
index 1d55fdf9e6a..c137fb9a522 100644
--- a/model-evaluation/src/test/java/ai/vespa/models/evaluation/OnnxEvaluatorTest.java
+++ b/model-evaluation/src/test/java/ai/vespa/models/evaluation/OnnxEvaluatorTest.java
@@ -10,6 +10,7 @@ import com.yahoo.tensor.Tensor;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import org.junit.Test;
import java.io.File;
@@ -54,6 +55,8 @@ public class OnnxEvaluatorTest {
RankProfilesConfig.class).getConfig("");
RankingConstantsConfig constantsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-constants.cfg").toFile()),
RankingConstantsConfig.class).getConfig("");
+ RankingExpressionsConfig expressionsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-expressions.cfg").toFile()),
+ RankingExpressionsConfig.class).getConfig("");
OnnxModelsConfig onnxModelsConfig = new ConfigGetter<>(new FileSource(configDir.append("onnx-models.cfg").toFile()),
OnnxModelsConfig.class).getConfig("");
@@ -63,7 +66,7 @@ public class OnnxEvaluatorTest {
}
FileAcquirer fileAcquirer = MockFileAcquirer.returnFiles(fileMap);
- return new ModelsEvaluator(config, constantsConfig, onnxModelsConfig, fileAcquirer);
+ return new ModelsEvaluator(config, constantsConfig, expressionsConfig, onnxModelsConfig, fileAcquirer);
}
}
diff --git a/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java b/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java
index a69a220e532..d5282bd52f7 100644
--- a/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java
+++ b/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java
@@ -12,6 +12,7 @@ import com.yahoo.tensor.TensorType;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -198,12 +199,14 @@ public class ModelsEvaluationHandlerTest {
RankProfilesConfig.class).getConfig("");
RankingConstantsConfig constantsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-constants.cfg").toFile()),
RankingConstantsConfig.class).getConfig("");
+ RankingExpressionsConfig expressionsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-expressions.cfg").toFile()),
+ RankingExpressionsConfig.class).getConfig("");
OnnxModelsConfig onnxModelsConfig = new ConfigGetter<>(new FileSource(configDir.append("onnx-models.cfg").toFile()),
OnnxModelsConfig.class).getConfig("");
ModelTester.RankProfilesConfigImporterWithMockedConstants importer =
new ModelTester.RankProfilesConfigImporterWithMockedConstants(Path.fromString(path).append("constants"),
MockFileAcquirer.returnFile(null));
- return new ModelsEvaluator(importer.importFrom(config, constantsConfig, onnxModelsConfig));
+ return new ModelsEvaluator(importer.importFrom(config, constantsConfig, expressionsConfig, onnxModelsConfig));
}
private String inputTensor() {
diff --git a/model-evaluation/src/test/java/ai/vespa/models/handler/OnnxEvaluationHandlerTest.java b/model-evaluation/src/test/java/ai/vespa/models/handler/OnnxEvaluationHandlerTest.java
index 6cfda4d8ce8..97692de56ef 100644
--- a/model-evaluation/src/test/java/ai/vespa/models/handler/OnnxEvaluationHandlerTest.java
+++ b/model-evaluation/src/test/java/ai/vespa/models/handler/OnnxEvaluationHandlerTest.java
@@ -11,6 +11,7 @@ import com.yahoo.tensor.Tensor;
import com.yahoo.vespa.config.search.RankProfilesConfig;
import com.yahoo.vespa.config.search.core.OnnxModelsConfig;
import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
+import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -122,6 +123,8 @@ public class OnnxEvaluationHandlerTest {
RankProfilesConfig.class).getConfig("");
RankingConstantsConfig constantsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-constants.cfg").toFile()),
RankingConstantsConfig.class).getConfig("");
+ RankingExpressionsConfig expressionsConfig = new ConfigGetter<>(new FileSource(configDir.append("ranking-expressions.cfg").toFile()),
+ RankingExpressionsConfig.class).getConfig("");
OnnxModelsConfig onnxModelsConfig = new ConfigGetter<>(new FileSource(configDir.append("onnx-models.cfg").toFile()),
OnnxModelsConfig.class).getConfig("");
@@ -131,7 +134,7 @@ public class OnnxEvaluationHandlerTest {
}
FileAcquirer fileAcquirer = MockFileAcquirer.returnFiles(fileMap);
- return new ModelsEvaluator(config, constantsConfig, onnxModelsConfig, fileAcquirer);
+ return new ModelsEvaluator(config, constantsConfig, expressionsConfig, onnxModelsConfig, fileAcquirer);
}
}
diff --git a/model-evaluation/src/test/resources/config/models/ranking-expressions.cfg b/model-evaluation/src/test/resources/config/models/ranking-expressions.cfg
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/model-evaluation/src/test/resources/config/models/ranking-expressions.cfg
diff --git a/model-evaluation/src/test/resources/config/onnx/ranking-expressions.cfg b/model-evaluation/src/test/resources/config/onnx/ranking-expressions.cfg
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/model-evaluation/src/test/resources/config/onnx/ranking-expressions.cfg
diff --git a/model-evaluation/src/test/resources/config/rankexpression/ranking-expressions.cfg b/model-evaluation/src/test/resources/config/rankexpression/ranking-expressions.cfg
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/model-evaluation/src/test/resources/config/rankexpression/ranking-expressions.cfg
diff --git a/model-evaluation/src/test/resources/config/smallconstant/ranking-expressions.cfg b/model-evaluation/src/test/resources/config/smallconstant/ranking-expressions.cfg
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/model-evaluation/src/test/resources/config/smallconstant/ranking-expressions.cfg
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/TestProcessFactory.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/TestProcessFactory.java
index 0586797d259..6771e0d5a27 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/TestProcessFactory.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/process/TestProcessFactory.java
@@ -23,6 +23,8 @@ public class TestProcessFactory implements ProcessFactory {
private final List<SpawnCall> expectedSpawnCalls = new ArrayList<>();
private final List<CommandLine> spawnCommandLines = new ArrayList<>();
+ private boolean muteVerifyAllCommandsExecuted = false;
+
/** Forward call to spawn() to callback. */
public TestProcessFactory interceptSpawn(String commandDescription,
Function<CommandLine, ChildProcess2> callback) {
@@ -32,9 +34,10 @@ public class TestProcessFactory implements ProcessFactory {
// Convenience method for the caller to avoid having to create a TestChildProcess2 instance.
public TestProcessFactory expectSpawn(String commandLineString, TestChildProcess2 toReturn) {
+ int commandIndex = expectedSpawnCalls.size();
return interceptSpawn(
commandLineString,
- commandLine -> defaultSpawn(commandLine, commandLineString, toReturn));
+ commandLine -> defaultSpawn(commandLine, commandLineString, toReturn, commandIndex));
}
// Convenience method for the caller to avoid having to create a TestChildProcess2 instance.
@@ -54,6 +57,8 @@ public class TestProcessFactory implements ProcessFactory {
}
public void verifyAllCommandsExecuted() {
+ if (muteVerifyAllCommandsExecuted) return;
+
if (spawnCommandLines.size() < expectedSpawnCalls.size()) {
int missingCommandIndex = spawnCommandLines.size();
throw new IllegalStateException("Command #" + missingCommandIndex +
@@ -81,12 +86,14 @@ public class TestProcessFactory implements ProcessFactory {
return expectedSpawnCalls.get(spawnCommandLines.size() - 1).callback.apply(commandLine);
}
- private static ChildProcess2 defaultSpawn(CommandLine commandLine,
+ private ChildProcess2 defaultSpawn(CommandLine commandLine,
String expectedCommandLineString,
- ChildProcess2 toReturn) {
+ ChildProcess2 toReturn,
+ int commandSequenceNumber) {
String actualCommandLineString = commandLine.toString();
if (!Objects.equals(actualCommandLineString, expectedCommandLineString)) {
- throw new IllegalArgumentException("Expected command line '" +
+ muteVerifyAllCommandsExecuted = true;
+ throw new IllegalArgumentException("Expected command #" + commandSequenceNumber + " to be '" +
expectedCommandLineString + "' but got '" + actualCommandLineString + "'");
}
diff --git a/node-repository/pom.xml b/node-repository/pom.xml
index 05220b6977f..d2deaf51afe 100644
--- a/node-repository/pom.xml
+++ b/node-repository/pom.xml
@@ -75,7 +75,7 @@
<dependency>
<groupId>org.questdb</groupId>
<artifactId>questdb</artifactId>
- <version>5.0.6</version>
+ <version>6.0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
index 79b09348d21..0a1c6c5df6b 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
@@ -49,7 +49,6 @@ public class AllocationOptimizer {
new ClusterResources(maximumNodes, maximumNodes, NodeResources.unspecified()));
else
limits = atLeast(minimumNodes, limits);
-
Optional<AllocatableClusterResources> bestAllocation = Optional.empty();
NodeList hosts = nodeRepository.nodes().list().hosts();
for (int groups = limits.min().groups(); groups <= limits.max().groups(); groups++) {
@@ -67,8 +66,8 @@ public class AllocationOptimizer {
nodeResourcesWith(nodesAdjustedForRedundancy,
groupsAdjustedForRedundancy,
limits, target, current, clusterModel));
-
var allocatableResources = AllocatableClusterResources.from(next, current.clusterSpec(), limits, hosts, nodeRepository);
+
if (allocatableResources.isEmpty()) continue;
if (bestAllocation.isEmpty() || allocatableResources.get().preferableTo(bestAllocation.get()))
bestAllocation = allocatableResources;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsDb.java
index 5ec1aac7d2d..ba317c0a58e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsDb.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsDb.java
@@ -43,6 +43,9 @@ public interface MetricsDb {
/** Returns all cluster level metric snapshots for a given cluster */
ClusterTimeseries getClusterTimeseries(ApplicationId applicationId, ClusterSpec.Id clusterId);
+ /** Returns the number of times QuestDb has returned null records since last gc */
+ default int getNullRecordsCount() { return 0; }
+
/** Must be called intermittently (as long as add is called) to gc old data */
void gc();
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java
index f71e7d608e0..2c29b9cc560 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java
@@ -59,6 +59,8 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
private long highestTimestampAdded = 0;
+ private volatile int nullRecords = 0;
+
@Inject
public QuestMetricsDb() {
this(Defaults.getDefaults().underVespaHome("var/db/vespa/autoscaling"), Clock.systemUTC());
@@ -80,9 +82,7 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
// silence Questdb's custom logging system
IOUtils.writeFile(new File(dataDir, "quest-log.conf"), new byte[0]);
- System.setProperty("questdbLog", dataDir + "/quest-log.conf");
- System.setProperty("org.jooq.no-logo", "true");
-
+ System.setProperty("out", dataDir + "/quest-log.conf");
CairoConfiguration configuration = new DefaultCairoConfiguration(dataDir);
engine = new CairoEngine(configuration);
sqlCompiler = ThreadLocal.withInitial(() -> new SqlCompiler(engine));
@@ -183,8 +183,11 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
}
}
+ public int getNullRecordsCount() { return nullRecords; }
+
@Override
public void gc() {
+ nullRecords = 0;
gc(nodeTable);
gc(clusterTable);
}
@@ -344,7 +347,7 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.of("UTC"));
String from = formatter.format(startTime).substring(0, 19) + ".000000Z";
String to = formatter.format(clock.instant()).substring(0, 19) + ".000000Z";
- String sql = "select * from " + nodeTable + " where at in('" + from + "', '" + to + "');";
+ String sql = "select * from " + nodeTable + " where at between('" + from + "', '" + to + "');";
// WHERE clauses does not work:
// String sql = "select * from " + tableName + " where hostname in('host1', 'host2', 'host3');";
@@ -354,6 +357,10 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb {
try (RecordCursor cursor = factory.getCursor(context)) {
Record record = cursor.getRecord();
while (cursor.hasNext()) {
+ if (record == null || record.getStr(0) == null) { // Observed to happen. QuestDb bug?
+ nullRecords++;
+ continue;
+ }
String hostname = record.getStr(0).toString();
if (hostnames.isEmpty() || hostnames.contains(hostname)) {
snapshots.put(hostname,
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java
index 17bd4b9174c..e98da35aa6a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.maintenance;
-import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.Zone;
@@ -43,26 +42,26 @@ import java.util.stream.Collectors;
public class FailedExpirer extends NodeRepositoryMaintainer {
private static final Logger log = Logger.getLogger(FailedExpirer.class.getName());
- // We will stop giving the nodes back to Openstack for break-fix, setting this to number a high value, we might
- // eventually remove this counter and recycling nodes forever
+ // Try recycling nodes until reaching this many failures
+ // TODO: Consider removing this altogether as this effectively always recycles nodes
private static final int maxAllowedFailures = 50;
private final NodeRepository nodeRepository;
- private final Duration defaultExpiry; // Grace period to allow recovery of data
- private final Duration containerExpiry; // Stateless nodes, no data to recover
+ private final Duration statefulExpiry; // Stateful nodes: Grace period to allow recovery of data
+ private final Duration statelessExpiry; // Stateless nodes: No data to recover
FailedExpirer(NodeRepository nodeRepository, Zone zone, Duration interval, Metric metric) {
super(nodeRepository, interval, metric);
this.nodeRepository = nodeRepository;
if (zone.system().isCd()) {
- defaultExpiry = containerExpiry = Duration.ofMinutes(30);
+ statefulExpiry = statelessExpiry = Duration.ofMinutes(30);
} else {
if (zone.environment() == Environment.staging || zone.environment() == Environment.test) {
- defaultExpiry = Duration.ofHours(1);
+ statefulExpiry = Duration.ofHours(1);
} else {
- defaultExpiry = Duration.ofDays(4);
+ statefulExpiry = Duration.ofDays(4);
}
- containerExpiry = Duration.ofHours(1);
+ statelessExpiry = Duration.ofHours(1);
}
}
@@ -74,10 +73,11 @@ public class FailedExpirer extends NodeRepositoryMaintainer {
recycleIf(remainingNodes, node -> node.allocation().isEmpty());
recycleIf(remainingNodes, node ->
- node.allocation().get().membership().cluster().type() == ClusterSpec.Type.container &&
- node.history().hasEventBefore(History.Event.Type.failed, clock().instant().minus(containerExpiry)));
+ !node.allocation().get().membership().cluster().isStateful() &&
+ node.history().hasEventBefore(History.Event.Type.failed, clock().instant().minus(statelessExpiry)));
recycleIf(remainingNodes, node ->
- node.history().hasEventBefore(History.Event.Type.failed, clock().instant().minus(defaultExpiry)));
+ node.allocation().get().membership().cluster().isStateful() &&
+ node.history().hasEventBefore(History.Event.Type.failed, clock().instant().minus(statefulExpiry)));
return true;
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java
index a2e02c15737..85437b3e78a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java
@@ -19,6 +19,7 @@ import com.yahoo.vespa.hosted.provision.Node.State;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
+import com.yahoo.vespa.hosted.provision.node.ClusterId;
import com.yahoo.vespa.hosted.provision.node.History;
import com.yahoo.vespa.hosted.provision.persistence.CacheStats;
import com.yahoo.vespa.orchestrator.Orchestrator;
@@ -30,7 +31,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
@@ -84,11 +84,11 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
}
private void updateAllocationMetrics(NodeList nodes) {
- Map<ClusterKey, List<Node>> byCluster = nodes.stream()
- .filter(node -> node.allocation().isPresent())
- .filter(node -> !node.allocation().get().owner().instance().isTester())
- .collect(Collectors.groupingBy(node -> new ClusterKey(node.allocation().get().owner(), node.allocation().get().membership().cluster().id())));
- byCluster.forEach((clusterKey, allocatedNodes) -> {
+ Map<ClusterId, List<Node>> byCluster = nodes.stream()
+ .filter(node -> node.allocation().isPresent())
+ .filter(node -> !node.allocation().get().owner().instance().isTester())
+ .collect(Collectors.groupingBy(node -> new ClusterId(node.allocation().get().owner(), node.allocation().get().membership().cluster().id())));
+ byCluster.forEach((clusterId, allocatedNodes) -> {
int activeNodes = 0;
int nonActiveNodes = 0;
for (var node : allocatedNodes) {
@@ -104,7 +104,7 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
} else {
nonActiveFraction = (double) nonActiveNodes / ((double) activeNodes + (double) nonActiveNodes);
}
- Metric.Context context = getContext(dimensions(clusterKey.application, clusterKey.cluster));
+ Metric.Context context = getContext(dimensions(clusterId.application(), clusterId.cluster()));
metric.set("nodes.active", activeNodes, context);
metric.set("nodes.nonActive", nonActiveNodes, context);
metric.set("nodes.nonActiveFraction", nonActiveFraction, context);
@@ -112,16 +112,16 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
}
private void updateExclusiveSwitchMetrics(NodeList nodes) {
- Map<ClusterKey, List<Node>> byCluster = nodes.stream()
+ Map<ClusterId, List<Node>> byCluster = nodes.stream()
.filter(node -> node.type() == NodeType.tenant)
.filter(node -> node.state() == State.active)
.filter(node -> node.allocation().isPresent())
- .collect(Collectors.groupingBy(node -> new ClusterKey(node.allocation().get().owner(), node.allocation().get().membership().cluster().id())));
- byCluster.forEach((clusterKey, clusterNodes) -> {
+ .collect(Collectors.groupingBy(node -> new ClusterId(node.allocation().get().owner(), node.allocation().get().membership().cluster().id())));
+ byCluster.forEach((clusterId, clusterNodes) -> {
NodeList clusterHosts = nodes.parentsOf(NodeList.copyOf(clusterNodes));
long nodesOnExclusiveSwitch = NodeList.copyOf(clusterNodes).onExclusiveSwitch(clusterHosts).size();
double exclusiveSwitchRatio = nodesOnExclusiveSwitch / (double) clusterNodes.size();
- metric.set("nodes.exclusiveSwitchFraction", exclusiveSwitchRatio, getContext(dimensions(clusterKey.application, clusterKey.cluster)));
+ metric.set("nodes.exclusiveSwitchFraction", exclusiveSwitchRatio, getContext(dimensions(clusterId.application(), clusterId.cluster())));
});
}
@@ -390,30 +390,4 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
.reduce(host.flavor().resources().justNumbers(), NodeResources::subtract);
}
- private static class ClusterKey {
-
- private final ApplicationId application;
- private final ClusterSpec.Id cluster;
-
- public ClusterKey(ApplicationId application, ClusterSpec.Id cluster) {
- this.application = application;
- this.cluster = cluster;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ClusterKey that = (ClusterKey) o;
- return application.equals(that.application) &&
- cluster.equals(that.cluster);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(application, cluster);
- }
-
- }
-
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java
index f95094f891c..1ea4577f7fe 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java
@@ -6,7 +6,6 @@ import com.yahoo.jdisc.Metric;
import com.yahoo.lang.MutableInteger;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.autoscale.MetricsFetcher;
-import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb;
import com.yahoo.vespa.hosted.provision.autoscale.MetricsResponse;
import com.yahoo.yolean.Exceptions;
@@ -51,6 +50,10 @@ public class NodeMetricsDbMaintainer extends NodeRepositoryMaintainer {
if (++done < applications.size())
Thread.sleep(pauseMs);
}
+
+ if (nodeRepository().metricsDb().getNullRecordsCount() > 0)
+ log.warning(nodeRepository().metricsDb().getNullRecordsCount() + " records returned null");
+
nodeRepository().metricsDb().gc();
// Suppress failures for manual zones for now to avoid noise
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/ClusterId.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/ClusterId.java
new file mode 100644
index 00000000000..ec9e4dc981b
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/ClusterId.java
@@ -0,0 +1,50 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.node;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
+
+import java.util.Objects;
+
+/**
+ * Identifies a cluster in an application.
+ *
+ * @author mpolden
+ */
+public class ClusterId {
+
+ private final ApplicationId application;
+ private final ClusterSpec.Id cluster;
+
+ public ClusterId(ApplicationId application, ClusterSpec.Id cluster) {
+ this.application = Objects.requireNonNull(application);
+ this.cluster = Objects.requireNonNull(cluster);
+ }
+
+ public ApplicationId application() {
+ return application;
+ }
+
+ public ClusterSpec.Id cluster() {
+ return cluster;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClusterId that = (ClusterId) o;
+ return application.equals(that.application) && cluster.equals(that.cluster);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(application, cluster);
+ }
+
+ @Override
+ public String toString() {
+ return cluster + " of " + application;
+ }
+
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java
index 266855002ea..2ef68566a87 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java
@@ -520,32 +520,34 @@ public class Nodes {
/**
* Throws if the given node cannot be removed. Removal is allowed if:
- * - Tenant node: node is unallocated
+ * - Tenant node:
+ * - non-recursively: node is unallocated
+ * - recursively: node is unallocated or node is in failed|parked
* - Host node: iff in state provisioned|failed|parked
* - Child node:
- * If only removing the container node: node in state ready
- * If also removing the parent node: child is in state provisioned|failed|parked|dirty|ready
+ * - non-recursively: node in state ready
+ * - recursively: child is in state provisioned|failed|parked|dirty|ready
*/
- private void requireRemovable(Node node, boolean removingAsChild, boolean force) {
+ private void requireRemovable(Node node, boolean removingRecursively, boolean force) {
if (force) return;
- if (node.type() == NodeType.tenant && node.allocation().isPresent())
- illegal(node + " is currently allocated and cannot be removed");
-
- if (!node.type().isHost() && !removingAsChild) {
- if (node.state() != Node.State.ready)
- illegal(node + " can not be removed as it is not in the state " + Node.State.ready);
- }
- else if (!node.type().isHost()) { // removing a child node
- Set<Node.State> legalStates = EnumSet.of(Node.State.provisioned, Node.State.failed, Node.State.parked, Node.State.dirty, Node.State.ready);
- if ( ! legalStates.contains(node.state()))
- illegal(node + " can not be removed as it is not in the states " + legalStates);
+ if (node.type() == NodeType.tenant && node.allocation().isPresent()) {
+ EnumSet<Node.State> removableStates = EnumSet.of(Node.State.failed, Node.State.parked);
+ if (!removingRecursively || !removableStates.contains(node.state()))
+ illegal(node + " is currently allocated and cannot be removed while in " + node.state());
}
- else { // a host
- Set<Node.State> legalStates = EnumSet.of(Node.State.provisioned, Node.State.failed, Node.State.parked);
- if (! legalStates.contains(node.state()))
- illegal(node + " can not be removed as it is not in the states " + legalStates);
+
+ final Set<Node.State> removableStates;
+ if (node.type().isHost()) {
+ removableStates = EnumSet.of(Node.State.provisioned, Node.State.failed, Node.State.parked);
+ } else {
+ removableStates = removingRecursively
+ ? EnumSet.of(Node.State.provisioned, Node.State.failed, Node.State.parked, Node.State.dirty, Node.State.ready)
+ // When not removing recursively, we can only remove children in state ready
+ : EnumSet.of(Node.State.ready);
}
+ if (!removableStates.contains(node.state()))
+ illegal(node + " can not be removed while in " + node.state());
}
/**
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java
index fce9eb562c9..20536b9dd9f 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java
@@ -2,8 +2,6 @@
package com.yahoo.vespa.hosted.provision.os;
import com.yahoo.component.Version;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.flags.IntFlag;
import com.yahoo.vespa.flags.PermanentFlags;
@@ -12,6 +10,7 @@ import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
+import com.yahoo.vespa.hosted.provision.node.ClusterId;
import com.yahoo.vespa.hosted.provision.node.filter.NodeListFilter;
import java.time.Instant;
@@ -19,7 +18,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
@@ -59,36 +57,37 @@ public class RebuildingOsUpgrader implements OsUpgrader {
}
/** Returns the number of hosts of given type that can be rebuilt concurrently */
- private int upgradeLimit(NodeType hostType, NodeList hosts) {
+ private int rebuildLimit(NodeType hostType, NodeList hostsOfType) {
+ if (hostsOfType.stream().anyMatch(host -> host.type() != hostType)) illegal("All hosts must be a " + hostType);
int limit = hostType == NodeType.host ? maxRebuilds.value() : 1;
- return Math.max(0, limit - hosts.rebuilding().size());
+ return Math.max(0, limit - hostsOfType.rebuilding().size());
}
private List<Node> rebuildableHosts(OsVersionTarget target, NodeList allNodes) {
NodeList hostsOfTargetType = allNodes.nodeType(target.nodeType());
- NodeList activeHosts = hostsOfTargetType.state(Node.State.active);
- int upgradeLimit = upgradeLimit(target.nodeType(), hostsOfTargetType);
+ int rebuildLimit = rebuildLimit(target.nodeType(), hostsOfTargetType);
// Find stateful clusters with retiring nodes
NodeList activeNodes = allNodes.state(Node.State.active);
Set<ClusterId> retiringClusters = statefulClustersOf(activeNodes.nodeType(target.nodeType().childNodeType())
.retiring());
- // Upgrade hosts not running stateful clusters that are already retiring
- List<Node> hostsToUpgrade = new ArrayList<>(upgradeLimit);
- NodeList candidates = activeHosts.not().rebuilding()
- .osVersionIsBefore(target.version())
- .byIncreasingOsVersion();
+ // Rebuild hosts not containing stateful clusters with retiring nodes, up to rebuild limit
+ List<Node> hostsToRebuild = new ArrayList<>(rebuildLimit);
+ NodeList candidates = hostsOfTargetType.state(Node.State.active)
+ .not().rebuilding()
+ .osVersionIsBefore(target.version())
+ .byIncreasingOsVersion();
for (Node host : candidates) {
- if (hostsToUpgrade.size() == upgradeLimit) break;
+ if (hostsToRebuild.size() == rebuildLimit) break;
Set<ClusterId> clustersOnHost = statefulClustersOf(activeNodes.childrenOf(host));
- boolean canUpgrade = Collections.disjoint(retiringClusters, clustersOnHost);
- if (canUpgrade) {
- hostsToUpgrade.add(host);
+ boolean canRebuild = Collections.disjoint(retiringClusters, clustersOnHost);
+ if (canRebuild) {
+ hostsToRebuild.add(host);
retiringClusters.addAll(clustersOnHost);
}
}
- return Collections.unmodifiableList(hostsToUpgrade);
+ return Collections.unmodifiableList(hostsToRebuild);
}
private void rebuild(Node host, Version target, Instant now) {
@@ -102,7 +101,7 @@ public class RebuildingOsUpgrader implements OsUpgrader {
private static Set<ClusterId> statefulClustersOf(NodeList nodes) {
Set<ClusterId> clusters = new HashSet<>();
for (Node node : nodes) {
- if (node.type().isHost()) throw new IllegalArgumentException("All nodes must be children, got host " + node);
+ if (node.type().isHost()) illegal("All nodes must be children, got host " + node);
if (node.allocation().isEmpty()) continue;
Allocation allocation = node.allocation().get();
if (!allocation.membership().cluster().isStateful()) continue;
@@ -111,34 +110,8 @@ public class RebuildingOsUpgrader implements OsUpgrader {
return clusters;
}
- private static class ClusterId {
-
- private final ApplicationId application;
- private final ClusterSpec.Id cluster;
-
- public ClusterId(ApplicationId application, ClusterSpec.Id cluster) {
- this.application = Objects.requireNonNull(application);
- this.cluster = Objects.requireNonNull(cluster);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ClusterId that = (ClusterId) o;
- return application.equals(that.application) && cluster.equals(that.cluster);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(application, cluster);
- }
-
- @Override
- public String toString() {
- return cluster + " of " + application;
- }
-
+ private static void illegal(String msg) {
+ throw new IllegalArgumentException(msg);
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
index 81a56e4d47e..def992a264b 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
@@ -62,8 +62,8 @@ public class GroupPreparer {
List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups) {
String allocateOsRequirement = allocateOsRequirementFlag
- .with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm())
- .value();
+ .with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm())
+ .value();
// Try preparing in memory without global unallocated lock. Most of the time there should be no changes and we
// can return nodes previously allocated.
@@ -89,24 +89,16 @@ public class GroupPreparer {
allocateOsRequirement);
NodeType hostType = allocation.nodeType().hostType();
if (canProvisionDynamically(hostType)) {
- final Version osVersion;
- if (allocateOsRequirement.equals("rhel8")) {
- osVersion = new Version(8, Integer.MAX_VALUE /* always use latest 8 version */, 0);
- } else if (allocateOsRequirement.equals("rhel7")) {
- osVersion = new Version(7, Integer.MAX_VALUE /* always use latest 7 version */, 0);
- } else {
- osVersion = nodeRepository.osVersions().targetFor(hostType).orElse(Version.emptyVersion);
- }
HostSharing sharing = hostSharing(requestedNodes, hostType);
List<ProvisionedHost> provisionedHosts = allocation.hostDeficit()
- .map(deficit -> {
- return hostProvisioner.get().provisionHosts(allocation.provisionIndices(deficit.count()),
- hostType,
- deficit.resources(),
- application,
- osVersion,
- sharing);
- })
+ .map(deficit ->
+ hostProvisioner.get().provisionHosts(allocation.provisionIndices(deficit.count()),
+ hostType,
+ deficit.resources(),
+ application,
+ decideOsVersion(allocateOsRequirement, hostType),
+ sharing)
+ )
.orElseGet(List::of);
// At this point we have started provisioning of the hosts, the first priority is to make sure that
@@ -141,12 +133,17 @@ public class GroupPreparer {
List<Node> surplusActiveNodes, Supplier<Integer> nextIndex, int wantedGroups,
Mutex allocationLock, String allocateOsRequirement) {
LockedNodeList allNodes = nodeRepository.nodes().list(allocationLock);
- NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requestedNodes,
- nextIndex, nodeRepository);
- NodePrioritizer prioritizer = new NodePrioritizer(
- allNodes, application, cluster, requestedNodes, wantedGroups,
- nodeRepository.zone().getCloud().dynamicProvisioning(), nodeRepository.nameResolver(),
- nodeRepository.resourcesCalculator(), nodeRepository.spareCount(), allocateOsRequirement);
+ NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requestedNodes, nextIndex, nodeRepository);
+ NodePrioritizer prioritizer = new NodePrioritizer(allNodes,
+ application,
+ cluster,
+ requestedNodes,
+ wantedGroups,
+ nodeRepository.zone().getCloud().dynamicProvisioning(),
+ nodeRepository.nameResolver(),
+ nodeRepository.resourcesCalculator(),
+ nodeRepository.spareCount(),
+ allocateOsRequirement);
allocation.offer(prioritizer.collect(surplusActiveNodes));
return allocation;
}
@@ -164,4 +161,13 @@ public class GroupPreparer {
return sharing;
}
+ private Version decideOsVersion(String allocateOsRequirement, NodeType hostType) {
+ if (allocateOsRequirement.equals("rhel8"))
+ return new Version(8, Integer.MAX_VALUE /* always use latest 8 version */, 0);
+ else if (allocateOsRequirement.equals("rhel7"))
+ return new Version(7, Integer.MAX_VALUE /* always use latest 7 version */, 0);
+ else
+ return nodeRepository.osVersions().targetFor(hostType).orElse(Version.emptyVersion);
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
index ab881a68ebe..ee8ce23a5c0 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
@@ -8,6 +8,7 @@ import com.yahoo.config.provision.ApplicationTransaction;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostFilter;
import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.NodeResources;
@@ -27,7 +28,6 @@ import com.yahoo.vespa.hosted.provision.autoscale.AllocatableClusterResources;
import com.yahoo.vespa.hosted.provision.autoscale.AllocationOptimizer;
import com.yahoo.vespa.hosted.provision.autoscale.ClusterModel;
import com.yahoo.vespa.hosted.provision.autoscale.Limits;
-import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb;
import com.yahoo.vespa.hosted.provision.autoscale.ResourceTarget;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter;
@@ -184,7 +184,7 @@ public class NodeRepositoryProvisioner implements Provisioner {
current,
clusterModel,
limits)
- .orElseThrow(() -> new IllegalArgumentException("No allocation possible within " + limits))
+ .orElseThrow(() -> newNoAllocationPossible(current.clusterSpec(), limits))
.advertisedResources();
}
@@ -224,4 +224,37 @@ public class NodeRepositoryProvisioner implements Provisioner {
}
}
+ private IllegalArgumentException newNoAllocationPossible(ClusterSpec spec, Limits limits) {
+ StringBuilder message = new StringBuilder("No allocation possible within ").append(limits);
+
+ boolean exclusiveHosts = spec.isExclusive() || nodeRepository.zone().getCloud().dynamicProvisioning();
+ if (exclusiveHosts)
+ message.append(". Nearest allowed node resources: ").append(findNearestNodeResources(limits));
+
+ return new IllegalArgumentException(message.toString());
+ }
+
+ private NodeResources findNearestNodeResources(Limits limits) {
+ NodeResources nearestMin = nearestFlavorResources(limits.min().nodeResources());
+ NodeResources nearestMax = nearestFlavorResources(limits.max().nodeResources());
+ if (limits.min().nodeResources().distanceTo(nearestMin) < limits.max().nodeResources().distanceTo(nearestMax))
+ return nearestMin;
+ else
+ return nearestMax;
+ }
+
+ /** Returns the advertised flavor resources which are nearest to the given resources */
+ private NodeResources nearestFlavorResources(NodeResources requestedResources) {
+ NodeResources nearestHostResources = nodeRepository.flavors().getFlavors().stream()
+ .map(flavor -> nodeRepository.resourcesCalculator().advertisedResourcesOf(flavor))
+ .filter(resources -> resources.diskSpeed().compatibleWith(requestedResources.diskSpeed()))
+ .filter(resources -> resources.storageType().compatibleWith(requestedResources.storageType()))
+ .min(Comparator.comparingDouble(resources -> resources.distanceTo(requestedResources)))
+ .orElseThrow()
+ .withBandwidthGbps(requestedResources.bandwidthGbps());
+ if ( nearestHostResources.storageType() == NodeResources.StorageType.remote)
+ nearestHostResources = nearestHostResources.withDiskGb(requestedResources.diskGb());
+ return nearestHostResources;
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
index d2b701e5312..97f935d273b 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
@@ -45,8 +45,7 @@ class Preparer {
catch (OutOfCapacityException e) {
throw new OutOfCapacityException("Could not satisfy " + requestedNodes +
( wantedGroups > 1 ? " (in " + wantedGroups + " groups)" : "") +
- " in " + application + " " + cluster +
- ": " + e.getMessage());
+ " in " + application + " " + cluster + ": " + e.getMessage());
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java
index b3ee30e4258..d20d6dd1dc4 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.provision.os;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.NodeResources;
@@ -24,7 +23,6 @@ import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
-import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -435,18 +433,11 @@ public class OsVersionsTest {
}
@Test
- public void upgrade_by_rebuilding_is_limited_by_infrastructure_host() {
+ public void upgrade_by_rebuilding_limits_infrastructure_host() {
int hostCount = 3;
tester.flagSource().withIntFlag(PermanentFlags.MAX_REBUILDS.id(), hostCount);
var versions = new OsVersions(tester.nodeRepository(), false, Integer.MAX_VALUE);
- ApplicationId routingApp = ApplicationId.from("t1", "a1", "i1");
- List<Node> proxyHosts = provisionInfraApplication(hostCount, infraApplication, NodeType.proxyhost);
- for (int i = 0; i < proxyHosts.size(); i++) {
- tester.makeReadyChildren(1, i, new NodeResources(4,8, 100, 0.3),
- NodeType.proxy, proxyHosts.get(i).hostname(), (index) -> "proxy" + index);
- }
- Capacity capacity = Capacity.fromRequiredNodeType(NodeType.proxy);
- tester.deploy(routingApp, capacity);
+ provisionInfraApplication(hostCount, infraApplication, NodeType.proxyhost);
Supplier<NodeList> hosts = () -> tester.nodeRepository().nodes().list().nodeType(NodeType.proxyhost);
// All hosts are on initial version
@@ -458,12 +449,11 @@ public class OsVersionsTest {
var version1 = Version.fromString("8.0");
versions.setTarget(NodeType.proxyhost, version1, Duration.ZERO, false);
- // Upgrades 1 host at a time
+ // Upgrades 1 infrastructure host at a time
for (int i = 0; i < hostCount; i++) {
versions.resumeUpgradeOf(NodeType.proxyhost, true);
List<Node> hostsRebuilding = hosts.get().rebuilding().asList();
assertEquals(1, hostsRebuilding.size());
- replaceNodes(routingApp, (app) -> tester.deploy(app, capacity));
completeRebuildOf(hostsRebuilding, NodeType.proxyhost);
}
}
@@ -475,18 +465,14 @@ public class OsVersionsTest {
}
private void replaceNodes(ApplicationId application) {
- replaceNodes(application, this::deployApplication);
- }
-
- private void replaceNodes(ApplicationId application, Consumer<ApplicationId> deployer) {
// Deploy to retire nodes
- deployer.accept(application);
+ deployApplication(application);
List<Node> retired = tester.nodeRepository().nodes().list().owner(application).retired().asList();
assertFalse("At least one node is retired", retired.isEmpty());
tester.nodeRepository().nodes().setRemovable(application, retired);
// Redeploy to deactivate removable nodes and allocate new ones
- deployer.accept(application);
+ deployApplication(application);
tester.nodeRepository().nodes().list(Node.State.inactive).owner(application)
.forEach(node -> tester.nodeRepository().nodes().removeRecursively(node, true));
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
index 3ad9041cdbb..f2ca993f4d7 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
@@ -338,7 +338,8 @@ public class DockerProvisioningTest {
tester.makeReadyHosts(2, hostFlavor.resources()).activateTenantHosts();
ApplicationId app1 = ProvisioningTester.applicationId("app1");
- ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build();
+ ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content,
+ new ClusterSpec.Id("cluster1")).vespaVersion("7").build();
// 5 Gb requested memory becomes 5-3=2 Gb real memory, which is an illegally small amount
var resources = new NodeResources(1, 5, 10, 1);
@@ -346,7 +347,34 @@ public class DockerProvisioningTest {
new ClusterResources(4, 1, resources)));
}
catch (IllegalArgumentException e) {
- assertEquals("No allocation possible within limits: from 2 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps] to 4 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps]",
+ assertEquals("No allocation possible within limits: " +
+ "from 2 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps] " +
+ "to 4 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps]",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void exclusive_resources_not_matching_host_causes_failure() {
+ try {
+ Flavor hostFlavor1 = new Flavor(new NodeResources(20, 40, 100, 4));
+ Flavor hostFlavor2 = new Flavor(new NodeResources(30, 40, 100, 4));
+ ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east")))
+ .flavors(List.of(hostFlavor1, hostFlavor2))
+ .build();
+ ApplicationId app1 = ProvisioningTester.applicationId("app1");
+ ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content,
+ new ClusterSpec.Id("cluster1")).exclusive(true).vespaVersion("7").build();
+
+ var resources = new NodeResources(20, 37, 100, 1);
+ tester.activate(app1, cluster1, Capacity.from(new ClusterResources(2, 1, resources),
+ new ClusterResources(4, 1, resources)));
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("No allocation possible within limits: " +
+ "from 2 nodes with [vcpu: 20.0, memory: 37.0 Gb, disk 100.0 Gb, bandwidth: 1.0 Gbps] " +
+ "to 4 nodes with [vcpu: 20.0, memory: 37.0 Gb, disk 100.0 Gb, bandwidth: 1.0 Gbps]. " +
+ "Nearest allowed node resources: [vcpu: 20.0, memory: 40.0 Gb, disk 100.0 Gb, bandwidth: 1.0 Gbps, storage type: remote]",
e.getMessage());
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
index d152cbaf5db..a28c11d009f 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
@@ -500,7 +500,7 @@ public class NodesV2ApiTest {
// Attempt to DELETE allocated node
tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/host4.yahoo.com",
new byte[0], Request.Method.DELETE),
- 400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"active child node host4.yahoo.com allocated to tenant3.application3.instance3 as 'content/id3/0/0/stateful' is currently allocated and cannot be removed\"}");
+ 400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"active child node host4.yahoo.com allocated to tenant3.application3.instance3 as 'content/id3/0/0/stateful' is currently allocated and cannot be removed while in active\"}");
// PUT current restart generation with string instead of long
tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/host4.yahoo.com",
diff --git a/parent/pom.xml b/parent/pom.xml
index 25851a9553e..6f1d6f23f51 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -853,7 +853,7 @@
<antlr4.version>4.5</antlr4.version>
<apache.httpclient.version>4.5.12</apache.httpclient.version>
<apache.httpcore.version>4.4.13</apache.httpcore.version>
- <apache.httpclient5.version>5.0.3</apache.httpclient5.version>
+ <apache.httpclient5.version>5.1</apache.httpclient5.version>
<asm.version>9.1</asm.version>
<!-- Athenz dependencies. Make sure these dependencies match those in Vespa's internal repositories -->
<athenz.version>1.10.14</athenz.version>
@@ -890,7 +890,7 @@
<spifly.version>1.3.3</spifly.version>
<surefire.version>2.22.0</surefire.version>
<tensorflow.version>1.12.0</tensorflow.version>
- <zookeeper.client.version>3.6.2</zookeeper.client.version>
+ <zookeeper.client.version>3.6.3</zookeeper.client.version>
<doclint>all</doclint>
<test.hide>true</test.hide>
diff --git a/pom.xml b/pom.xml
index c77842a149d..82d9efbb70e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -133,6 +133,7 @@
<module>vespa-athenz</module>
<module>vespa-documentgen-plugin</module>
<module>vespa-feed-client</module>
+ <module>vespa-feed-client-cli</module>
<module>vespa-hadoop</module>
<module>vespa-http-client</module>
<module>vespa-maven-plugin</module>
diff --git a/searchcore/src/apps/tests/persistenceconformance_test.cpp b/searchcore/src/apps/tests/persistenceconformance_test.cpp
index 69d509c25fd..24db810b9df 100644
--- a/searchcore/src/apps/tests/persistenceconformance_test.cpp
+++ b/searchcore/src/apps/tests/persistenceconformance_test.cpp
@@ -133,6 +133,7 @@ public:
1,
std::make_shared<RankProfilesConfig>(),
std::make_shared<matching::RankingConstants>(),
+ std::make_shared<matching::RankingExpressions>(),
std::make_shared<matching::OnnxModels>(),
indexschema,
attributes,
diff --git a/searchcore/src/apps/verify_ranksetup/verify_ranksetup.cpp b/searchcore/src/apps/verify_ranksetup/verify_ranksetup.cpp
index 559e8fd14cd..853395be3e1 100644
--- a/searchcore/src/apps/verify_ranksetup/verify_ranksetup.cpp
+++ b/searchcore/src/apps/verify_ranksetup/verify_ranksetup.cpp
@@ -15,8 +15,10 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/searchcommon/common/schemaconfigurer.h>
#include <vespa/searchcore/config/config-ranking-constants.h>
+#include <vespa/searchcore/config/config-ranking-expressions.h>
#include <vespa/searchcore/config/config-onnx-models.h>
#include <vespa/searchcore/proton/matching/indexenvironment.h>
+#include <vespa/searchcore/proton/matching/ranking_expressions.h>
#include <vespa/searchlib/features/setup.h>
#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/fef/test/plugin/setup.h>
@@ -33,11 +35,13 @@ using config::ConfigSubscriber;
using config::IConfigContext;
using config::InvalidConfigException;
using proton::matching::IConstantValueRepo;
+using proton::matching::RankingExpressions;
using proton::matching::OnnxModels;
using vespa::config::search::AttributesConfig;
using vespa::config::search::IndexschemaConfig;
using vespa::config::search::RankProfilesConfig;
using vespa::config::search::core::RankingConstantsConfig;
+using vespa::config::search::core::RankingExpressionsConfig;
using vespa::config::search::core::OnnxModelsConfig;
using vespa::config::search::core::VerifyRanksetupConfig;
using vespalib::eval::BadConstantValue;
@@ -57,6 +61,19 @@ std::optional<vespalib::string> get_file(const vespalib::string &ref, const Veri
return std::nullopt;
}
+RankingExpressions make_expressions(const RankingExpressionsConfig &expressionsCfg, const VerifyRanksetupConfig &myCfg) {
+ RankingExpressions expressions;
+ for (const auto &entry: expressionsCfg.expression) {
+ if (auto file = get_file(entry.fileref, myCfg)) {
+ expressions.add(entry.name, file.value());
+ } else {
+ LOG(warning, "could not find file for ranking expression '%s' (ref:'%s')",
+ entry.name.c_str(), entry.fileref.c_str());
+ }
+ }
+ return expressions;
+}
+
OnnxModels make_models(const OnnxModelsConfig &modelsCfg, const VerifyRanksetupConfig &myCfg) {
OnnxModels::Vector model_list;
for (const auto &entry: modelsCfg.model) {
@@ -64,7 +81,7 @@ OnnxModels make_models(const OnnxModelsConfig &modelsCfg, const VerifyRanksetupC
model_list.emplace_back(entry.name, file.value());
OnnxModels::configure(entry, model_list.back());
} else {
- LOG(warning, "could not find file for onnx model '%s' (ref:'%s')\n",
+ LOG(warning, "could not find file for onnx model '%s' (ref:'%s')",
entry.name.c_str(), entry.fileref.c_str());
}
}
@@ -77,13 +94,15 @@ public:
bool verify(const search::index::Schema &schema,
const search::fef::Properties &props,
const IConstantValueRepo &repo,
- OnnxModels models);
+ const RankingExpressions &expressions,
+ const OnnxModels &models);
bool verifyConfig(const VerifyRanksetupConfig &myCfg,
const RankProfilesConfig &rankCfg,
const IndexschemaConfig &schemaCfg,
const AttributesConfig &attributeCfg,
const RankingConstantsConfig &constantsCfg,
+ const RankingExpressionsConfig &expressionsCfg,
const OnnxModelsConfig &modelsCfg);
int usage();
@@ -112,9 +131,10 @@ bool
App::verify(const search::index::Schema &schema,
const search::fef::Properties &props,
const IConstantValueRepo &repo,
- OnnxModels models)
+ const RankingExpressions &expressions,
+ const OnnxModels &models)
{
- proton::matching::IndexEnvironment indexEnv(0, schema, props, repo, models);
+ proton::matching::IndexEnvironment indexEnv(0, schema, props, repo, expressions, models);
search::fef::BlueprintFactory factory;
search::features::setup_search_features(factory);
search::fef::test::setup_fef_test_plugin(factory);
@@ -143,6 +163,7 @@ App::verifyConfig(const VerifyRanksetupConfig &myCfg,
const IndexschemaConfig &schemaCfg,
const AttributesConfig &attributeCfg,
const RankingConstantsConfig &constantsCfg,
+ const RankingExpressionsConfig &expressionsCfg,
const OnnxModelsConfig &modelsCfg)
{
bool ok = true;
@@ -150,6 +171,7 @@ App::verifyConfig(const VerifyRanksetupConfig &myCfg,
search::index::SchemaBuilder::build(schemaCfg, schema);
search::index::SchemaBuilder::build(attributeCfg, schema);
DummyConstantValueRepo repo(constantsCfg);
+ auto expressions = make_expressions(expressionsCfg, myCfg);
auto models = make_models(modelsCfg, myCfg);
for(size_t i = 0; i < rankCfg.rankprofile.size(); i++) {
search::fef::Properties properties;
@@ -158,7 +180,7 @@ App::verifyConfig(const VerifyRanksetupConfig &myCfg,
properties.add(profile.fef.property[j].name,
profile.fef.property[j].value);
}
- if (verify(schema, properties, repo, models)) {
+ if (verify(schema, properties, repo, expressions, models)) {
LOG(info, "rank profile '%s': pass", profile.name.c_str());
} else {
LOG(error, "rank profile '%s': FAIL", profile.name.c_str());
@@ -196,6 +218,7 @@ App::Main()
ConfigHandle<AttributesConfig>::UP attributesHandle = subscriber.subscribe<AttributesConfig>(cfgId);
ConfigHandle<IndexschemaConfig>::UP schemaHandle = subscriber.subscribe<IndexschemaConfig>(cfgId);
ConfigHandle<RankingConstantsConfig>::UP constantsHandle = subscriber.subscribe<RankingConstantsConfig>(cfgId);
+ ConfigHandle<RankingExpressionsConfig>::UP expressionsHandle = subscriber.subscribe<RankingExpressionsConfig>(cfgId);
ConfigHandle<OnnxModelsConfig>::UP modelsHandle = subscriber.subscribe<OnnxModelsConfig>(cfgId);
subscriber.nextConfig();
@@ -204,6 +227,7 @@ App::Main()
*schemaHandle->getConfig(),
*attributesHandle->getConfig(),
*constantsHandle->getConfig(),
+ *expressionsHandle->getConfig(),
*modelsHandle->getConfig());
} catch (ConfigRuntimeException & e) {
LOG(error, "Unable to subscribe to config: %s", e.getMessage().c_str());
diff --git a/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp b/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp
index 6e346bcfa60..e4dd4715ed1 100644
--- a/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp
+++ b/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp
@@ -187,6 +187,7 @@ std::shared_ptr<DocumentDBConfig> make_document_db_config(std::shared_ptr<Docume
1,
std::make_shared<RankProfilesConfig>(),
std::make_shared<matching::RankingConstants>(),
+ std::make_shared<matching::RankingExpressions>(),
std::make_shared<matching::OnnxModels>(),
indexschema,
attributes,
diff --git a/searchcore/src/apps/vespa-proton-cmd/vespa-proton-cmd.cpp b/searchcore/src/apps/vespa-proton-cmd/vespa-proton-cmd.cpp
index 8b6203c78a2..50a2aecce76 100644
--- a/searchcore/src/apps/vespa-proton-cmd/vespa-proton-cmd.cpp
+++ b/searchcore/src/apps/vespa-proton-cmd/vespa-proton-cmd.cpp
@@ -3,6 +3,7 @@
#include <vespa/slobrok/sbmirror.h>
#include <vespa/config/common/configsystem.h>
#include <vespa/config/common/exceptions.h>
+#include <vespa/vespalib/util/exceptions.h>
#include <vespa/fnet/frt/supervisor.h>
#include <vespa/fnet/frt/target.h>
#include <vespa/vespalib/util/host_name.h>
@@ -158,6 +159,9 @@ public:
} catch (config::InvalidConfigException& e) {
fprintf(stderr, "ERROR: failed to get service location broker configuration\n");
std::_Exit(1);
+ } catch (vespalib::IllegalStateException& e) {
+ fprintf(stderr, "ERROR: empty or invalid service location broker configuration: %s\n", e.what());
+ std::_Exit(2);
}
return "";
}
diff --git a/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp b/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
index 7ae828241c8..3f65f61cbaa 100644
--- a/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
@@ -643,6 +643,7 @@ TEST("require that maintenance controller should change if some config has chang
TEST_DO(assertMaintenanceControllerShouldChange(CCR().setRankProfilesChanged(true)));
TEST_DO(assertMaintenanceControllerShouldChange(CCR().setRankingConstantsChanged(true)));
+ TEST_DO(assertMaintenanceControllerShouldChange(CCR().setRankingExpressionsChanged(true)));
TEST_DO(assertMaintenanceControllerShouldChange(CCR().setOnnxModelsChanged(true)));
TEST_DO(assertMaintenanceControllerShouldChange(CCR().setIndexschemaChanged(true)));
TEST_DO(assertMaintenanceControllerShouldChange(CCR().setAttributesChanged(true)));
@@ -690,6 +691,7 @@ TEST("require that subdbs should change if relevant config changed")
TEST_DO(assertSubDbsShouldChange(CCR().setVisibilityDelayChanged(true)));
TEST_DO(assertSubDbsShouldChange(CCR().setRankProfilesChanged(true)));
TEST_DO(assertSubDbsShouldChange(CCR().setRankingConstantsChanged(true)));
+ TEST_DO(assertSubDbsShouldChange(CCR().setRankingExpressionsChanged(true)));
TEST_DO(assertSubDbsShouldChange(CCR().setOnnxModelsChanged(true)));
TEST_DO(assertSubDbsShouldChange(CCR().setSchemaChanged(true)));
TEST_DO(assertSubDbsShouldChange(CCR().set_alloc_config_changed(true)));
diff --git a/searchcore/src/tests/proton/documentdb/documentdbconfig/documentdbconfig_test.cpp b/searchcore/src/tests/proton/documentdb/documentdbconfig/documentdbconfig_test.cpp
index aed01ca0192..544ff853384 100644
--- a/searchcore/src/tests/proton/documentdb/documentdbconfig/documentdbconfig_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/documentdbconfig/documentdbconfig_test.cpp
@@ -17,6 +17,7 @@ using namespace search::index;
using namespace search;
using namespace vespa::config::search;
using proton::matching::RankingConstants;
+using proton::matching::RankingExpressions;
using proton::matching::OnnxModels;
using std::make_shared;
using std::shared_ptr;
@@ -69,6 +70,11 @@ public:
_builder.rankingConstants(make_shared<RankingConstants>(constants));
return *this;
}
+ MyConfigBuilder &addRankingExpression() {
+ auto expr_list = RankingExpressions().add("my_expr", "my_file");
+ _builder.rankingExpressions(make_shared<RankingExpressions>(expr_list));
+ return *this;
+ }
MyConfigBuilder &addOnnxModel() {
OnnxModels::Vector models = {{"my_model_name", "my_model_file"}};
_builder.onnxModels(make_shared<OnnxModels>(models));
@@ -138,6 +144,7 @@ struct Fixture {
fullCfg = MyConfigBuilder(4, schema, repo).addAttribute().
addRankProfile().
addRankingConstant().
+ addRankingExpression().
addOnnxModel().
addImportedField().
addSummary(true).
@@ -173,6 +180,7 @@ struct DelayAttributeAspectFixture {
attrCfg = MyConfigBuilder(4, schema, makeDocTypeRepo(true)).addAttribute().
addRankProfile().
addRankingConstant().
+ addRankingExpression().
addOnnxModel().
addImportedField().
addSummary(true).
@@ -180,6 +188,7 @@ struct DelayAttributeAspectFixture {
build();
noAttrCfg = MyConfigBuilder(4, schema, makeDocTypeRepo(hasDocField)).addRankProfile().
addRankingConstant().
+ addRankingExpression().
addOnnxModel().
addImportedField().
addSummary(hasDocField).
diff --git a/searchcore/src/tests/proton/documentdb/fileconfigmanager/fileconfigmanager_test.cpp b/searchcore/src/tests/proton/documentdb/fileconfigmanager/fileconfigmanager_test.cpp
index e6bcbf18495..4ac66e464e2 100644
--- a/searchcore/src/tests/proton/documentdb/fileconfigmanager/fileconfigmanager_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/fileconfigmanager/fileconfigmanager_test.cpp
@@ -28,6 +28,7 @@ using namespace vespa::config::search;
using namespace std::chrono_literals;
using vespa::config::content::core::BucketspacesConfig;
using proton::matching::RankingConstants;
+using proton::matching::RankingExpressions;
using proton::matching::OnnxModels;
typedef DocumentDBConfigHelper DBCM;
@@ -76,8 +77,10 @@ assertEqualSnapshot(const DocumentDBConfig &exp, const DocumentDBConfig &act)
{
EXPECT_TRUE(exp.getRankProfilesConfig() == act.getRankProfilesConfig());
EXPECT_TRUE(exp.getRankingConstants() == act.getRankingConstants());
+ EXPECT_TRUE(exp.getRankingExpressions() == act.getRankingExpressions());
EXPECT_TRUE(exp.getOnnxModels() == act.getOnnxModels());
EXPECT_EQUAL(0u, exp.getRankingConstants().size());
+ EXPECT_EQUAL(0u, exp.getRankingExpressions().size());
EXPECT_EQUAL(0u, exp.getOnnxModels().size());
EXPECT_TRUE(exp.getIndexschemaConfig() == act.getIndexschemaConfig());
EXPECT_TRUE(exp.getAttributesConfig() == act.getAttributesConfig());
@@ -108,6 +111,9 @@ addConfigsThatAreNotSavedToDisk(const DocumentDBConfig &cfg)
RankingConstants::Vector constants = {{"my_name", "my_type", "my_path"}};
builder.rankingConstants(std::make_shared<RankingConstants>(constants));
+ auto expr_list = RankingExpressions().add("my_expr", "my_file");
+ builder.rankingExpressions(std::make_shared<RankingExpressions>(expr_list));
+
OnnxModels::Vector models = {{"my_model_name", "my_model_file"}};
builder.onnxModels(std::make_shared<OnnxModels>(models));
diff --git a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
index 67a7ceeae34..59bb33f51ce 100644
--- a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
@@ -908,7 +908,6 @@ TEST_F("require that document pruner is active", MaintenanceControllerFixture)
f._builder.createDocs(3, 6, 8); // 2 docs
f._builder.createDocs(4, 8, 11); // 3 docs
test::UserDocuments removeDocs(f._builder.getDocs());
- BucketId bucketId3(removeDocs.getBucket(3));
f.removeDocs(removeDocs, remTime);
f.notifyClusterStateChanged();
EXPECT_TRUE(f._executor.isIdle());
diff --git a/searchcore/src/tests/proton/matching/index_environment/.gitattributes b/searchcore/src/tests/proton/matching/index_environment/.gitattributes
new file mode 100644
index 00000000000..c7aff0d0972
--- /dev/null
+++ b/searchcore/src/tests/proton/matching/index_environment/.gitattributes
@@ -0,0 +1 @@
+/my_expr.lz4 binary
diff --git a/searchcore/src/tests/proton/matching/index_environment/index_environment_test.cpp b/searchcore/src/tests/proton/matching/index_environment/index_environment_test.cpp
index 421ebffafa4..20b9e1206c7 100644
--- a/searchcore/src/tests/proton/matching/index_environment/index_environment_test.cpp
+++ b/searchcore/src/tests/proton/matching/index_environment/index_environment_test.cpp
@@ -3,6 +3,7 @@
#include <vespa/eval/eval/value_cache/constant_value.h>
#include <vespa/searchcore/proton/matching/indexenvironment.h>
+#include <vespa/searchcore/proton/matching/ranking_expressions.h>
using namespace proton::matching;
using search::fef::FieldInfo;
@@ -15,6 +16,21 @@ using search::index::schema::DataType;
using vespalib::eval::ConstantValue;
using SIAF = Schema::ImportedAttributeField;
+const vespalib::string my_expr_ref(
+ "this is my reference ranking expression.\n"
+ "this is my reference ranking expression.\n"
+ "it will not compile into a function.\n"
+ "it will not compile into a function.\n"
+ "it is just some text, that can also be compressed...\n"
+ "it is just some text, that can also be compressed...\n");
+
+RankingExpressions make_expressions() {
+ RankingExpressions expr_list;
+ expr_list.add("expr1", TEST_PATH("my_expr"));
+ expr_list.add("expr2", TEST_PATH("my_expr.lz4"));
+ return expr_list;
+}
+
OnnxModels make_models() {
OnnxModels::Vector list;
list.emplace_back(OnnxModel("model1", "path1").input_feature("input1","feature1").output_name("output1", "out1"));
@@ -50,7 +66,7 @@ struct Fixture {
Fixture(Schema::UP schema_)
: repo(),
schema(std::move(schema_)),
- env(7, *schema, Properties(), repo, make_models())
+ env(7, *schema, Properties(), repo, make_expressions(), make_models())
{
}
const FieldInfo *assertField(size_t idx,
@@ -123,4 +139,13 @@ TEST_F("require that onnx model config can be obtained", Fixture(buildEmptySchem
EXPECT_TRUE(f1.env.getOnnxModel("model3") == nullptr);
}
+TEST_F("require that external ranking expressions can be obtained", Fixture(buildEmptySchema())) {
+ auto expr1 = f1.env.getRankingExpression("expr1");
+ auto expr2 = f1.env.getRankingExpression("expr2");
+ auto expr3 = f1.env.getRankingExpression("expr3");
+ EXPECT_EQUAL(expr1, my_expr_ref);
+ EXPECT_EQUAL(expr2, my_expr_ref);
+ EXPECT_TRUE(expr3.empty());
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchcore/src/tests/proton/matching/index_environment/my_expr b/searchcore/src/tests/proton/matching/index_environment/my_expr
new file mode 100644
index 00000000000..2fdd1e4c9ff
--- /dev/null
+++ b/searchcore/src/tests/proton/matching/index_environment/my_expr
@@ -0,0 +1,6 @@
+this is my reference ranking expression.
+this is my reference ranking expression.
+it will not compile into a function.
+it will not compile into a function.
+it is just some text, that can also be compressed...
+it is just some text, that can also be compressed...
diff --git a/searchcore/src/tests/proton/matching/index_environment/my_expr.lz4 b/searchcore/src/tests/proton/matching/index_environment/my_expr.lz4
new file mode 100644
index 00000000000..c88594ce81d
--- /dev/null
+++ b/searchcore/src/tests/proton/matching/index_environment/my_expr.lz4
Binary files differ
diff --git a/searchcore/src/tests/proton/matching/matching_test.cpp b/searchcore/src/tests/proton/matching/matching_test.cpp
index 8057dbf2da6..2bee53a5215 100644
--- a/searchcore/src/tests/proton/matching/matching_test.cpp
+++ b/searchcore/src/tests/proton/matching/matching_test.cpp
@@ -280,7 +280,7 @@ struct MyWorld {
}
Matcher::SP createMatcher() {
- return std::make_shared<Matcher>(schema, config, clock, queryLimiter, constantValueRepo, OnnxModels(), 0);
+ return std::make_shared<Matcher>(schema, config, clock, queryLimiter, constantValueRepo, RankingExpressions(), OnnxModels(), 0);
}
struct MySearchHandler : ISearchHandler {
diff --git a/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp b/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp
index e42f8323473..20a10aee678 100644
--- a/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp
+++ b/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp
@@ -10,6 +10,7 @@
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/proton/common/subdbtype.h>
#include <vespa/searchcore/config/config-ranking-constants.h>
+#include <vespa/searchcore/config/config-ranking-expressions.h>
#include <vespa/searchcore/config/config-onnx-models.h>
#include <vespa/searchsummary/config/config-juniperrc.h>
#include <vespa/document/repo/documenttyperepo.h>
@@ -52,6 +53,7 @@ struct DoctypeFixture {
AttributesConfigBuilder attributesBuilder;
RankProfilesConfigBuilder rankProfilesBuilder;
RankingConstantsConfigBuilder rankingConstantsBuilder;
+ RankingExpressionsConfigBuilder rankingExpressionsBuilder;
OnnxModelsConfigBuilder onnxModelsBuilder;
IndexschemaConfigBuilder indexschemaBuilder;
SummaryConfigBuilder summaryBuilder;
@@ -108,6 +110,7 @@ struct ConfigTestFixture {
set.addBuilder(db.configid, &fixture->attributesBuilder);
set.addBuilder(db.configid, &fixture->rankProfilesBuilder);
set.addBuilder(db.configid, &fixture->rankingConstantsBuilder);
+ set.addBuilder(db.configid, &fixture->rankingExpressionsBuilder);
set.addBuilder(db.configid, &fixture->onnxModelsBuilder);
set.addBuilder(db.configid, &fixture->indexschemaBuilder);
set.addBuilder(db.configid, &fixture->summaryBuilder);
@@ -262,7 +265,7 @@ TEST_FF("require that documentdb config manager subscribes for config",
DocumentDBConfigManager(f1.configId + "/typea", "typea")) {
f1.addDocType("typea");
const ConfigKeySet keySet(f2.createConfigKeySet());
- ASSERT_EQUAL(9u, keySet.size());
+ ASSERT_EQUAL(10u, keySet.size());
ASSERT_TRUE(f1.configEqual("typea", getDocumentDBConfig(f1, f2)));
}
diff --git a/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp b/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
index 16e2fc4b1a8..faf09be3bbd 100644
--- a/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
+++ b/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
@@ -48,14 +48,13 @@ using std::map;
using search::index::Schema;
using search::index::SchemaBuilder;
using proton::matching::RankingConstants;
+using proton::matching::RankingExpressions;
using proton::matching::OnnxModels;
struct DBConfigFixture {
using UP = std::unique_ptr<DBConfigFixture>;
AttributesConfigBuilder _attributesBuilder;
RankProfilesConfigBuilder _rankProfilesBuilder;
- RankingConstantsConfigBuilder _rankingConstantsBuilder;
- OnnxModelsConfigBuilder _onnxModelsBuilder;
IndexschemaConfigBuilder _indexschemaBuilder;
SummaryConfigBuilder _summaryBuilder;
SummarymapConfigBuilder _summarymapBuilder;
@@ -76,6 +75,11 @@ struct DBConfigFixture {
return std::make_shared<RankingConstants>();
}
+ RankingExpressions::SP buildRankingExpressions()
+ {
+ return std::make_shared<RankingExpressions>();
+ }
+
OnnxModels::SP buildOnnxModels()
{
return std::make_shared<OnnxModels>();
@@ -91,6 +95,7 @@ struct DBConfigFixture {
(generation,
std::make_shared<RankProfilesConfig>(_rankProfilesBuilder),
buildRankingConstants(),
+ buildRankingExpressions(),
buildOnnxModels(),
std::make_shared<IndexschemaConfig>(_indexschemaBuilder),
std::make_shared<AttributesConfig>(_attributesBuilder),
diff --git a/searchcore/src/tests/proton/verify_ranksetup/bad_ranking_expression b/searchcore/src/tests/proton/verify_ranksetup/bad_ranking_expression
new file mode 100644
index 00000000000..475b311dc84
--- /dev/null
+++ b/searchcore/src/tests/proton/verify_ranksetup/bad_ranking_expression
@@ -0,0 +1 @@
+join(constant(my_tensor),attribute(date),f(t,d)(join(t,d,f(x,y)(x+y))))
diff --git a/searchcore/src/tests/proton/verify_ranksetup/good_ranking_expression b/searchcore/src/tests/proton/verify_ranksetup/good_ranking_expression
new file mode 100644
index 00000000000..f6712be7a82
--- /dev/null
+++ b/searchcore/src/tests/proton/verify_ranksetup/good_ranking_expression
@@ -0,0 +1 @@
+join(constant(my_tensor),attribute(date),f(t,d)(t+d))
diff --git a/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp b/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp
index c46990732b7..31557f13a54 100644
--- a/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp
+++ b/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp
@@ -71,6 +71,7 @@ struct Setup {
std::map<std::string,std::string> properties;
std::map<std::string,std::string> constants;
std::vector<bool> extra_profiles;
+ std::map<std::string,std::string> ranking_expressions;
std::map<std::string,OnnxModel> onnx_models;
Setup();
~Setup();
@@ -96,6 +97,9 @@ struct Setup {
void rank_expr(const std::string &name, const std::string &expr) {
property(fmt("rankingExpression(%s).rankingScript", name.c_str()), expr);
}
+ void ext_rank_expr(const std::string &name, const std::string &file) {
+ ranking_expressions.insert_or_assign(name, TEST_PATH(file));
+ }
void first_phase(const std::string &feature) {
property(rank::FirstPhase::NAME, feature);
}
@@ -157,6 +161,14 @@ struct Setup {
++idx;
}
}
+ void write_ranking_expressions(const Writer &out) {
+ size_t idx = 0;
+ for (const auto &entry: ranking_expressions) {
+ out.fmt("expression[%zu].name \"%s\"\n", idx, entry.first.c_str());
+ out.fmt("expression[%zu].fileref \"expr_ref_%zu\"\n", idx, idx);
+ ++idx;
+ }
+ }
void write_onnx_models(const Writer &out) {
size_t idx = 0;
for (const auto &entry: onnx_models) {
@@ -179,6 +191,12 @@ struct Setup {
}
void write_self_cfg(const Writer &out) {
size_t idx = 0;
+ for (const auto &entry: ranking_expressions) {
+ out.fmt("file[%zu].ref \"expr_ref_%zu\"\n", idx, idx);
+ out.fmt("file[%zu].path \"%s\"\n", idx, entry.second.c_str());
+ ++idx;
+ }
+ idx = 0;
for (const auto &entry: onnx_models) {
out.fmt("file[%zu].ref \"onnx_ref_%zu\"\n", idx, idx);
out.fmt("file[%zu].path \"%s\"\n", idx, entry.second.file_path().c_str());
@@ -190,6 +208,7 @@ struct Setup {
write_indexschema(Writer(gen_dir + "/indexschema.cfg"));
write_rank_profiles(Writer(gen_dir + "/rank-profiles.cfg"));
write_ranking_constants(Writer(gen_dir + "/ranking-constants.cfg"));
+ write_ranking_expressions(Writer(gen_dir + "/ranking-expressions.cfg"));
write_onnx_models(Writer(gen_dir + "/onnx-models.cfg"));
write_self_cfg(Writer(gen_dir + "/verify-ranksetup.cfg"));
}
@@ -358,6 +377,23 @@ TEST_F("require that imported attribute field can be used by rank feature", Simp
//-----------------------------------------------------------------------------
+TEST_F("require that external ranking expression can be verified", SimpleSetup()) {
+ f.ext_rank_expr("my_expr", "good_ranking_expression");
+ f.verify_valid({"rankingExpression(my_expr)"});
+}
+
+TEST_F("require that external ranking expression can fail verification", SimpleSetup()) {
+ f.ext_rank_expr("my_expr", "bad_ranking_expression");
+ f.verify_invalid({"rankingExpression(my_expr)"});
+}
+
+TEST_F("require that missing expression file fails verification", SimpleSetup()) {
+ f.ext_rank_expr("my_expr", "missing_ranking_expression_file");
+ f.verify_invalid({"rankingExpression(my_expr)"});
+}
+
+//-----------------------------------------------------------------------------
+
TEST_F("require that onnx model can be verified", OnnxSetup()) {
f.rank_expr("query_tensor", "tensor<float>(a[1],b[4]):[[1,2,3,4]]");
f.rank_expr("attribute_tensor", "tensor<float>(a[4],b[1]):[[5],[6],[7],[8]]");
diff --git a/searchcore/src/vespa/searchcore/proton/matching/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/matching/CMakeLists.txt
index a4688b5fdca..d83d8ed91d9 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/matching/CMakeLists.txt
@@ -27,6 +27,7 @@ vespa_add_library(searchcore_matching STATIC
querylimiter.cpp
querynodes.cpp
ranking_constants.cpp
+ ranking_expressions.cpp
requestcontext.cpp
resolveviewvisitor.cpp
result_processor.cpp
diff --git a/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.cpp b/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.cpp
index 013f359c4f9..5f5cc4dcf76 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.cpp
@@ -65,15 +65,17 @@ IndexEnvironment::IndexEnvironment(uint32_t distributionKey,
const search::index::Schema &schema,
const search::fef::Properties &props,
const IConstantValueRepo &constantValueRepo,
+ RankingExpressions rankingExpressions,
OnnxModels onnxModels)
- : _tableManager(),
- _properties(props),
- _fieldNames(),
- _fields(),
- _motivation(UNKNOWN),
- _constantValueRepo(constantValueRepo),
- _onnxModels(std::move(onnxModels)),
- _distributionKey(distributionKey)
+ : _tableManager(),
+ _properties(props),
+ _fieldNames(),
+ _fields(),
+ _motivation(UNKNOWN),
+ _constantValueRepo(constantValueRepo),
+ _rankingExpressions(std::move(rankingExpressions)),
+ _onnxModels(std::move(onnxModels)),
+ _distributionKey(distributionKey)
{
_tableManager.addFactory(std::make_shared<search::fef::FunctionTableFactory>(256));
extractFields(schema);
@@ -131,6 +133,12 @@ IndexEnvironment::hintFieldAccess(uint32_t ) const { }
void
IndexEnvironment::hintAttributeAccess(const string &) const { }
+vespalib::string
+IndexEnvironment::getRankingExpression(const vespalib::string &name) const
+{
+ return _rankingExpressions.loadExpression(name);
+}
+
const search::fef::OnnxModel *
IndexEnvironment::getOnnxModel(const vespalib::string &name) const
{
diff --git a/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.h b/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.h
index ad51eb17b4d..d270825d3f7 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/indexenvironment.h
@@ -3,6 +3,7 @@
#pragma once
#include "onnx_models.h"
+#include "ranking_expressions.h"
#include "i_constant_value_repo.h"
#include <vespa/searchlib/fef/fieldinfo.h>
#include <vespa/searchlib/fef/iindexenvironment.h>
@@ -26,6 +27,7 @@ private:
std::vector<search::fef::FieldInfo> _fields;
mutable FeatureMotivation _motivation;
const IConstantValueRepo &_constantValueRepo;
+ RankingExpressions _rankingExpressions;
OnnxModels _onnxModels;
uint32_t _distributionKey;
@@ -46,12 +48,14 @@ public:
* @param schema the index schema
* @param props config
* @param constantValueRepo repo used to access constant values for ranking
+ * @param rankingExpressions processed config about ranking expressions
* @param onnxModels processed config about onnx models
**/
IndexEnvironment(uint32_t distributionKey,
const search::index::Schema &schema,
const search::fef::Properties &props,
const IConstantValueRepo &constantValueRepo,
+ RankingExpressions rankingExpressions,
OnnxModels onnxModels);
const search::fef::Properties &getProperties() const override;
@@ -68,6 +72,7 @@ public:
vespalib::eval::ConstantValue::UP getConstantValue(const vespalib::string &name) const override {
return _constantValueRepo.getConstant(name);
}
+ vespalib::string getRankingExpression(const vespalib::string &name) const override;
const search::fef::OnnxModel *getOnnxModel(const vespalib::string &name) const override;
~IndexEnvironment() override;
diff --git a/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp b/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
index e459a45040b..806753a6660 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
@@ -99,17 +99,18 @@ handleGroupingSession(SessionManager &sessionMgr, GroupingContext & groupingCont
} // namespace proton::matching::<unnamed>
Matcher::Matcher(const search::index::Schema &schema, const Properties &props, const vespalib::Clock &clock,
- QueryLimiter &queryLimiter, const IConstantValueRepo &constantValueRepo, OnnxModels onnxModels, uint32_t distributionKey)
- : _indexEnv(distributionKey, schema, props, constantValueRepo, std::move(onnxModels)),
- _blueprintFactory(),
- _rankSetup(),
- _viewResolver(ViewResolver::createFromSchema(schema)),
- _statsLock(),
- _stats(),
- _startTime(my_clock::now()),
- _clock(clock),
- _queryLimiter(queryLimiter),
- _distributionKey(distributionKey)
+ QueryLimiter &queryLimiter, const IConstantValueRepo &constantValueRepo,
+ RankingExpressions rankingExpressions, OnnxModels onnxModels, uint32_t distributionKey)
+ : _indexEnv(distributionKey, schema, props, constantValueRepo, std::move(rankingExpressions), std::move(onnxModels)),
+ _blueprintFactory(),
+ _rankSetup(),
+ _viewResolver(ViewResolver::createFromSchema(schema)),
+ _statsLock(),
+ _stats(),
+ _startTime(my_clock::now()),
+ _clock(clock),
+ _queryLimiter(queryLimiter),
+ _distributionKey(distributionKey)
{
search::features::setup_search_features(_blueprintFactory);
search::fef::test::setup_fef_test_plugin(_blueprintFactory);
diff --git a/searchcore/src/vespa/searchcore/proton/matching/matcher.h b/searchcore/src/vespa/searchcore/proton/matching/matcher.h
index dcd1bbb2b46..d40ae9b18f1 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/matcher.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/matcher.h
@@ -88,7 +88,8 @@ public:
**/
Matcher(const search::index::Schema &schema, const Properties &props,
const vespalib::Clock &clock, QueryLimiter &queryLimiter,
- const IConstantValueRepo &constantValueRepo, OnnxModels onnxModels,
+ const IConstantValueRepo &constantValueRepo,
+ RankingExpressions rankingExpressions, OnnxModels onnxModels,
uint32_t distributionKey);
const search::fef::IIndexEnvironment &get_index_env() const { return _indexEnv; }
diff --git a/searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.cpp b/searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.cpp
new file mode 100644
index 00000000000..9b74f76aa6e
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.cpp
@@ -0,0 +1,67 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "ranking_expressions.h"
+#include <vespa/vespalib/io/mapped_file_input.h>
+#include <vespa/vespalib/data/lz4_input_decoder.h>
+#include <vespa/vespalib/util/size_literals.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".proton.matching.ranking_expressions");
+
+namespace proton::matching {
+
+namespace {
+
+vespalib::string extract_data(vespalib::Input &input) {
+ vespalib::string result;
+ for (auto chunk = input.obtain(); chunk.size > 0; chunk = input.obtain()) {
+ result.append(vespalib::stringref(chunk.data, chunk.size));
+ input.evict(chunk.size);
+ }
+ return result;
+}
+
+} // unnamed
+
+RankingExpressions::RankingExpressions() = default;
+RankingExpressions::RankingExpressions(RankingExpressions &&rhs) = default;
+RankingExpressions::RankingExpressions(const RankingExpressions &rhs) = default;
+RankingExpressions::~RankingExpressions() = default;
+
+RankingExpressions &
+RankingExpressions::add(const vespalib::string &name, const vespalib::string &path)
+{
+ _expressions.insert_or_assign(name, path);
+ return *this;
+}
+
+vespalib::string
+RankingExpressions::loadExpression(const vespalib::string &name) const
+{
+ auto pos = _expressions.find(name);
+ if (pos == _expressions.end()) {
+ // not warning about missing expression here since what we
+ // think is a name might be an expression itself.
+ return {};
+ }
+ auto path = pos->second;
+ vespalib::MappedFileInput file(path);
+ if (!file.valid()) {
+ LOG(warning, "could not read file: %s", path.c_str());
+ return {};
+ }
+ if (ends_with(path, ".lz4")) {
+ size_t buffer_size = 64_Ki;
+ vespalib::Lz4InputDecoder lz4_decoder(file, buffer_size);
+ auto result = extract_data(lz4_decoder);
+ if (lz4_decoder.failed()) {
+ LOG(warning, "file contains lz4 errors (%s): %s",
+ lz4_decoder.reason().c_str(), path.c_str());
+ return {};
+ }
+ return result;
+ }
+ return extract_data(file);
+}
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.h b/searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.h
new file mode 100644
index 00000000000..e1472b03ee0
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/matching/ranking_expressions.h
@@ -0,0 +1,35 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/stllike/string.h>
+#include <memory>
+#include <map>
+
+namespace proton::matching {
+
+/**
+ * Class representing a collection of named ranking expressions
+ * obtained through file-distribution.
+ */
+class RankingExpressions
+{
+private:
+ // expression name -> full_path of expression file
+ std::map<vespalib::string,vespalib::string> _expressions;
+
+public:
+ RankingExpressions();
+ RankingExpressions(RankingExpressions &&rhs);
+ RankingExpressions(const RankingExpressions &rhs);
+ ~RankingExpressions();
+ using SP = std::shared_ptr<RankingExpressions>;
+ bool operator==(const RankingExpressions &rhs) const {
+ return _expressions == rhs._expressions;
+ }
+ size_t size() const { return _expressions.size(); }
+ RankingExpressions &add(const vespalib::string &name, const vespalib::string &path);
+ vespalib::string loadExpression(const vespalib::string &name) const;
+};
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
index 7c487043b5b..e39188042d7 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
@@ -35,6 +35,7 @@ namespace proton {
DocumentDBConfig::ComparisonResult::ComparisonResult()
: rankProfilesChanged(false),
rankingConstantsChanged(false),
+ rankingExpressionsChanged(false),
onnxModelsChanged(false),
indexschemaChanged(false),
attributesChanged(false),
@@ -58,6 +59,7 @@ DocumentDBConfig::DocumentDBConfig(
int64_t generation,
const RankProfilesConfigSP &rankProfiles,
const RankingConstants::SP &rankingConstants,
+ const RankingExpressions::SP &rankingExpressions,
const OnnxModels::SP &onnxModels,
const IndexschemaConfigSP &indexschema,
const AttributesConfigSP &attributes,
@@ -80,6 +82,7 @@ DocumentDBConfig::DocumentDBConfig(
_generation(generation),
_rankProfiles(rankProfiles),
_rankingConstants(rankingConstants),
+ _rankingExpressions(rankingExpressions),
_onnxModels(onnxModels),
_indexschema(indexschema),
_attributes(attributes),
@@ -107,6 +110,7 @@ DocumentDBConfig(const DocumentDBConfig &cfg)
_generation(cfg._generation),
_rankProfiles(cfg._rankProfiles),
_rankingConstants(cfg._rankingConstants),
+ _rankingExpressions(cfg._rankingExpressions),
_onnxModels(cfg._onnxModels),
_indexschema(cfg._indexschema),
_attributes(cfg._attributes),
@@ -133,6 +137,7 @@ DocumentDBConfig::operator==(const DocumentDBConfig & rhs) const
{
return equals<RankProfilesConfig>(_rankProfiles.get(), rhs._rankProfiles.get()) &&
equals<RankingConstants>(_rankingConstants.get(), rhs._rankingConstants.get()) &&
+ equals<RankingExpressions>(_rankingExpressions.get(), rhs._rankingExpressions.get()) &&
equals<OnnxModels>(_onnxModels.get(), rhs._onnxModels.get()) &&
equals<IndexschemaConfig>(_indexschema.get(), rhs._indexschema.get()) &&
equals<AttributesConfig>(_attributes.get(), rhs._attributes.get()) &&
@@ -157,6 +162,7 @@ DocumentDBConfig::compare(const DocumentDBConfig &rhs) const
ComparisonResult retval;
retval.rankProfilesChanged = !equals<RankProfilesConfig>(_rankProfiles.get(), rhs._rankProfiles.get());
retval.rankingConstantsChanged = !equals<RankingConstants>(_rankingConstants.get(), rhs._rankingConstants.get());
+ retval.rankingExpressionsChanged = !equals<RankingExpressions>(_rankingExpressions.get(), rhs._rankingExpressions.get());
retval.onnxModelsChanged = !equals<OnnxModels>(_onnxModels.get(), rhs._onnxModels.get());
retval.indexschemaChanged = !equals<IndexschemaConfig>(_indexschema.get(), rhs._indexschema.get());
retval.attributesChanged = !equals<AttributesConfig>(_attributes.get(), rhs._attributes.get());
@@ -183,6 +189,7 @@ DocumentDBConfig::valid() const
{
return _rankProfiles &&
_rankingConstants &&
+ _rankingExpressions &&
_onnxModels &&
_indexschema &&
_attributes &&
@@ -226,6 +233,7 @@ DocumentDBConfig::makeReplayConfig(const SP & orig)
o._generation,
emptyConfig(o._rankProfiles),
std::make_shared<RankingConstants>(),
+ std::make_shared<RankingExpressions>(),
std::make_shared<OnnxModels>(),
o._indexschema,
o._attributes,
@@ -269,6 +277,7 @@ DocumentDBConfig::newFromAttributesConfig(const AttributesConfigSP &attributes)
_generation,
_rankProfiles,
_rankingConstants,
+ _rankingExpressions,
_onnxModels,
_indexschema,
attributes,
@@ -307,6 +316,7 @@ DocumentDBConfig::makeDelayedAttributeAspectConfig(const SP &newCfg, const Docum
(n._generation,
n._rankProfiles,
n._rankingConstants,
+ n._rankingExpressions,
n._onnxModels,
n._indexschema,
attributeAspectDelayer.getAttributesConfig(),
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h
index 8e24ed8e96a..2ca7fb1dfa8 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h
@@ -6,6 +6,7 @@
#include <vespa/searchlib/common/tunefileinfo.h>
#include <vespa/searchcommon/common/schema.h>
#include <vespa/searchcore/proton/matching/ranking_constants.h>
+#include <vespa/searchcore/proton/matching/ranking_expressions.h>
#include <vespa/searchcore/proton/matching/onnx_models.h>
#include <vespa/config/retriever/configkeyset.h>
#include <vespa/config/retriever/configsnapshot.h>
@@ -40,6 +41,7 @@ public:
public:
bool rankProfilesChanged;
bool rankingConstantsChanged;
+ bool rankingExpressionsChanged;
bool onnxModelsChanged;
bool indexschemaChanged;
bool attributesChanged;
@@ -61,6 +63,7 @@ public:
ComparisonResult();
ComparisonResult &setRankProfilesChanged(bool val) { rankProfilesChanged = val; return *this; }
ComparisonResult &setRankingConstantsChanged(bool val) { rankingConstantsChanged = val; return *this; }
+ ComparisonResult &setRankingExpressionsChanged(bool val) { rankingExpressionsChanged = val; return *this; }
ComparisonResult &setOnnxModelsChanged(bool val) { onnxModelsChanged = val; return *this; }
ComparisonResult &setIndexschemaChanged(bool val) { indexschemaChanged = val; return *this; }
ComparisonResult &setAttributesChanged(bool val) { attributesChanged = val; return *this; }
@@ -101,6 +104,7 @@ public:
using RankProfilesConfig = const vespa::config::search::internal::InternalRankProfilesType;
using RankProfilesConfigSP = std::shared_ptr<RankProfilesConfig>;
using RankingConstants = matching::RankingConstants;
+ using RankingExpressions = matching::RankingExpressions;
using OnnxModels = matching::OnnxModels;
using SummaryConfig = const vespa::config::search::internal::InternalSummaryType;
using SummaryConfigSP = std::shared_ptr<SummaryConfig>;
@@ -120,6 +124,7 @@ private:
int64_t _generation;
RankProfilesConfigSP _rankProfiles;
RankingConstants::SP _rankingConstants;
+ RankingExpressions::SP _rankingExpressions;
OnnxModels::SP _onnxModels;
IndexschemaConfigSP _indexschema;
AttributesConfigSP _attributes;
@@ -159,6 +164,7 @@ public:
DocumentDBConfig(int64_t generation,
const RankProfilesConfigSP &rankProfiles,
const RankingConstants::SP &rankingConstants,
+ const RankingExpressions::SP &rankingExpressions,
const OnnxModels::SP &onnxModels,
const IndexschemaConfigSP &indexschema,
const AttributesConfigSP &attributes,
@@ -189,6 +195,7 @@ public:
const RankProfilesConfig &getRankProfilesConfig() const { return *_rankProfiles; }
const RankingConstants &getRankingConstants() const { return *_rankingConstants; }
+ const RankingExpressions &getRankingExpressions() const { return *_rankingExpressions; }
const OnnxModels &getOnnxModels() const { return *_onnxModels; }
const IndexschemaConfig &getIndexschemaConfig() const { return *_indexschema; }
const AttributesConfig &getAttributesConfig() const { return *_attributes; }
@@ -198,6 +205,7 @@ public:
const DocumenttypesConfig &getDocumenttypesConfig() const { return *_documenttypes; }
const RankProfilesConfigSP &getRankProfilesConfigSP() const { return _rankProfiles; }
const RankingConstants::SP &getRankingConstantsSP() const { return _rankingConstants; }
+ const RankingExpressions::SP &getRankingExpressionsSP() const { return _rankingExpressions; }
const OnnxModels::SP &getOnnxModelsSP() const { return _onnxModels; }
const IndexschemaConfigSP &getIndexschemaConfigSP() const { return _indexschema; }
const AttributesConfigSP &getAttributesConfigSP() const { return _attributes; }
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
index 8d513fde62f..fb154081963 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
@@ -6,6 +6,7 @@
#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/config/config-ranking-constants.h>
+#include <vespa/searchcore/config/config-ranking-expressions.h>
#include <vespa/searchcore/config/config-onnx-models.h>
#include <vespa/config-imported-fields.h>
#include <vespa/config-rank-profiles.h>
@@ -20,6 +21,7 @@
#include <vespa/searchlib/index/schemautil.h>
#include <vespa/searchsummary/config/config-juniperrc.h>
#include <vespa/vespalib/time/time_box.h>
+#include <vespa/vespalib/util/stringfmt.h>
#include <thread>
#include <cassert>
@@ -36,6 +38,7 @@ using search::TuneFileDocumentDB;
using search::index::Schema;
using search::index::SchemaBuilder;
using proton::matching::RankingConstants;
+using proton::matching::RankingExpressions;
using proton::matching::OnnxModels;
using vespalib::compression::CompressionConfig;
using search::LogDocumentStore;
@@ -45,6 +48,8 @@ using search::WriteableFileChunk;
using std::make_shared;
using std::make_unique;
+using vespalib::make_string_short::fmt;
+
namespace proton {
const ConfigKeySet
@@ -53,6 +58,7 @@ DocumentDBConfigManager::createConfigKeySet() const
ConfigKeySet set;
set.add<RankProfilesConfig,
RankingConstantsConfig,
+ RankingExpressionsConfig,
OnnxModelsConfig,
IndexschemaConfig,
AttributesConfig,
@@ -264,6 +270,21 @@ build_alloc_config(const ProtonConfig& proton_config, const vespalib::string& do
distribution_config.redundancy, distribution_config.searchablecopies);
}
+vespalib::string resolve_file(config::RpcFileAcquirer &fileAcquirer, vespalib::TimeBox &timeBox,
+ const vespalib::string &desc, const vespalib::string &fileref)
+{
+ vespalib::string filePath;
+ LOG(info, "Waiting for file acquirer (%s, ref='%s')", desc.c_str(), fileref.c_str());
+ while (timeBox.hasTimeLeft() && (filePath == "")) {
+ filePath = fileAcquirer.wait_for(fileref, timeBox.timeLeft());
+ if (filePath == "") {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ }
+ LOG(info, "Got file path from file acquirer: '%s' (%s, ref='%s')", filePath.c_str(), desc.c_str(), fileref.c_str());
+ return filePath;
+}
+
}
void
@@ -271,6 +292,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
{
using RankProfilesConfigSP = DocumentDBConfig::RankProfilesConfigSP;
using RankingConstantsConfigSP = std::shared_ptr<vespa::config::search::core::RankingConstantsConfig>;
+ using RankingExpressionsConfigSP = std::shared_ptr<vespa::config::search::core::RankingExpressionsConfig>;
using OnnxModelsConfigSP = std::shared_ptr<vespa::config::search::core::OnnxModelsConfig>;
using IndexschemaConfigSP = DocumentDBConfig::IndexschemaConfigSP;
using SummaryConfigSP = DocumentDBConfig::SummaryConfigSP;
@@ -282,6 +304,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
DocumentDBConfig::SP current = _pendingConfigSnapshot;
RankProfilesConfigSP newRankProfilesConfig;
matching::RankingConstants::SP newRankingConstants;
+ matching::RankingExpressions::SP newRankingExpressions;
matching::OnnxModels::SP newOnnxModels;
IndexschemaConfigSP newIndexschemaConfig;
MaintenanceConfigSP oldMaintenanceConfig;
@@ -308,6 +331,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
if (current) {
newRankProfilesConfig = current->getRankProfilesConfigSP();
newRankingConstants = current->getRankingConstantsSP();
+ newRankingExpressions = current->getRankingExpressionsSP();
newOnnxModels = current->getOnnxModelsSP();
newIndexschemaConfig = current->getIndexschemaConfigSP();
oldMaintenanceConfig = current->getMaintenanceConfigSP();
@@ -328,22 +352,29 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
config::RpcFileAcquirer fileAcquirer(spec);
vespalib::TimeBox timeBox(5*60, 5);
for (const RankingConstantsConfig::Constant &rc : newRankingConstantsConfig->constant) {
- vespalib::string filePath;
- LOG(info, "Waiting for file acquirer (name='%s', type='%s', ref='%s')",
- rc.name.c_str(), rc.type.c_str(), rc.fileref.c_str());
- while (timeBox.hasTimeLeft() && (filePath == "")) {
- filePath = fileAcquirer.wait_for(rc.fileref, timeBox.timeLeft());
- if (filePath == "") {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
- }
- LOG(info, "Got file path from file acquirer: '%s' (name='%s', type='%s', ref='%s')",
- filePath.c_str(), rc.name.c_str(), rc.type.c_str(), rc.fileref.c_str());
+ auto desc = fmt("name='%s', type='%s'", rc.name.c_str(), rc.type.c_str());
+ vespalib::string filePath = resolve_file(fileAcquirer, timeBox, desc, rc.fileref);
constants.emplace_back(rc.name, rc.type, filePath);
}
}
newRankingConstants = std::make_shared<RankingConstants>(constants);
}
+ if (snapshot.isChanged<RankingExpressionsConfig>(_configId, currentGeneration)) {
+ RankingExpressionsConfigSP newRankingExpressionsConfig = RankingExpressionsConfigSP(
+ snapshot.getConfig<RankingExpressionsConfig>(_configId));
+ const vespalib::string &spec = _bootstrapConfig->getFiledistributorrpcConfig().connectionspec;
+ RankingExpressions expressions;
+ if (spec != "") {
+ config::RpcFileAcquirer fileAcquirer(spec);
+ vespalib::TimeBox timeBox(5*60, 5);
+ for (const RankingExpressionsConfig::Expression &rc : newRankingExpressionsConfig->expression) {
+ auto desc = fmt("name='%s'", rc.name.c_str());
+ vespalib::string filePath = resolve_file(fileAcquirer, timeBox, desc, rc.fileref);
+ expressions.add(rc.name, filePath);
+ }
+ }
+ newRankingExpressions = std::make_shared<RankingExpressions>(std::move(expressions));
+ }
if (snapshot.isChanged<OnnxModelsConfig>(_configId, currentGeneration)) {
OnnxModelsConfigSP newOnnxModelsConfig = OnnxModelsConfigSP(
snapshot.getConfig<OnnxModelsConfig>(_configId));
@@ -353,17 +384,8 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
config::RpcFileAcquirer fileAcquirer(spec);
vespalib::TimeBox timeBox(5*60, 5);
for (const OnnxModelsConfig::Model &rc : newOnnxModelsConfig->model) {
- vespalib::string filePath;
- LOG(info, "Waiting for file acquirer (name='%s', ref='%s')",
- rc.name.c_str(), rc.fileref.c_str());
- while (timeBox.hasTimeLeft() && (filePath == "")) {
- filePath = fileAcquirer.wait_for(rc.fileref, timeBox.timeLeft());
- if (filePath == "") {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
- }
- LOG(info, "Got file path from file acquirer: '%s' (name='%s', ref='%s')",
- filePath.c_str(), rc.name.c_str(), rc.fileref.c_str());
+ auto desc = fmt("name='%s'", rc.name.c_str());
+ vespalib::string filePath = resolve_file(fileAcquirer, timeBox, desc, rc.fileref);
models.emplace_back(rc.name, filePath);
OnnxModels::configure(rc, models.back());
}
@@ -403,6 +425,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
auto newSnapshot = std::make_shared<DocumentDBConfig>(generation,
newRankProfilesConfig,
newRankingConstants,
+ newRankingExpressions,
newOnnxModels,
newIndexschemaConfig,
filterImportedAttributes(newAttributesConfig),
diff --git a/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp b/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp
index 9f67058a2d7..4b862b40896 100644
--- a/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/fileconfigmanager.cpp
@@ -327,6 +327,7 @@ FileConfigManager::loadConfig(const DocumentDBConfig &currentSnapshot,
config::DirSpec spec(snapDir);
addEmptyFile(snapDir, "ranking-constants.cfg");
+ addEmptyFile(snapDir, "ranking-expressions.cfg");
addEmptyFile(snapDir, "onnx-models.cfg");
addEmptyFile(snapDir, "imported-fields.cfg");
diff --git a/searchcore/src/vespa/searchcore/proton/server/matchers.cpp b/searchcore/src/vespa/searchcore/proton/server/matchers.cpp
index 53c96a81134..7288e3e98fb 100644
--- a/searchcore/src/vespa/searchcore/proton/server/matchers.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/matchers.cpp
@@ -2,11 +2,13 @@
#include "matchers.h"
#include <vespa/searchcore/proton/matching/matcher.h>
+#include <vespa/searchcore/proton/matching/ranking_expressions.h>
#include <vespa/searchcore/proton/matching/onnx_models.h>
#include <vespa/vespalib/stllike/hash_map.hpp>
namespace proton {
+using matching::RankingExpressions;
using matching::OnnxModels;
Matchers::Matchers(const vespalib::Clock &clock,
@@ -14,7 +16,7 @@ Matchers::Matchers(const vespalib::Clock &clock,
const matching::IConstantValueRepo &constantValueRepo)
: _rpmap(),
_fallback(new matching::Matcher(search::index::Schema(), search::fef::Properties(),
- clock, queryLimiter, constantValueRepo, OnnxModels(), -1)),
+ clock, queryLimiter, constantValueRepo, RankingExpressions(), OnnxModels(), -1)),
_default()
{ }
diff --git a/searchcore/src/vespa/searchcore/proton/server/pruneremoveddocumentsjob.h b/searchcore/src/vespa/searchcore/proton/server/pruneremoveddocumentsjob.h
index 9216b10e5a9..637c341ff6a 100644
--- a/searchcore/src/vespa/searchcore/proton/server/pruneremoveddocumentsjob.h
+++ b/searchcore/src/vespa/searchcore/proton/server/pruneremoveddocumentsjob.h
@@ -15,7 +15,7 @@ namespace proton {
struct IDocumentMetaStore;
class IPruneRemovedDocumentsHandler;
-class RawDocumentMetaData;
+struct RawDocumentMetaData;
/**
* Job that regularly checks whether old removed documents should be
diff --git a/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp b/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp
index bb6c5423175..7cdc523e381 100644
--- a/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp
@@ -15,6 +15,7 @@ ReconfigParams::configHasChanged() const
{
return _res.rankProfilesChanged ||
_res.rankingConstantsChanged ||
+ _res.rankingExpressionsChanged ||
_res.onnxModelsChanged ||
_res.indexschemaChanged ||
_res.attributesChanged ||
@@ -40,7 +41,7 @@ ReconfigParams::shouldSchemaChange() const
bool
ReconfigParams::shouldMatchersChange() const
{
- return _res.rankProfilesChanged || _res.rankingConstantsChanged || _res.onnxModelsChanged || shouldSchemaChange();
+ return _res.rankProfilesChanged || _res.rankingConstantsChanged || _res.rankingExpressionsChanged || _res.onnxModelsChanged || shouldSchemaChange();
}
bool
diff --git a/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.cpp b/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.cpp
index 1e5c558af0b..05e68ee0ec4 100644
--- a/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.cpp
@@ -21,6 +21,7 @@ using vespa::config::search::RankProfilesConfig;
namespace proton {
using matching::Matcher;
+using matching::RankingExpressions;
using matching::OnnxModels;
typedef AttributeReprocessingInitializer::Config ARIConfig;
@@ -107,6 +108,7 @@ SearchableDocSubDBConfigurer::~SearchableDocSubDBConfigurer() = default;
Matchers::UP
SearchableDocSubDBConfigurer::createMatchers(const Schema::SP &schema,
const RankProfilesConfig &cfg,
+ const RankingExpressions &rankingExpressions,
const OnnxModels &onnxModels)
{
auto newMatchers = std::make_unique<Matchers>(_clock, _queryLimiter, _constantValueRepo);
@@ -117,7 +119,8 @@ SearchableDocSubDBConfigurer::createMatchers(const Schema::SP &schema,
properties.add(property.name, property.value);
}
// schema instance only used during call.
- auto profptr = std::make_shared<Matcher>(*schema, properties, _clock, _queryLimiter, _constantValueRepo, onnxModels, _distributionKey);
+ auto profptr = std::make_shared<Matcher>(*schema, properties, _clock, _queryLimiter, _constantValueRepo,
+ rankingExpressions, onnxModels, _distributionKey);
newMatchers->add(name, profptr);
}
return newMatchers;
@@ -185,6 +188,7 @@ SearchableDocSubDBConfigurer::reconfigure(const DocumentDBConfig &newConfig,
_constantValueRepo.reconfigure(newConfig.getRankingConstants());
Matchers::SP newMatchers = createMatchers(newConfig.getSchemaSP(),
newConfig.getRankProfilesConfig(),
+ newConfig.getRankingExpressions(),
newConfig.getOnnxModels());
matchers = newMatchers;
shouldMatchViewChange = true;
diff --git a/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.h b/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.h
index d460bb6506f..1a9ba1abff7 100644
--- a/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.h
+++ b/searchcore/src/vespa/searchcore/proton/server/searchable_doc_subdb_configurer.h
@@ -74,6 +74,7 @@ public:
Matchers::UP createMatchers(const search::index::Schema::SP &schema,
const vespa::config::search::RankProfilesConfig &cfg,
+ const proton::matching::RankingExpressions &rankingExpressions,
const proton::matching::OnnxModels &onnxModels);
void reconfigureIndexSearchable();
diff --git a/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp b/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
index 51e6f8e45df..d7aaa901530 100644
--- a/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
@@ -201,7 +201,8 @@ SearchableDocSubDB::initViews(const DocumentDBConfig &configSnapshot, const Sess
const Schema::SP &schema = configSnapshot.getSchemaSP();
const IIndexManager::SP &indexMgr = getIndexManager();
_constantValueRepo.reconfigure(configSnapshot.getRankingConstants());
- Matchers::SP matchers = _configurer.createMatchers(schema, configSnapshot.getRankProfilesConfig(), configSnapshot.getOnnxModels());
+ Matchers::SP matchers = _configurer.createMatchers(schema, configSnapshot.getRankProfilesConfig(),
+ configSnapshot.getRankingExpressions(), configSnapshot.getOnnxModels());
auto matchView = std::make_shared<MatchView>(std::move(matchers), indexMgr->getSearchable(), attrMgr,
sessionManager, _metaStoreCtx, _docIdLimit);
_rSearchView.set(SearchView::create(
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
index c050719b361..de3baa8d26e 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
@@ -325,11 +325,10 @@ StoreOnlyFeedView::putSummary(SerialNum serialNum, Lid lid,
}
void
-StoreOnlyFeedView::putSummaryNoop(SerialNum serialNum, Lid lid,
- FutureStream futureStream, OnOperationDoneType onDone)
+StoreOnlyFeedView::putSummaryNoop(FutureStream futureStream, OnOperationDoneType onDone)
{
summaryExecutor().execute(
- makeLambdaTask([serialNum, lid, futureStream = std::move(futureStream), onDone] () mutable {
+ makeLambdaTask([futureStream = std::move(futureStream), onDone] () mutable {
(void) onDone;
vespalib::nbostream os = futureStream.get();
(void) os;
@@ -427,7 +426,7 @@ StoreOnlyFeedView::internalUpdate(FeedToken token, const UpdateOperation &updOp)
if (useDocStore) {
putSummary(serialNum, lid, std::move(futureStream), onWriteDone);
} else {
- putSummaryNoop(serialNum, lid, std::move(futureStream), onWriteDone);
+ putSummaryNoop(std::move(futureStream), onWriteDone);
}
_writeService.shared().execute(makeLambdaTask(
[upd = updOp.getUpdate(), useDocStore, lid, onWriteDone, promisedDoc = std::move(promisedDoc),
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
index 5a0257fda57..b9e02276180 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
@@ -157,7 +157,7 @@ private:
return _writeService.summary();
}
void putSummary(SerialNum serialNum, Lid lid, FutureStream doc, OnOperationDoneType onDone);
- void putSummaryNoop(SerialNum serialNum, Lid lid, FutureStream doc, OnOperationDoneType onDone);
+ void putSummaryNoop(FutureStream doc, OnOperationDoneType onDone);
void putSummary(SerialNum serialNum, Lid lid, DocumentSP doc, OnOperationDoneType onDone);
void removeSummary(SerialNum serialNum, Lid lid, OnWriteDoneType onDone);
void heartBeatSummary(SerialNum serialNum);
diff --git a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp
index 17cb91ac4ce..18774e60202 100644
--- a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp
+++ b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp
@@ -34,6 +34,7 @@ DocumentDBConfigBuilder::DocumentDBConfigBuilder(int64_t generation,
: _generation(generation),
_rankProfiles(std::make_shared<RankProfilesConfig>()),
_rankingConstants(std::make_shared<matching::RankingConstants>()),
+ _rankingExpressions(std::make_shared<matching::RankingExpressions>()),
_onnxModels(std::make_shared<matching::OnnxModels>()),
_indexschema(std::make_shared<IndexschemaConfig>()),
_attributes(std::make_shared<AttributesConfig>()),
@@ -58,6 +59,7 @@ DocumentDBConfigBuilder::DocumentDBConfigBuilder(const DocumentDBConfig &cfg)
: _generation(cfg.getGeneration()),
_rankProfiles(cfg.getRankProfilesConfigSP()),
_rankingConstants(cfg.getRankingConstantsSP()),
+ _rankingExpressions(cfg.getRankingExpressionsSP()),
_onnxModels(cfg.getOnnxModelsSP()),
_indexschema(cfg.getIndexschemaConfigSP()),
_attributes(cfg.getAttributesConfigSP()),
@@ -86,6 +88,7 @@ DocumentDBConfigBuilder::build()
_generation,
_rankProfiles,
_rankingConstants,
+ _rankingExpressions,
_onnxModels,
_indexschema,
_attributes,
diff --git a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h
index 706e14e73db..a248ed5544a 100644
--- a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h
+++ b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h
@@ -14,6 +14,7 @@ private:
int64_t _generation;
DocumentDBConfig::RankProfilesConfigSP _rankProfiles;
DocumentDBConfig::RankingConstants::SP _rankingConstants;
+ DocumentDBConfig::RankingExpressions::SP _rankingExpressions;
DocumentDBConfig::OnnxModels::SP _onnxModels;
DocumentDBConfig::IndexschemaConfigSP _indexschema;
DocumentDBConfig::AttributesConfigSP _attributes;
@@ -57,6 +58,10 @@ public:
_rankingConstants = rankingConstants_in;
return *this;
}
+ DocumentDBConfigBuilder &rankingExpressions(const DocumentDBConfig::RankingExpressions::SP &rankingExpressions_in) {
+ _rankingExpressions = rankingExpressions_in;
+ return *this;
+ }
DocumentDBConfigBuilder &onnxModels(const DocumentDBConfig::OnnxModels::SP &onnxModels_in) {
_onnxModels = onnxModels_in;
return *this;
diff --git a/searchlib/src/tests/features/ranking_expression/ranking_expression_test.cpp b/searchlib/src/tests/features/ranking_expression/ranking_expression_test.cpp
index 251040ecfa7..6baa6581edf 100644
--- a/searchlib/src/tests/features/ranking_expression/ranking_expression_test.cpp
+++ b/searchlib/src/tests/features/ranking_expression/ranking_expression_test.cpp
@@ -67,28 +67,38 @@ struct SetupResult {
RankingExpressionBlueprint rank;
DummyDependencyHandler deps;
bool setup_ok;
- SetupResult(const TypeMap &object_inputs, const vespalib::string &expression);
+ SetupResult(const TypeMap &object_inputs, const vespalib::string &expression,
+ bool external_expression = false);
~SetupResult();
};
SetupResult::SetupResult(const TypeMap &object_inputs,
- const vespalib::string &expression)
+ const vespalib::string &expression,
+ bool external_expression)
: stash(), index_env(), query_env(&index_env), rank(make_replacer()), deps(rank), setup_ok(false)
{
rank.setName("self");
- index_env.getProperties().add("self.rankingScript", expression);
for (const auto &input: object_inputs) {
deps.define_object_input(input.first, ValueType::from_spec(input.second));
}
- setup_ok = rank.setup(index_env, {});
+ std::vector<vespalib::string> params;
+ if (external_expression) {
+ params.push_back("my_expr");
+ index_env.addRankingExpression("my_expr", expression);
+ } else {
+ index_env.getProperties().add("self.rankingScript", expression);
+ }
+ Blueprint &bp = rank;
+ setup_ok = bp.setup(index_env, params);
EXPECT_TRUE(!deps.accept_type_mismatch);
}
SetupResult::~SetupResult() = default;
void verify_output_type(const TypeMap &object_inputs,
- const vespalib::string &expression, const FeatureType &expect)
+ const vespalib::string &expression, const FeatureType &expect,
+ bool external_expression = false)
{
- SetupResult result(object_inputs, expression);
+ SetupResult result(object_inputs, expression, external_expression);
EXPECT_TRUE(result.setup_ok);
EXPECT_EQUAL(1u, result.deps.output.size());
ASSERT_EQUAL(1u, result.deps.output_type.size());
@@ -126,6 +136,13 @@ TEST("require that ranking expression can resolve to concrete complex type") {
FeatureType::object(ValueType::from_spec("tensor(x{},y{},z{})"))));
}
+TEST("require that ranking expression can be external") {
+ TEST_DO(verify_output_type({}, "a*b", FeatureType::number(), true));
+ TEST_DO(verify_output_type({{"b", "double"}}, "a*b", FeatureType::object(ValueType::double_type()), true));
+ TEST_DO(verify_output_type({{"a", "tensor(x{},y{})"}, {"b", "tensor(y{},z{})"}}, "a*b",
+ FeatureType::object(ValueType::from_spec("tensor(x{},y{},z{})")), true));
+}
+
TEST("require that setup fails for incompatible types") {
TEST_DO(verify_setup_fail({{"a", "tensor(x{},y{})"}, {"b", "tensor(y[10],z{})"}}, "a*b"));
}
diff --git a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp
index 8cba6013627..070d70997e6 100644
--- a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp
@@ -250,7 +250,10 @@ RankingExpressionBlueprint::setup(const fef::IIndexEnvironment &env,
}
//LOG(debug, "Script from config: '%s'\n", script.c_str());
} else if (params.size() == 1) {
- script = params[0].getValue();
+ script = env.getRankingExpression(params[0].getValue());
+ if (script.empty()) {
+ script = params[0].getValue();
+ }
//LOG(debug, "Script from param: '%s'\n", script.c_str());
} else {
return fail("No expression given.");
diff --git a/searchlib/src/vespa/searchlib/fef/iindexenvironment.h b/searchlib/src/vespa/searchlib/fef/iindexenvironment.h
index 0cd017d8ffa..a01d7d8762b 100644
--- a/searchlib/src/vespa/searchlib/fef/iindexenvironment.h
+++ b/searchlib/src/vespa/searchlib/fef/iindexenvironment.h
@@ -123,6 +123,11 @@ public:
virtual std::unique_ptr<vespalib::eval::ConstantValue> getConstantValue(const vespalib::string &name) const = 0;
/**
+ * Returns the ranking expression with the given name or empty string if not found.
+ **/
+ virtual vespalib::string getRankingExpression(const vespalib::string &name) const = 0;
+
+ /**
* Get configuration for the given onnx model.
**/
virtual const OnnxModel *getOnnxModel(const vespalib::string &name) const = 0;
diff --git a/searchlib/src/vespa/searchlib/fef/test/indexenvironment.cpp b/searchlib/src/vespa/searchlib/fef/test/indexenvironment.cpp
index d2d336dcdc8..85b20710f7a 100644
--- a/searchlib/src/vespa/searchlib/fef/test/indexenvironment.cpp
+++ b/searchlib/src/vespa/searchlib/fef/test/indexenvironment.cpp
@@ -54,6 +54,22 @@ IndexEnvironment::addConstantValue(const vespalib::string &name,
(void) insertRes;
}
+vespalib::string
+IndexEnvironment::getRankingExpression(const vespalib::string &name) const
+{
+ auto pos = _expressions.find(name);
+ if (pos != _expressions.end()) {
+ return pos->second;
+ }
+ return {};
+}
+
+void
+IndexEnvironment::addRankingExpression(const vespalib::string &name, const vespalib::string &value)
+{
+ _expressions.insert_or_assign(name, value);
+}
+
const OnnxModel *
IndexEnvironment::getOnnxModel(const vespalib::string &name) const
{
diff --git a/searchlib/src/vespa/searchlib/fef/test/indexenvironment.h b/searchlib/src/vespa/searchlib/fef/test/indexenvironment.h
index 0d8d0091921..ca63117971a 100644
--- a/searchlib/src/vespa/searchlib/fef/test/indexenvironment.h
+++ b/searchlib/src/vespa/searchlib/fef/test/indexenvironment.h
@@ -48,6 +48,7 @@ public:
};
using ConstantsMap = std::map<vespalib::string, Constant>;
+ using ExprMap = std::map<vespalib::string, vespalib::string>;
using ModelMap = std::map<vespalib::string, OnnxModel>;
IndexEnvironment();
@@ -85,6 +86,9 @@ public:
vespalib::eval::ValueType type,
std::unique_ptr<vespalib::eval::Value> value);
+ vespalib::string getRankingExpression(const vespalib::string &name) const override;
+ void addRankingExpression(const vespalib::string &name, const vespalib::string &value);
+
const OnnxModel *getOnnxModel(const vespalib::string &name) const override;
void addOnnxModel(const OnnxModel &model);
@@ -98,6 +102,7 @@ private:
AttributeMap _attrMap;
TableManager _tableMan;
ConstantsMap _constants;
+ ExprMap _expressions;
ModelMap _models;
};
diff --git a/slobrok/src/vespa/slobrok/cfg.cpp b/slobrok/src/vespa/slobrok/cfg.cpp
index 03b2b12803e..01da82b41c1 100644
--- a/slobrok/src/vespa/slobrok/cfg.cpp
+++ b/slobrok/src/vespa/slobrok/cfg.cpp
@@ -34,7 +34,6 @@ Configurator::Configurator(Configurable& target, const config::ConfigUri & uri)
: _subscriber(uri.getContext()),
_handle(_subscriber.subscribe<cloud::config::SlobroksConfig>(uri.getConfigId())),
_target(target)
-
{
}
diff --git a/slobrok/src/vespa/slobrok/sbmirror.cpp b/slobrok/src/vespa/slobrok/sbmirror.cpp
index 6aff47e13fd..13680d16e68 100644
--- a/slobrok/src/vespa/slobrok/sbmirror.cpp
+++ b/slobrok/src/vespa/slobrok/sbmirror.cpp
@@ -3,6 +3,7 @@
#include "sbmirror.h"
#include <vespa/fnet/frt/supervisor.h>
#include <vespa/fnet/frt/target.h>
+#include <vespa/vespalib/util/exceptions.h>
#include <vespa/log/log.h>
LOG_SETUP(".slobrok.mirror");
@@ -30,7 +31,9 @@ MirrorAPI::MirrorAPI(FRT_Supervisor &orb, const ConfiguratorFactory & config)
_req(0)
{
_configurator->poll();
- LOG_ASSERT(_slobrokSpecs.ok());
+ if (!_slobrokSpecs.ok()) {
+ throw vespalib::IllegalStateException("Not able to initialize MirrorAPI due to missing or bad slobrok specs");
+ }
ScheduleNow();
}
diff --git a/storage/src/tests/distributor/distributor_stripe_pool_test.cpp b/storage/src/tests/distributor/distributor_stripe_pool_test.cpp
index fb7c446a781..bf3beda504e 100644
--- a/storage/src/tests/distributor/distributor_stripe_pool_test.cpp
+++ b/storage/src/tests/distributor/distributor_stripe_pool_test.cpp
@@ -73,7 +73,7 @@ struct ParkingInvariantCheckingMockStripe : MockTickableStripe {
TEST_F(DistributorStripePoolThreadingTest, can_park_and_unpark_single_stripe) {
ParkingInvariantCheckingMockStripe stripe(_is_parked);
- _pool.start({{&stripe}});
+ _pool.start({&stripe});
loop_park_unpark_cycle_until_test_time_expired();
_pool.stop_and_join();
}
diff --git a/storage/src/vespa/storage/distributor/maintenance/bucketprioritydatabase.h b/storage/src/vespa/storage/distributor/maintenance/bucketprioritydatabase.h
index 41878f09014..ec7b7791c11 100644
--- a/storage/src/vespa/storage/distributor/maintenance/bucketprioritydatabase.h
+++ b/storage/src/vespa/storage/distributor/maintenance/bucketprioritydatabase.h
@@ -56,9 +56,9 @@ public:
}
};
- typedef ConstIterator const_iterator;
+ using const_iterator = ConstIterator;
- virtual ~BucketPriorityDatabase() { }
+ virtual ~BucketPriorityDatabase() = default;
virtual const_iterator begin() const = 0;
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenanceoperation.h b/storage/src/vespa/storage/distributor/maintenance/maintenanceoperation.h
index f9fbc4a2be1..a1cdaf57119 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenanceoperation.h
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenanceoperation.h
@@ -3,13 +3,12 @@
#include <vespa/storage/distributor/operations/operation.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class MaintenanceOperation : public Operation
{
public:
- typedef enum {
+ enum Type {
DELETE_BUCKET,
MERGE_BUCKET,
SPLIT_BUCKET,
@@ -17,12 +16,11 @@ public:
SET_BUCKET_STATE,
GARBAGE_COLLECTION,
OPERATION_COUNT
- } Type;
+ };
- typedef std::shared_ptr<MaintenanceOperation> SP;
+ using SP = std::shared_ptr<MaintenanceOperation>;
virtual const std::string& getDetailedReason() const = 0;
};
-} // distributor
-} // storage
+} // storage::distributor
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenanceoperationgenerator.h b/storage/src/vespa/storage/distributor/maintenance/maintenanceoperationgenerator.h
index 1afc0990222..3cf6ad33694 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenanceoperationgenerator.h
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenanceoperationgenerator.h
@@ -5,13 +5,12 @@
#include <vespa/storage/distributor/maintenance/maintenanceoperation.h>
#include <vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class MaintenanceOperationGenerator
{
public:
- virtual ~MaintenanceOperationGenerator() {}
+ virtual ~MaintenanceOperationGenerator() = default;
/**
* Generate and return the highest prioritized maintenance operation for
@@ -40,5 +39,4 @@ public:
}
};
-} // distributor
-} // storage
+} // storage::distributor
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenancepriority.h b/storage/src/vespa/storage/distributor/maintenance/maintenancepriority.h
index 11bde9b1986..ad82022b216 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenancepriority.h
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenancepriority.h
@@ -1,8 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class MaintenancePriority
{
@@ -18,7 +17,7 @@ public:
PRIORITY_LIMIT
};
- static const std::string toString(Priority pri) {
+ static constexpr const char* toString(Priority pri) noexcept {
switch (pri) {
case NO_MAINTENANCE_NEEDED: return "NO_MAINTENANCE_NEEDED";
case VERY_LOW: return "VERY_LOW";
@@ -56,6 +55,3 @@ private:
};
}
-}
-
-
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenancepriorityandtype.h b/storage/src/vespa/storage/distributor/maintenance/maintenancepriorityandtype.h
index 9335f053b4c..4e272bf03cc 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenancepriorityandtype.h
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenancepriorityandtype.h
@@ -4,8 +4,7 @@
#include <vespa/storage/distributor/maintenance/maintenanceoperation.h>
#include <vespa/storage/distributor/maintenance/maintenancepriority.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class MaintenancePriorityAndType
{
@@ -32,5 +31,4 @@ public:
}
};
-} // distributor
-} // storage
+} // storage::distributor
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenanceprioritygenerator.h b/storage/src/vespa/storage/distributor/maintenance/maintenanceprioritygenerator.h
index ca7fb6ae81f..bf32fd4ccc1 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenanceprioritygenerator.h
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenanceprioritygenerator.h
@@ -6,13 +6,12 @@
#include <vespa/storage/distributor/maintenance/maintenancepriorityandtype.h>
#include <vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class MaintenancePriorityGenerator
{
public:
- virtual ~MaintenancePriorityGenerator() {}
+ virtual ~MaintenancePriorityGenerator() = default;
virtual MaintenancePriorityAndType prioritize(
const document::Bucket &bucket,
@@ -20,5 +19,3 @@ public:
};
}
-}
-
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenancescanner.h b/storage/src/vespa/storage/distributor/maintenance/maintenancescanner.h
index f329133f2c4..412fe91d690 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenancescanner.h
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenancescanner.h
@@ -4,13 +4,12 @@
#include <vespa/document/bucket/bucketspace.h>
#include <vespa/storage/bucketdb/bucketdatabase.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class MaintenanceScanner
{
public:
- virtual ~MaintenanceScanner() {}
+ virtual ~MaintenanceScanner() = default;
class ScanResult {
bool _done;
@@ -38,5 +37,3 @@ public:
};
}
-}
-
diff --git a/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.h b/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.h
index 7d17e42b934..acb0c555f49 100644
--- a/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.h
+++ b/storage/src/vespa/storage/distributor/maintenance/maintenancescheduler.h
@@ -5,8 +5,7 @@
#include <vespa/storage/distributor/maintenance/simplemaintenancescanner.h>
#include <vespa/storage/distributor/operationstarter.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class MaintenanceOperationGenerator;
class BucketPriorityDatabase;
@@ -19,7 +18,7 @@ public:
NORMAL_SCHEDULING_MODE
};
- typedef int WaitTimeMs;
+ using WaitTimeMs = int;
MaintenanceScheduler(MaintenanceOperationGenerator& operationGenerator,
BucketPriorityDatabase& priorityDb,
@@ -31,7 +30,6 @@ private:
MaintenanceScheduler(const MaintenanceScheduler&);
MaintenanceScheduler& operator=(const MaintenanceScheduler&);
- //void scanNextBucket();
PrioritizedBucket getMostImportantBucket();
bool possibleToSchedule(const PrioritizedBucket& bucket, SchedulingMode currentMode) const;
bool possibleToScheduleInEmergency(const PrioritizedBucket& bucket) const;
@@ -46,5 +44,3 @@ private:
};
}
-}
-
diff --git a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp
index 9d999681407..b954ef93c76 100644
--- a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp
+++ b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp
@@ -20,8 +20,8 @@ operator<<(std::ostream& os, const NodeMaintenanceStats& stats)
return os;
}
-NodeMaintenanceStatsTracker::NodeMaintenanceStatsTracker() {}
-NodeMaintenanceStatsTracker::~NodeMaintenanceStatsTracker() {}
+NodeMaintenanceStatsTracker::NodeMaintenanceStatsTracker() = default;
+NodeMaintenanceStatsTracker::~NodeMaintenanceStatsTracker() = default;
}
diff --git a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h
index 296c6f76563..faf253fc84c 100644
--- a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h
+++ b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h
@@ -6,8 +6,7 @@
#include <stdint.h>
#include <vespa/document/bucket/bucketspace.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
struct NodeMaintenanceStats
{
@@ -17,12 +16,15 @@ struct NodeMaintenanceStats
uint64_t copyingOut;
uint64_t total;
- NodeMaintenanceStats()
+ constexpr NodeMaintenanceStats() noexcept
: movingOut(0), syncing(0), copyingIn(0), copyingOut(0), total(0)
{}
- NodeMaintenanceStats(uint64_t movingOut_, uint64_t syncing_, uint64_t copyingIn_, uint64_t copyingOut_, uint64_t total_)
- : movingOut(movingOut_), syncing(syncing_), copyingIn(copyingIn_), copyingOut(copyingOut_), total(total_)
+ constexpr NodeMaintenanceStats(uint64_t movingOut_, uint64_t syncing_, uint64_t copyingIn_,
+ uint64_t copyingOut_, uint64_t total_) noexcept
+ : movingOut(movingOut_), syncing(syncing_),
+ copyingIn(copyingIn_), copyingOut(copyingOut_),
+ total(total_)
{}
bool operator==(const NodeMaintenanceStats& other) const noexcept {
@@ -52,6 +54,7 @@ private:
public:
NodeMaintenanceStatsTracker();
~NodeMaintenanceStatsTracker();
+
void incMovingOut(uint16_t node, document::BucketSpace bucketSpace) {
++_stats[node][bucketSpace].movingOut;
}
@@ -92,6 +95,4 @@ public:
}
};
-} // distributor
-} // storage
-
+} // storage::distributor
diff --git a/storage/src/vespa/storage/distributor/maintenance/prioritizedbucket.h b/storage/src/vespa/storage/distributor/maintenance/prioritizedbucket.h
index fde73de0358..d6d68a956be 100644
--- a/storage/src/vespa/storage/distributor/maintenance/prioritizedbucket.h
+++ b/storage/src/vespa/storage/distributor/maintenance/prioritizedbucket.h
@@ -6,13 +6,11 @@
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/storage/distributor/maintenance/maintenancepriority.h>
-namespace storage {
-
-namespace distributor {
+namespace storage::distributor {
class PrioritizedBucket {
public:
- typedef MaintenancePriority::Priority Priority;
+ using Priority = MaintenancePriority::Priority;
static const PrioritizedBucket INVALID;
@@ -40,7 +38,7 @@ public:
std::string toString() const {
return vespalib::make_string("PrioritizedBucket(%s, pri %s)",
_bucket.toString().c_str(),
- MaintenancePriority::toString(_priority).c_str());
+ MaintenancePriority::toString(_priority));
}
bool operator==(const PrioritizedBucket& other) const {
@@ -68,6 +66,3 @@ std::ostream&
operator<<(std::ostream& os, const PrioritizedBucket& bucket);
}
-
-}
-
diff --git a/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.cpp b/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.cpp
index 7b75091e252..fa1a632d355 100644
--- a/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.cpp
+++ b/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.cpp
@@ -6,9 +6,7 @@
namespace storage::distributor {
-SimpleBucketPriorityDatabase::~SimpleBucketPriorityDatabase()
-{
-}
+SimpleBucketPriorityDatabase::~SimpleBucketPriorityDatabase() = default;
void
SimpleBucketPriorityDatabase::clearAllEntriesForBucket(const document::Bucket &bucket)
diff --git a/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.h b/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.h
index 70b007decb6..db02c248980 100644
--- a/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.h
+++ b/storage/src/vespa/storage/distributor/maintenance/simplebucketprioritydatabase.h
@@ -5,14 +5,13 @@
#include <set>
#include <map>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class SimpleBucketPriorityDatabase : public BucketPriorityDatabase
{
public:
virtual ~SimpleBucketPriorityDatabase();
- typedef PrioritizedBucket::Priority Priority;
+ using Priority = PrioritizedBucket::Priority;
virtual void setPriority(const PrioritizedBucket&) override;
virtual const_iterator begin() const override;
@@ -21,8 +20,8 @@ public:
std::string toString() const;
private:
- typedef std::set<document::Bucket> BucketSet;
- typedef std::map<Priority, BucketSet> PriorityMap;
+ using BucketSet = std::set<document::Bucket>;
+ using PriorityMap = std::map<Priority, BucketSet>;
class SimpleConstIteratorImpl : public ConstIteratorImpl
{
@@ -62,4 +61,3 @@ private:
};
}
-}
diff --git a/storage/src/vespa/storage/distributor/maintenance/simplemaintenancescanner.h b/storage/src/vespa/storage/distributor/maintenance/simplemaintenancescanner.h
index 5b316201036..254b3244171 100644
--- a/storage/src/vespa/storage/distributor/maintenance/simplemaintenancescanner.h
+++ b/storage/src/vespa/storage/distributor/maintenance/simplemaintenancescanner.h
@@ -7,8 +7,7 @@
#include "node_maintenance_stats_tracker.h"
#include <vespa/storage/distributor/distributor_bucket_space_repo.h>
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
class SimpleMaintenanceScanner : public MaintenanceScanner
{
@@ -31,7 +30,7 @@ public:
private:
BucketPriorityDatabase& _bucketPriorityDb;
const MaintenancePriorityGenerator& _priorityGenerator;
- const DistributorBucketSpaceRepo &_bucketSpaceRepo;
+ const DistributorBucketSpaceRepo& _bucketSpaceRepo;
DistributorBucketSpaceRepo::BucketSpaceMap::const_iterator _bucketSpaceItr;
document::BucketId _bucketCursor;
PendingMaintenanceStats _pendingMaintenance;
@@ -60,4 +59,3 @@ std::ostream&
operator<<(std::ostream&, const SimpleMaintenanceScanner::GlobalMaintenanceStats&);
}
-}
diff --git a/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp b/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp
index 42d9e3d4a3d..142ff72bc79 100644
--- a/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp
@@ -185,6 +185,9 @@ public:
}
};
+// TODO STRIPE replace with check for pending cluster state transition.
+// Null-bucket messages are not intercepted nor observeable by stripes,
+// only by the top-level distributor.
bool
checkNullBucketRequestBucketInfoMessage(uint16_t node,
document::BucketSpace bucketSpace,
diff --git a/storage/src/vespa/storage/distributor/operations/operation.h b/storage/src/vespa/storage/distributor/operations/operation.h
index 75d72a2b5c9..5099762fd6a 100644
--- a/storage/src/vespa/storage/distributor/operations/operation.h
+++ b/storage/src/vespa/storage/distributor/operations/operation.h
@@ -5,7 +5,6 @@
#include <vespa/storage/distributor/distributormessagesender.h>
#include <vespa/storageframework/generic/clock/time.h>
-
namespace storage {
namespace api {
diff --git a/storage/src/vespa/storage/distributor/statechecker.cpp b/storage/src/vespa/storage/distributor/statechecker.cpp
index 82b8371e163..049e39fa3fc 100644
--- a/storage/src/vespa/storage/distributor/statechecker.cpp
+++ b/storage/src/vespa/storage/distributor/statechecker.cpp
@@ -79,6 +79,7 @@ StateChecker::Context::Context(const DistributorNodeContext& node_ctx_in,
db(distributorBucketSpace.getBucketDatabase()),
stats(statsTracker)
{
+ // TODO STRIPE use existing cache for computing ideal storage nodes for bucket
idealState = distribution.getIdealStorageNodes(systemState, bucket.getBucketId());
unorderedIdealState.insert(idealState.begin(), idealState.end());
}
diff --git a/storage/src/vespa/storage/persistence/mergehandler.cpp b/storage/src/vespa/storage/persistence/mergehandler.cpp
index 6d580ead659..004a6a798a9 100644
--- a/storage/src/vespa/storage/persistence/mergehandler.cpp
+++ b/storage/src/vespa/storage/persistence/mergehandler.cpp
@@ -1127,32 +1127,6 @@ MergeHandler::handleGetBucketDiff(api::GetBucketDiffCommand& cmd, MessageTracker
return tracker;
}
-namespace {
-
- struct DiffInfoTimestampOrder
- : public std::binary_function<api::GetBucketDiffCommand::Entry,
- api::GetBucketDiffCommand::Entry, bool>
- {
- bool operator()(const api::GetBucketDiffCommand::Entry& x,
- const api::GetBucketDiffCommand::Entry& y)
- {
- return (x._timestamp < y._timestamp);
- }
- };
-
- struct ApplyDiffInfoTimestampOrder
- : public std::binary_function<api::ApplyBucketDiffCommand::Entry,
- api::ApplyBucketDiffCommand::Entry, bool>
- {
- bool operator()(const api::ApplyBucketDiffCommand::Entry& x,
- const api::ApplyBucketDiffCommand::Entry& y)
- {
- return (x._entry._timestamp < y._entry._timestamp);
- }
- };
-
-} // End of anonymous namespace
-
void
MergeHandler::handleGetBucketDiffReply(api::GetBucketDiffReply& reply, MessageSender& sender) const
{
diff --git a/streamingvisitors/src/vespa/searchvisitor/indexenvironment.h b/streamingvisitors/src/vespa/searchvisitor/indexenvironment.h
index dc7be36c290..7bc706f21ce 100644
--- a/streamingvisitors/src/vespa/searchvisitor/indexenvironment.h
+++ b/streamingvisitors/src/vespa/searchvisitor/indexenvironment.h
@@ -73,6 +73,10 @@ public:
return vespalib::eval::ConstantValue::UP();
}
+ vespalib::string getRankingExpression(const vespalib::string &) const override {
+ return {};
+ }
+
const search::fef::OnnxModel *getOnnxModel(const vespalib::string &) const override {
return nullptr;
}
diff --git a/vespa-feed-client-cli/CMakeLists.txt b/vespa-feed-client-cli/CMakeLists.txt
new file mode 100644
index 00000000000..a918981dcd3
--- /dev/null
+++ b/vespa-feed-client-cli/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+install_java_artifact(vespa-feed-client-cli)
+
+vespa_install_script(src/main/sh/vespa-feed-client.sh vespa-feed-client bin)
diff --git a/vespa-feed-client-cli/OWNERS b/vespa-feed-client-cli/OWNERS
new file mode 100644
index 00000000000..606d074d8a8
--- /dev/null
+++ b/vespa-feed-client-cli/OWNERS
@@ -0,0 +1,2 @@
+bjorncs
+jonmv
diff --git a/vespa-feed-client-cli/pom.xml b/vespa-feed-client-cli/pom.xml
new file mode 100644
index 00000000000..62ff5c149ec
--- /dev/null
+++ b/vespa-feed-client-cli/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<!-- Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>7-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>vespa-feed-client-cli</artifactId>
+ <packaging>jar</packaging>
+ <version>7-SNAPSHOT</version>
+
+ <properties>
+ <maven.javadoc.skip>true</maven.javadoc.skip>
+ <!-- Used by internal properties that are still using JDK8-->
+ <maven.compiler.release>8</maven.compiler.release>
+ </properties>
+
+ <dependencies>
+ <!-- compile scope -->
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespa-feed-client</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- test scope -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <jdkToolchain>
+ <version>${java.version}</version>
+ </jdkToolchain>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ <showDeprecation>true</showDeprecation>
+ <compilerArgs>
+ <arg>-Xlint:all</arg>
+ <arg>-Xlint:-serial</arg>
+ <arg>-Werror</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>ai.vespa.feed.client.CliClient</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/CliArguments.java b/vespa-feed-client-cli/src/main/java/ai/vespa/feed/client/CliArguments.java
index 63b438134d6..33c3d0e7894 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/CliArguments.java
+++ b/vespa-feed-client-cli/src/main/java/ai/vespa/feed/client/CliArguments.java
@@ -16,6 +16,9 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
@@ -28,16 +31,18 @@ class CliArguments {
private static final Options optionsDefinition = createOptions();
- private static final String HELP_OPTION = "help";
- private static final String VERSION_OPTION = "version";
+ private static final String BENCHMARK_OPTION = "benchmark";
+ private static final String CA_CERTIFICATES_OPTION = "ca-certificates";
+ private static final String CERTIFICATE_OPTION = "certificate";
+ private static final String CONNECTIONS_OPTION = "connections";
+ private static final String DISABLE_SSL_HOSTNAME_VERIFICATION_OPTION = "disable-ssl-hostname-verification";
private static final String ENDPOINT_OPTION = "endpoint";
private static final String FILE_OPTION = "file";
- private static final String CONNECTIONS_OPTION = "connections";
+ private static final String HEADER_OPTION = "header";
+ private static final String HELP_OPTION = "help";
private static final String MAX_STREAMS_PER_CONNECTION = "max-streams-per-connection";
- private static final String CERTIFICATE_OPTION = "certificate";
private static final String PRIVATE_KEY_OPTION = "private-key";
- private static final String CA_CERTIFICATES_OPTION = "ca-certificates";
- private static final String DISABLE_SSL_HOSTNAME_VERIFICATION_OPTION = "disable-ssl-hostname-verification";
+ private static final String VERSION_OPTION = "version";
private final CommandLine arguments;
@@ -89,8 +94,28 @@ class CliArguments {
.orElseThrow(() -> new CliArgumentsException("Feed file must be specified"));
}
+ Map<String, String> headers() throws CliArgumentsException {
+ String[] rawArguments = arguments.getOptionValues(HEADER_OPTION);
+ if (rawArguments == null) return Collections.emptyMap();
+ Map<String, String> headers = new HashMap<>();
+ for (String rawArgument : rawArguments) {
+ if (rawArgument.startsWith("\"") || rawArgument.startsWith("'")) {
+ rawArgument = rawArgument.substring(1);
+ }
+ if (rawArgument.endsWith("\"") || rawArgument.endsWith("'")) {
+ rawArgument = rawArgument.substring(0, rawArgument.length() - 1);
+ }
+ int colonIndex = rawArgument.indexOf(':');
+ if (colonIndex == -1) throw new CliArgumentsException("Invalid header: '" + rawArgument + "'");
+ headers.put(rawArgument.substring(0, colonIndex), rawArgument.substring(colonIndex + 1).trim());
+ }
+ return Collections.unmodifiableMap(headers);
+ }
+
boolean sslHostnameVerificationDisabled() { return has(DISABLE_SSL_HOSTNAME_VERIFICATION_OPTION); }
+ boolean benchmarkModeEnabled() { return has(BENCHMARK_OPTION); }
+
private OptionalInt intValue(String option) throws CliArgumentsException {
try {
Number number = (Number) arguments.getParsedOptionValue(option);
@@ -113,6 +138,7 @@ class CliArguments {
private boolean has(String option) { return arguments.hasOption(option); }
private static Options createOptions() {
+ // TODO Add description to each option
return new Options()
.addOption(Option.builder()
.longOpt(HELP_OPTION)
@@ -126,6 +152,10 @@ class CliArguments {
.type(URL.class)
.build())
.addOption(Option.builder()
+ .longOpt(HEADER_OPTION)
+ .hasArgs()
+ .build())
+ .addOption(Option.builder()
.longOpt(FILE_OPTION)
.type(File.class)
.hasArg()
@@ -162,6 +192,9 @@ class CliArguments {
.build())
.addOption(Option.builder()
.longOpt(DISABLE_SSL_HOSTNAME_VERIFICATION_OPTION)
+ .build())
+ .addOption(Option.builder()
+ .longOpt(BENCHMARK_OPTION)
.build());
}
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/CliClient.java b/vespa-feed-client-cli/src/main/java/ai/vespa/feed/client/CliClient.java
index 83a0c650318..27ecada98c3 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/CliClient.java
+++ b/vespa-feed-client-cli/src/main/java/ai/vespa/feed/client/CliClient.java
@@ -4,6 +4,7 @@ package ai.vespa.feed.client;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Path;
import java.util.Properties;
@@ -13,25 +14,27 @@ import java.util.Properties;
*
* @author bjorncs
*/
-class CliClient {
+public class CliClient {
private final PrintStream systemOut;
private final PrintStream systemError;
+ private final InputStream systemIn;
private final Properties systemProperties;
- CliClient(PrintStream systemOut, PrintStream systemError, Properties systemProperties) {
+ private CliClient(PrintStream systemOut, PrintStream systemError, InputStream systemIn, Properties systemProperties) {
this.systemOut = systemOut;
this.systemError = systemError;
+ this.systemIn = systemIn;
this.systemProperties = systemProperties;
}
public static void main(String[] args) {
- CliClient client = new CliClient(System.out, System.err, System.getProperties());
+ CliClient client = new CliClient(System.out, System.err, System.in, System.getProperties());
int exitCode = client.run(args);
System.exit(exitCode);
}
- int run(String[] rawArgs) {
+ private int run(String[] rawArgs) {
try {
CliArguments cliArgs = CliArguments.fromRawArgs(rawArgs);
if (cliArgs.helpSpecified()) {
@@ -68,6 +71,7 @@ class CliClient {
}
builder.setSslContext(sslContextBuilder.build());
}
+ cliArgs.headers().forEach(builder::addRequestHeader);
return builder.build();
}
diff --git a/vespa-feed-client-cli/src/main/sh/vespa-feed-client.sh b/vespa-feed-client-cli/src/main/sh/vespa-feed-client.sh
new file mode 100755
index 00000000000..2a166dd40bb
--- /dev/null
+++ b/vespa-feed-client-cli/src/main/sh/vespa-feed-client.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+# BEGIN environment bootstrap section
+# Do not edit between here and END as this section should stay identical in all scripts
+
+findpath () {
+ myname=${0}
+ mypath=${myname%/*}
+ myname=${myname##*/}
+ empty_if_start_slash=${mypath%%/*}
+ if [ "${empty_if_start_slash}" ]; then
+ mypath=$(pwd)/${mypath}
+ fi
+ if [ "$mypath" ] && [ -d "$mypath" ]; then
+ return
+ fi
+ mypath=$(pwd)
+ if [ -f "${mypath}/${myname}" ]; then
+ return
+ fi
+ echo "FATAL: Could not figure out the path where $myname lives from $0"
+ exit 1
+}
+
+COMMON_ENV=libexec/vespa/common-env.sh
+
+source_common_env () {
+ if [ "$VESPA_HOME" ] && [ -d "$VESPA_HOME" ]; then
+ export VESPA_HOME
+ common_env=$VESPA_HOME/$COMMON_ENV
+ if [ -f "$common_env" ]; then
+ . $common_env
+ return
+ fi
+ fi
+ return 1
+}
+
+findroot () {
+ source_common_env && return
+ if [ "$VESPA_HOME" ]; then
+ echo "FATAL: bad VESPA_HOME value '$VESPA_HOME'"
+ exit 1
+ fi
+ if [ "$ROOT" ] && [ -d "$ROOT" ]; then
+ VESPA_HOME="$ROOT"
+ source_common_env && return
+ fi
+ findpath
+ while [ "$mypath" ]; do
+ VESPA_HOME=${mypath}
+ source_common_env && return
+ mypath=${mypath%/*}
+ done
+ echo "FATAL: missing VESPA_HOME environment variable"
+ echo "Could not locate $COMMON_ENV anywhere"
+ exit 1
+}
+
+findhost () {
+ if [ "${VESPA_HOSTNAME}" = "" ]; then
+ VESPA_HOSTNAME=$(vespa-detect-hostname || hostname -f || hostname || echo "localhost") || exit 1
+ fi
+ validate="${VESPA_HOME}/bin/vespa-validate-hostname"
+ if [ -f "$validate" ]; then
+ "$validate" "${VESPA_HOSTNAME}" || exit 1
+ fi
+ export VESPA_HOSTNAME
+}
+
+findroot
+findhost
+
+# END environment bootstrap section
+
+export MALLOC_ARENA_MAX=1 #Does not need fast allocation
+exec java \
+-Djava.library.path=${VESPA_HOME}/libexec64/native:${VESPA_HOME}/lib64 \
+-Djava.awt.headless=true \
+-Xms128m -Xmx2048m $(getJavaOptionsIPV46) \
+-cp ${VESPA_HOME}/lib/jars/vespa-feed-client-cli.jar ai.vespa.feed.client.CliClient "$@"
diff --git a/vespa-feed-client/src/test/java/ai/vespa/feed/client/CliArgumentsTest.java b/vespa-feed-client-cli/src/test/java/ai/vespa/feed/client/CliArgumentsTest.java
index b8dda66bf96..33ee31ff0dc 100644
--- a/vespa-feed-client/src/test/java/ai/vespa/feed/client/CliArgumentsTest.java
+++ b/vespa-feed-client-cli/src/test/java/ai/vespa/feed/client/CliArgumentsTest.java
@@ -20,7 +20,8 @@ class CliArgumentsTest {
CliArguments args = CliArguments.fromRawArgs(new String[]{
"--endpoint=https://vespa.ai:4443/", "--file=feed.json", "--connections=10",
"--max-streams-per-connection=128", "--certificate=cert.pem", "--private-key=key.pem",
- "--ca-certificates=ca-certs.pem", "--disable-ssl-hostname-verification"});
+ "--ca-certificates=ca-certs.pem", "--disable-ssl-hostname-verification",
+ "--header=\"My-Header: my-value\"", "--header", "Another-Header: another-value", "--benchmark"});
assertEquals(URI.create("https://vespa.ai:4443/"), args.endpoint());
assertEquals(Paths.get("feed.json"), args.inputFile());
assertEquals(10, args.connections().getAsInt());
@@ -30,6 +31,11 @@ class CliArgumentsTest {
assertEquals(Paths.get("ca-certs.pem"), args.caCertificates().get());
assertTrue(args.sslHostnameVerificationDisabled());
assertFalse(args.helpSpecified());
+ assertFalse(args.versionSpecified());
+ assertEquals(2, args.headers().size());
+ assertEquals("my-value", args.headers().get("My-Header"));
+ assertEquals("another-value", args.headers().get("Another-Header"));
+ assertTrue(args.benchmarkModeEnabled());
}
@Test
diff --git a/vespa-feed-client/src/test/resources/help.txt b/vespa-feed-client-cli/src/test/resources/help.txt
index d0017003a11..9ad7642d4ec 100644
--- a/vespa-feed-client/src/test/resources/help.txt
+++ b/vespa-feed-client-cli/src/test/resources/help.txt
@@ -1,11 +1,13 @@
usage: vespa-feed-client <options>
Vespa feed client
+ --benchmark
--ca-certificates <arg>
--certificate <arg>
--connections <arg>
--disable-ssl-hostname-verification
--endpoint <arg>
--file <arg>
+ --header <arg>
--help
--max-streams-per-connection <arg>
--private-key <arg>
diff --git a/vespa-feed-client/CMakeLists.txt b/vespa-feed-client/CMakeLists.txt
index ee6dfdeff05..7e6346c81cb 100644
--- a/vespa-feed-client/CMakeLists.txt
+++ b/vespa-feed-client/CMakeLists.txt
@@ -1,2 +1 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-# TODO Install fat jar \ No newline at end of file
diff --git a/vespa-feed-client/pom.xml b/vespa-feed-client/pom.xml
index 4a44f1c0240..7759e9d2308 100644
--- a/vespa-feed-client/pom.xml
+++ b/vespa-feed-client/pom.xml
@@ -18,37 +18,18 @@
</properties>
<dependencies>
- <!-- provided -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <scope>provided</scope>
- </dependency>
+ <!-- compile scope -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
- <scope>provided</scope>
+ <scope>compile</scope>
</dependency>
-
- <!-- compile scope -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<scope>compile</scope>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
</dependency>
<dependency>
- <groupId>commons-cli</groupId>
- <artifactId>commons-cli</artifactId>
- <scope>compile</scope>
- </dependency>
-
- <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>compile</scope>
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClientBuilder.java b/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClientBuilder.java
index 95a49abcc25..3cd3f3cb4ca 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClientBuilder.java
+++ b/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClientBuilder.java
@@ -3,7 +3,10 @@ package ai.vespa.feed.client;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.io.UncheckedIOException;
import java.net.URI;
+import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
@@ -27,6 +30,9 @@ public class FeedClientBuilder {
int maxConnections = 4;
int maxStreamsPerConnection = 1024;
FeedClient.RetryStrategy retryStrategy = defaultRetryStrategy;
+ Path certificate;
+ Path privateKey;
+ Path caCertificates;
public static FeedClientBuilder create(URI endpoint) { return new FeedClientBuilder(endpoint); }
@@ -63,6 +69,9 @@ public class FeedClientBuilder {
}
public FeedClientBuilder setSslContext(SSLContext context) {
+ if (certificate != null || caCertificates != null || privateKey != null) {
+ throw new IllegalArgumentException("Cannot set both SSLContext and certificate / CA certificates");
+ }
this.sslContext = requireNonNull(context);
return this;
}
@@ -86,8 +95,25 @@ public class FeedClientBuilder {
return this;
}
+ public FeedClientBuilder setCertificate(Path certificatePemFile, Path privateKeyPemFile) {
+ if (sslContext != null) throw new IllegalArgumentException("Cannot set both SSLContext and certificate");
+ this.certificate = certificatePemFile;
+ this.privateKey = privateKeyPemFile;
+ return this;
+ }
+
+ public FeedClientBuilder setCaCertificates(Path caCertificatesFile) {
+ if (sslContext != null) throw new IllegalArgumentException("Cannot set both SSLContext and CA certificate");
+ this.caCertificates = caCertificatesFile;
+ return this;
+ }
+
public FeedClient build() {
- return new HttpFeedClient(this);
+ try {
+ return new HttpFeedClient(this);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
}
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpFeedClient.java b/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpFeedClient.java
index fc1637fe17f..49f79c07d37 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpFeedClient.java
+++ b/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpFeedClient.java
@@ -5,19 +5,17 @@ import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
-import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
-import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.net.URIBuilder;
-import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
import org.apache.hc.core5.reactor.IOReactorConfig;
-import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
+import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
@@ -47,7 +45,7 @@ class HttpFeedClient implements FeedClient {
private final CloseableHttpAsyncClient httpClient;
private final AtomicBoolean closed = new AtomicBoolean();
- HttpFeedClient(FeedClientBuilder builder) {
+ HttpFeedClient(FeedClientBuilder builder) throws IOException {
this.endpoint = builder.endpoint;
this.requestHeaders = new HashMap<>(builder.requestHeaders);
@@ -56,45 +54,47 @@ class HttpFeedClient implements FeedClient {
this.httpClient.start();
}
- private static CloseableHttpAsyncClient createHttpClient(FeedClientBuilder builder, HttpRequestStrategy retryStrategy) {
- HttpAsyncClientBuilder httpClientBuilder = HttpAsyncClientBuilder.create()
- .setUserAgent(String.format("vespa-feed-client/%s", Vespa.VERSION))
- .setDefaultHeaders(Collections.singletonList(new BasicHeader("Vespa-Client-Version", Vespa.VERSION)))
- .disableCookieManagement()
- .disableRedirectHandling()
- .disableConnectionState()
- .setRetryStrategy(retryStrategy)
- .setIOReactorConfig(IOReactorConfig.custom()
- .setSoTimeout(Timeout.ofSeconds(10))
- .build())
- .setDefaultRequestConfig(
- RequestConfig.custom()
- .setConnectTimeout(Timeout.ofSeconds(10))
- .setConnectionRequestTimeout(Timeout.DISABLED)
- .setResponseTimeout(Timeout.ofMinutes(5))
- .build())
- .setH2Config(H2Config.custom()
- .setMaxConcurrentStreams(builder.maxStreamsPerConnection)
- .setCompressionEnabled(true)
- .setPushEnabled(false)
- .build());
-
- int maxConnections = builder.maxConnections;
- PoolingAsyncClientConnectionManagerBuilder connectionManagerBuilder = PoolingAsyncClientConnectionManagerBuilder.create()
- .setConnectionTimeToLive(TimeValue.ofMinutes(10))
- .setMaxConnTotal(maxConnections)
- .setMaxConnPerRoute(maxConnections)
- .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.LAX);
- if (builder.sslContext != null) {
- ClientTlsStrategyBuilder tlsStrategyBuilder = ClientTlsStrategyBuilder.create()
- .setSslContext(builder.sslContext);
- if (builder.hostnameVerifier != null) {
- tlsStrategyBuilder.setHostnameVerifier(builder.hostnameVerifier);
- }
- connectionManagerBuilder.setTlsStrategy(tlsStrategyBuilder.build());
+ private static CloseableHttpAsyncClient createHttpClient(FeedClientBuilder builder, HttpRequestStrategy retryStrategy) throws IOException {
+ H2AsyncClientBuilder httpClientBuilder = H2AsyncClientBuilder.create()
+ .setUserAgent(String.format("vespa-feed-client/%s", Vespa.VERSION))
+ .setDefaultHeaders(Collections.singletonList(new BasicHeader("Vespa-Client-Version", Vespa.VERSION)))
+ .disableCookieManagement()
+ .disableRedirectHandling()
+ .setRetryStrategy(retryStrategy)
+ .setIOReactorConfig(IOReactorConfig.custom()
+ .setSoTimeout(Timeout.ofSeconds(10))
+ .build())
+ .setDefaultRequestConfig(
+ RequestConfig.custom()
+ .setConnectTimeout(Timeout.ofSeconds(10))
+ .setConnectionRequestTimeout(Timeout.DISABLED)
+ .setResponseTimeout(Timeout.ofMinutes(5))
+ .build())
+ .setH2Config(H2Config.initial()
+ .setMaxConcurrentStreams(builder.maxStreamsPerConnection)
+ .setCompressionEnabled(true)
+ .setPushEnabled(false)
+ .build());
+
+ ClientTlsStrategyBuilder tlsStrategyBuilder = ClientTlsStrategyBuilder.create()
+ .setSslContext(constructSslContext(builder));
+ if (builder.hostnameVerifier != null) {
+ tlsStrategyBuilder.setHostnameVerifier(builder.hostnameVerifier);
+ }
+ return httpClientBuilder.setTlsStrategy(tlsStrategyBuilder.build())
+ .build();
+ }
+
+ private static SSLContext constructSslContext(FeedClientBuilder builder) throws IOException {
+ if (builder.sslContext != null) return builder.sslContext;
+ SslContextBuilder sslContextBuilder = new SslContextBuilder();
+ if (builder.certificate != null && builder.privateKey != null) {
+ sslContextBuilder.withCertificateAndKey(builder.certificate, builder.privateKey);
+ }
+ if (builder.caCertificates != null) {
+ sslContextBuilder.withCaCertificates(builder.caCertificates);
}
- httpClientBuilder.setConnectionManager(connectionManagerBuilder.build());
- return httpClientBuilder.build();
+ return sslContextBuilder.build();
}
@Override
diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java
index 652fea9b332..734c46a2819 100644
--- a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java
+++ b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java
@@ -75,6 +75,11 @@ public abstract class Maintainer implements Runnable {
}
}
+ /** Returns whether this is being shut down */
+ public final boolean shuttingDown() {
+ return shutDown.get();
+ }
+
@Override
public final String toString() { return name(); }
diff --git a/vespajlib/src/main/java/com/yahoo/lang/MutableBoolean.java b/vespajlib/src/main/java/com/yahoo/lang/MutableBoolean.java
index b009ffdd2b6..17501b17bd0 100644
--- a/vespajlib/src/main/java/com/yahoo/lang/MutableBoolean.java
+++ b/vespajlib/src/main/java/com/yahoo/lang/MutableBoolean.java
@@ -18,6 +18,10 @@ public class MutableBoolean {
public void set(boolean value) { this.value = value; }
+ public void andSet(boolean value) { this.value &= value; }
+
+ public void orSet(boolean value) { this.value |= value; }
+
@Override
public String toString() { return Boolean.toString(value); }
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java b/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java
index 9eb9cb06666..461e73e3611 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java
@@ -216,7 +216,7 @@ public class JsonFormat {
return result;
}
- private static double[] decodeHexString(String input, TensorType.Value valueType) {
+ public static double[] decodeHexString(String input, TensorType.Value valueType) {
switch(valueType) {
case INT8:
return decodeHexStringAsBytes(input);
diff --git a/zookeeper-server/zookeeper-server-3.6.2/CMakeLists.txt b/zookeeper-server/zookeeper-server-3.6.2/CMakeLists.txt
index c85a65e8e78..1167067ec73 100644
--- a/zookeeper-server/zookeeper-server-3.6.2/CMakeLists.txt
+++ b/zookeeper-server/zookeeper-server-3.6.2/CMakeLists.txt
@@ -1,4 +1,4 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
install_fat_java_artifact(zookeeper-server-3.6.2)
# Needs to be included when this is the wanted default version (and symlinks for other versions need to be removed)
-install_symlink(lib/jars/zookeeper-server-3.6.2-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)
+#install_symlink(lib/jars/zookeeper-server-3.6.2-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)
diff --git a/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt b/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt
index ddccb1f0dbc..af216e0e8c2 100644
--- a/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt
+++ b/zookeeper-server/zookeeper-server-3.6.3/CMakeLists.txt
@@ -1,4 +1,4 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
install_fat_java_artifact(zookeeper-server-3.6.3)
# Needs to be included when this is the wanted default version (and symlinks for other versions need to be removed)
-#install_symlink(lib/jars/zookeeper-server-3.6.3-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)
+install_symlink(lib/jars/zookeeper-server-3.6.3-jar-with-dependencies.jar lib/jars/zookeeper-server-jar-with-dependencies.jar)
diff --git a/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java b/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java
index 113669b2e76..c4062c64dd4 100644
--- a/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java
+++ b/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java
@@ -30,7 +30,7 @@ class VespaQuorumPeer extends QuorumPeerMain implements QuorumPeer {
@Override
public void shutdown(Duration timeout) {
if (quorumPeer != null) {
- log.log(Level.INFO, "Shutting down ZooKeeper server");
+ log.log(Level.FINE, "Shutting down ZooKeeper server");
try {
quorumPeer.shutdown();
quorumPeer.join(timeout.toMillis()); // Wait for shutdown to complete
@@ -43,7 +43,7 @@ class VespaQuorumPeer extends QuorumPeerMain implements QuorumPeer {
// server with the new config, this will fail until the old server is deconstructed. If the old server
// fails to deconstruct/shut down, the new one will never start and if that happens forcing a restart is
// the better option.
- Process.logAndDie("Failed to shut down ZooKeeper properly, forcing shutdown", e);
+ Process.logAndDie("Failed to shut down ZooKeeper server properly, forcing shutdown", e);
}
}
}
diff --git a/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java b/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java
index d92527fb5fd..b41b80d6f93 100644
--- a/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java
+++ b/zookeeper-server/zookeeper-server-3.6.3/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java
@@ -38,8 +38,7 @@ public class VespaZooKeeperAdminImpl implements VespaZooKeeperAdmin {
if (zooKeeperAdmin != null) {
try {
zooKeeperAdmin.close();
- } catch (InterruptedException e) {
- }
+ } catch (InterruptedException e) { /* ignore */}
}
}
}
diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java
index 9705c8c379e..863374f40f6 100644
--- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java
+++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Configurator.java
@@ -1,5 +1,4 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
package com.yahoo.vespa.zookeeper;
import com.yahoo.cloud.config.ZookeeperServerConfig;
@@ -75,8 +74,8 @@ public class Configurator {
sb.append("dataDir=").append(getDefaults().underVespaHome(config.dataDir())).append("\n");
sb.append("autopurge.purgeInterval=").append(config.autopurge().purgeInterval()).append("\n");
sb.append("autopurge.snapRetainCount=").append(config.autopurge().snapRetainCount()).append("\n");
- // See http://zookeeper.apache.org/doc/r3.5.5/zookeeperAdmin.html#sc_zkCommands
- // Includes all available commands in 3.5, except 'wchc' and 'wchp'
+ // See http://zookeeper.apache.org/doc/r3.6.3/zookeeperAdmin.html#sc_zkCommands
+ // Includes all available commands in 3.6, except 'wchc' and 'wchp'
sb.append("4lw.commands.whitelist=conf,cons,crst,dirs,dump,envi,mntr,ruok,srst,srvr,stat,wchs").append("\n");
sb.append("admin.enableServer=false").append("\n");
// Use custom connection factory for TLS on client port - see class' Javadoc for rationale
@@ -107,7 +106,7 @@ public class Configurator {
}
}
if (!found) {
- throw new RuntimeException("No id in zookeeper server list that corresponds to my id(" + myid + ")");
+ throw new RuntimeException("No id in zookeeper server list that corresponds to my id (" + myid + ")");
}
}
@@ -125,7 +124,7 @@ public class Configurator {
// actually make the server an observer, but prevent it from forming an ensemble independently of the
// existing cluster.
//
- // See https://zookeeper.apache.org/doc/r3.6.2/zookeeperReconfig.html#sc_reconfig_modifying
+ // See https://zookeeper.apache.org/doc/r3.6.3/zookeeperReconfig.html#sc_reconfig_modifying
sb.append(":")
.append("observer");
}
diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ReconfigException.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ReconfigException.java
index c4bb09eeedf..765bff474a8 100644
--- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ReconfigException.java
+++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ReconfigException.java
@@ -2,8 +2,8 @@
package com.yahoo.vespa.zookeeper;
/**
- * Exception used to wrap zookeeper exception when reconfiguration fails in a
- * class that can be used without depending on ZooKeeper.
+ * Exception used to wrap zookeeper exception when reconfiguration fails, done to be able
+ * to use a class that does not depend on ZooKeeper.
*
* @author hmusum
*/
diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java
index 9c2a543d2ef..f0318ddfcad 100644
--- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java
+++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/Reconfigurer.java
@@ -20,8 +20,8 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
- * Starts zookeeper server and supports reconfiguring zookeeper cluster. Created as a component
- * without any config injected, to make sure that it is not recreated when config changes.
+ * Starts zookeeper server and supports reconfiguring zookeeper cluster. Keep this as a component
+ * without injected config, to make sure that it is not recreated when config changes.
*
* @author hmusum
*/
@@ -48,7 +48,6 @@ public class Reconfigurer extends AbstractComponent {
Reconfigurer(VespaZooKeeperAdmin vespaZooKeeperAdmin, Sleeper sleeper) {
this.vespaZooKeeperAdmin = Objects.requireNonNull(vespaZooKeeperAdmin);
this.sleeper = Objects.requireNonNull(sleeper);
- log.log(Level.FINE, "Created ZooKeeperReconfigurer");
}
void startOrReconfigure(ZookeeperServerConfig newConfig, VespaZooKeeperServer server,
@@ -155,7 +154,7 @@ public class Reconfigurer extends AbstractComponent {
}
private static List<String> servers(ZookeeperServerConfig config) {
- // See https://zookeeper.apache.org/doc/r3.5.8/zookeeperReconfig.html#sc_reconfig_clientport for format
+ // See https://zookeeper.apache.org/doc/r3.6.3/zookeeperReconfig.html#sc_reconfig_clientport for format
return config.server().stream()
.map(server -> server.id() + "=" + server.hostname() + ":" + server.quorumPort() + ":" +
server.electionPort() + ";" + config.clientPort())
diff --git a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java
index 8c748250503..db8db1ac4b7 100644
--- a/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java
+++ b/zookeeper-server/zookeeper-server-common/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java
@@ -39,7 +39,7 @@ public class ZooKeeperRunner implements Runnable {
this.zookeeperServerConfig = zookeeperServerConfig;
this.server = server;
new Configurator(zookeeperServerConfig).writeConfigToDisk();
- executorService = Executors.newSingleThreadExecutor(new DaemonThreadFactory("zookeeper server"));
+ executorService = Executors.newSingleThreadExecutor(new DaemonThreadFactory("zookeeper-server-"));
executorService.submit(this);
}
@@ -66,14 +66,13 @@ public class ZooKeeperRunner implements Runnable {
Instant end = now.plus(START_TIMEOUT);
for (int attempt = 1; now.isBefore(end) && !executorService.isShutdown(); attempt++) {
try {
- log.log(Level.INFO, "Starting ZooKeeper server with config file " + path.toFile().getAbsolutePath() +
+ log.log(Level.INFO, "Starting ZooKeeper server with " + path.toFile().getAbsolutePath() +
". Trying to establish ZooKeeper quorum (members: " +
- zookeeperServerHostnames(zookeeperServerConfig) + ", attempt: " + attempt + ")");
+ zookeeperServerHostnames(zookeeperServerConfig) + ", attempt " + attempt + ")");
startServer(path); // Will block in a real implementation of VespaZooKeeperServer
return;
} catch (RuntimeException e) {
- String messagePart = "Starting " + serverDescription() + " failed on attempt " +
- attempt;
+ String messagePart = "Starting " + serverDescription() + " failed on attempt " + attempt;
if (server.reconfigurable()) {
Duration delay = backoff.delay(attempt);
log.log(Level.WARNING, messagePart + ". Retrying in " + delay + ", time left " +