summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/js/app/package.json3
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/AddPropertyButton.jsx46
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.jsx48
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.jsx25
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.jsx40
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/PasteJSONButton.jsx75
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/ShowQueryButton.jsx29
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.jsx9
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryContext.jsx14
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.jsx159
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Contexts/ResponseContext.jsx13
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.jsx37
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.jsx68
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/Info.jsx37
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.jsx67
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.jsx74
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx194
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/ResponseBox.jsx17
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/SendQuery.jsx108
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.jsx38
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.jsx34
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.jsx9
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.pngbin0 -> 29139 bytes
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.pngbin0 -> 19776 bytes
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/copy.svg7
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg1
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/features-help.pngbin0 -> 2554 bytes
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/information.svg10
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/paste.svg6
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/reload.svg6
-rw-r--r--client/js/app/src/app/pages/querybuilder/query-builder.jsx71
-rw-r--r--client/js/app/src/app/styles/agency.css895
-rw-r--r--client/js/app/src/app/styles/vespa.css752
-rw-r--r--client/js/app/yarn.lock142
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java20
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java49
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java15
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/Container.java13
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java26
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java28
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocprocOptionsBuilder.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/Content.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java21
-rw-r--r--config-model/src/main/resources/schema/docproc.rnc2
-rw-r--r--config-model/src/test/configmodel/types/documentmanager.cfg1
-rw-r--r--config-model/src/test/configmodel/types/documenttypes.cfg1
-rw-r--r--config-model/src/test/configmodel/types/documenttypes_with_doc_field.cfg1
-rw-r--r--config-model/src/test/configmodel/types/references/documentmanager_multiple_imported_fields.cfg1
-rw-r--r--config-model/src/test/configmodel/types/references/documentmanager_refs_to_other_types.cfg1
-rw-r--r--config-model/src/test/configmodel/types/references/documentmanager_refs_to_same_type.cfg1
-rw-r--r--config-model/src/test/configmodel/types/references/documenttypes_multiple_imported_fields.cfg1
-rw-r--r--config-model/src/test/configmodel/types/references/documenttypes_ref_to_self_type.cfg1
-rw-r--r--config-model/src/test/configmodel/types/references/documenttypes_refs_to_other_types.cfg1
-rw-r--r--config-model/src/test/configmodel/types/references/documenttypes_refs_to_same_type.cfg1
-rw-r--r--config-model/src/test/derived/advanced/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationsimplicitstruct/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationsinheritance/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationsinheritance2/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationspolymorphy/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationsreference/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationssimple/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationsstruct/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/annotationsstructarray/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/arrays/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/attributeprefetch/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/complex/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/declstruct/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/duplicate_struct/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/duplicate_struct/documenttypes.cfg1
-rw-r--r--config-model/src/test/derived/emptydefault/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/id/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/imported_fields_inherited_reference/documenttypes.cfg1
-rw-r--r--config-model/src/test/derived/indexswitches/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/inheritance/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/inheritdiamond/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/inheritfromgrandparent/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/inheritfromparent/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/inheritfromparent/documenttypes.cfg1
-rw-r--r--config-model/src/test/derived/mail/onlydoc/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/multi_struct/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/multi_struct/documenttypes.cfg1
-rw-r--r--config-model/src/test/derived/namecollision/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/prefixexactattribute/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/ranktypes/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/reference_from_several/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/schemainheritance/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/streamingstruct/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/structandfieldset/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/structanyorder/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/structinheritance/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/structinheritance/documenttypes.cfg1
-rw-r--r--config-model/src/test/derived/tensor/documentmanager.cfg1
-rw-r--r--config-model/src/test/derived/tensor/documenttypes.cfg1
-rw-r--r--config-model/src/test/derived/types/documentmanager.cfg1
-rw-r--r--config-model/src/test/examples/fieldoftypedocument-doctypes.cfg1
-rw-r--r--config-model/src/test/examples/fieldoftypedocument.cfg1
-rw-r--r--config-model/src/test/examples/structresult.cfg1
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java19
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/docproc/StandaloneDocprocContainerTest.java82
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/DocprocBuilderTest.java16
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java4
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java26
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java15
-rwxr-xr-xconfig/src/main/java/com/yahoo/config/subscription/ConfigGetter.java3
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java1
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/FunctionTest.java5
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java1
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java21
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java49
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java20
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java23
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java10
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java31
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java26
-rw-r--r--container-core/abi-spec.json2
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java22
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java28
-rw-r--r--container-core/src/main/resources/configdefinitions/container.jdisc.state.metrics-packets-handler.def2
-rw-r--r--container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def5
-rwxr-xr-xcontainer-core/src/main/sh/find-pid2
-rw-r--r--container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java1
-rw-r--r--container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java3
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java30
-rw-r--r--container-messagebus/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java10
-rw-r--r--container-messagebus/src/main/resources/configdefinitions/container.jdisc.container-mbus.def4
-rw-r--r--container-search/src/main/java/com/yahoo/search/handler/Json2SingleLevelMap.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java1
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java1
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParsingTester.java1
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java1
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java14
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/searcher/test/ValidateSortingSearcherTestCase.java16
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/semantics/test/ConfigurationTestCase.java7
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java27
-rw-r--r--container-search/src/test/java/com/yahoo/search/handler/Json2SinglelevelMapTestCase.java6
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/rewrite/test/QueryRewriteSearcherTestUtils.java1
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchers/ValidateFuzzySearcherTestCase.java34
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java11
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java18
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/TestReport.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java18
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java20
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java78
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java21
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java19
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java35
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java39
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainerTest.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java23
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java48
-rw-r--r--docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java1
-rw-r--r--document/src/vespa/document/config/documentmanager.def3
-rw-r--r--document/src/vespa/document/config/documenttypes.def3
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusVisitorSession.java5
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/EmptyFileReferenceData.java2
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java23
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java1
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceCompressor.java4
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceData.java6
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownload.java2
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java21
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyFileReferenceData.java4
-rw-r--r--filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyTemporaryStorageFileReferenceData.java4
-rw-r--r--filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileDownloaderTest.java29
-rw-r--r--filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileReferenceDataTest.java9
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java63
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java7
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java3
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetwork.cpp9
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetwork.h7
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp3
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcnetworkparams.h31
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsend.cpp19
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelTester.java4
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/evaluation/ModelsEvaluatorTest.java3
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/evaluation/OnnxEvaluatorTest.java1
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java1
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/handler/OnnxEvaluationHandlerTest.java1
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java21
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java36
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/History.java7
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/NodeListFilter.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/DelegatingOsUpgrader.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsUpgrader.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsVersions.java16
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java5
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java13
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json1
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java2
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java8
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java7
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java21
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ContentService.java14
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java3
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java31
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java27
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java22
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorTest.java3
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java15
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImplTest.java12
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java6
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java24
-rw-r--r--persistence/src/vespa/persistence/conformancetest/conformancetest.cpp4
-rw-r--r--persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp8
-rw-r--r--persistence/src/vespa/persistence/dummyimpl/dummypersistence.h2
-rw-r--r--persistence/src/vespa/persistence/spi/CMakeLists.txt1
-rw-r--r--persistence/src/vespa/persistence/spi/abstractpersistenceprovider.cpp4
-rw-r--r--persistence/src/vespa/persistence/spi/id_and_timestamp.cpp17
-rw-r--r--persistence/src/vespa/persistence/spi/id_and_timestamp.h38
-rw-r--r--persistence/src/vespa/persistence/spi/persistenceprovider.cpp4
-rw-r--r--persistence/src/vespa/persistence/spi/persistenceprovider.h3
-rwxr-xr-xscrewdriver/build-vespa.sh17
-rw-r--r--searchcore/src/tests/proton/docsummary/docsummary.cpp58
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/spi_bm_feed_handler.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp7
-rw-r--r--searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp16
-rw-r--r--searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h4
-rw-r--r--searchlib/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp13
-rw-r--r--searchlib/src/tests/features/nns_closeness/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/features/nns_closeness/nns_closeness_test.cpp102
-rw-r--r--searchlib/src/tests/features/nns_distance/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/features/nns_distance/nns_distance_test.cpp106
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp49
-rw-r--r--searchlib/src/vespa/searchlib/common/bitvector.h1
-rw-r--r--searchlib/src/vespa/searchlib/features/CMakeLists.txt3
-rw-r--r--searchlib/src/vespa/searchlib/features/closenessfeature.cpp51
-rw-r--r--searchlib/src/vespa/searchlib/features/closenessfeature.h1
-rw-r--r--searchlib/src/vespa/searchlib/features/distance_calculator_bundle.cpp172
-rw-r--r--searchlib/src/vespa/searchlib/features/distance_calculator_bundle.h59
-rw-r--r--searchlib/src/vespa/searchlib/features/distancefeature.cpp50
-rw-r--r--searchlib/src/vespa/searchlib/features/distancefeature.h1
-rw-r--r--searchlib/src/vespa/searchlib/fef/query_value.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/fef/query_value.h1
-rw-r--r--searchlib/src/vespa/searchlib/fef/test/labels.h2
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp15
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h5
-rw-r--r--searchlib/src/vespa/searchlib/tensor/distance_calculator.cpp47
-rw-r--r--searchlib/src/vespa/searchlib/tensor/distance_calculator.h16
-rw-r--r--searchlib/src/vespa/searchlib/test/CMakeLists.txt1
-rw-r--r--searchlib/src/vespa/searchlib/test/features/CMakeLists.txt7
-rw-r--r--searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.cpp39
-rw-r--r--searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.h75
-rw-r--r--searchsummary/src/tests/docsumformat/docsum-pack.cpp10
-rw-r--r--searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp6
-rw-r--r--searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp4
-rw-r--r--searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp4
-rw-r--r--searchsummary/src/tests/docsummary/positionsdfw_test.cpp2
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/CMakeLists.txt8
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.cpp1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.h1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.cpp6
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.h9
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp25
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h30
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/copy_dfw.cpp (renamed from searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.cpp)68
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/copy_dfw.h31
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsum_blob_entry_filter.h29
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.cpp27
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.h41
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.cpp11
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.h1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp11
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.h4
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h86
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.cpp46
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.h14
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp4
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.cpp19
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.h22
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/general_result.cpp16
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/general_result.h15
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/i_docsum_store_document.h3
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/juniperdfw.h6
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.cpp11
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.h18
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp5
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.h2
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.h6
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/res_type.h30
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.cpp29
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.h102
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/resultclass.cpp6
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/resultclass.h54
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/resultconfig.cpp23
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/resultconfig.h93
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/resultpacker.cpp1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/resultpacker.h14
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.cpp13
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.h20
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/struct_map_attribute_combiner_dfw.cpp1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.cpp1
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.h4
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp11
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.h2
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.cpp7
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.h8
-rw-r--r--storage/src/tests/persistence/common/persistenceproviderwrapper.cpp6
-rw-r--r--storage/src/tests/persistence/common/persistenceproviderwrapper.h2
-rw-r--r--storage/src/vespa/storage/config/stor-communicationmanager.def7
-rw-r--r--storage/src/vespa/storage/persistence/asynchandler.cpp4
-rw-r--r--storage/src/vespa/storage/persistence/mergehandler.cpp6
-rw-r--r--storage/src/vespa/storage/persistence/provider_error_wrapper.cpp2
-rw-r--r--storage/src/vespa/storage/persistence/provider_error_wrapper.h2
-rw-r--r--storage/src/vespa/storage/storageserver/communicationmanager.cpp3
-rw-r--r--streamingvisitors/src/vespa/vsm/vsm/docsumconfig.cpp10
-rw-r--r--streamingvisitors/src/vespa/vsm/vsm/docsumconfig.h2
-rw-r--r--streamingvisitors/src/vespa/vsm/vsm/docsumfilter.cpp10
-rw-r--r--streamingvisitors/src/vespa/vsm/vsm/vsm-adapter.cpp6
-rw-r--r--vdslib/src/test/java/com/yahoo/vdslib/distribution/DistributionTestFactory.java1
-rw-r--r--vespa-hadoop/pom.xml13
-rw-r--r--vespa-osgi-testrunner/pom.xml5
-rw-r--r--vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java200
-rw-r--r--vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/test/samples/SampleTest.java2
-rw-r--r--vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java9
-rw-r--r--vespa-osgi-testrunner/src/test/resources/output.json46
-rw-r--r--vespa-osgi-testrunner/src/test/resources/report.json4
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java2
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java2
-rw-r--r--vespaclient-core/src/main/java/com/yahoo/vespaclient/ClusterList.java1
351 files changed, 5571 insertions, 1710 deletions
diff --git a/client/js/app/package.json b/client/js/app/package.json
index 8ea59b0cc35..030fe818ba6 100644
--- a/client/js/app/package.json
+++ b/client/js/app/package.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"react": "^18",
+ "react-bootstrap": "^2.4.0",
"react-dom": "^18"
},
"devDependencies": {
@@ -33,7 +34,7 @@
"husky": "^7",
"prettier": "2",
"pretty-quick": "^3",
- "react-router-dom": "^6",
+ "react-router-dom": "^6.3.0",
"vite": "^2"
}
}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddPropertyButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddPropertyButton.jsx
new file mode 100644
index 00000000000..47b5a67875b
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddPropertyButton.jsx
@@ -0,0 +1,46 @@
+import React, { useContext, useState } from 'react';
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+import SimpleButton from './SimpleButton';
+
+export default function AddPropertyButton({ id }) {
+ const { inputs, setInputs, childMap } = useContext(QueryInputContext);
+ const [childId, setChildId] = useState(1);
+
+ /**
+ * Add a child to the input that has the provided id
+ */
+ const addChildProperty = () => {
+ const newInputs = inputs.slice();
+ let currentId = id.substring(0, 1);
+ let index = newInputs.findIndex((element) => element.id === currentId); //get the index of the root parent
+ let children = newInputs[index].children;
+ let parentType = newInputs[index].type;
+ for (let i = 3; i < id.length + 1; i += 2) {
+ currentId = id.substring(0, i);
+ index = children.findIndex((element) => element.id === currentId);
+ parentType = parentType + '_' + children[index].type;
+ children = children[index].children;
+ }
+ let type = childMap[parentType];
+ children.push({
+ id: id + '.' + childId,
+ type: type[Object.keys(type)[0]].name,
+ typeof: type[Object.keys(type)[0]].type,
+ input: '',
+ hasChildren: false,
+ children: [],
+ });
+ setInputs(newInputs);
+ setChildId((childId) => childId + 1);
+ };
+
+ return (
+ <SimpleButton
+ id={`propb${id}`}
+ className={'addpropsbutton'}
+ onClick={addChildProperty}
+ >
+ + Add property
+ </SimpleButton>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.jsx
new file mode 100644
index 00000000000..8aeaff971bf
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.jsx
@@ -0,0 +1,48 @@
+import React, { useContext } from 'react';
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
+import Tooltip from 'react-bootstrap/Tooltip';
+
+export default function AddQueryInput() {
+ const { inputs, setInputs, id, setId } = useContext(QueryInputContext);
+
+ /**
+ * Adds a new element to inputs.
+ * @param {Event} e Event that happened.
+ */
+ const updateInputs = (e) => {
+ e.preventDefault();
+ setId((id) => id + 1);
+ setInputs((prevInputs) => [
+ ...prevInputs,
+ {
+ id: `${id + 1}`,
+ type: 'yql',
+ typeof: 'String',
+ input: '',
+ hasChildren: false,
+ children: [],
+ },
+ ]);
+ };
+
+ return (
+ <OverlayTrigger
+ placement="right"
+ delay={{ show: 250, hide: 400 }}
+ overlay={<Tooltip id="button-tooltip">Add row</Tooltip>}
+ >
+ <span>
+ <button
+ id="addRow"
+ className="addRow"
+ height="0"
+ width="0"
+ onClick={updateInputs}
+ >
+ +
+ </button>
+ </span>
+ </OverlayTrigger>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.jsx
new file mode 100644
index 00000000000..f620146bea5
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+export default function ImageButton({
+ onClick,
+ children,
+ className,
+ id,
+ image,
+ height = '15',
+ width = '15',
+ style,
+}) {
+ return (
+ <button id={id} className={className} onClick={onClick}>
+ <img
+ src={image}
+ height={height}
+ width={width}
+ style={style}
+ alt="Missing"
+ />
+ {children}
+ </button>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.jsx
new file mode 100644
index 00000000000..2cb9c7d6e9a
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
+import Tooltip from 'react-bootstrap/Tooltip';
+import ImageButton from './ImageButton';
+
+export default function OverlayImageButton({
+ onClick,
+ children,
+ className,
+ id,
+ image,
+ height = '15',
+ width = '15',
+ style,
+ tooltip,
+}) {
+ return (
+ <OverlayTrigger
+ placement="right"
+ delay={{ show: 250, hide: 400 }}
+ overlay={<Tooltip id="button-tooltip">{tooltip}</Tooltip>}
+ >
+ <span>
+ <ImageButton
+ id={id}
+ className={className}
+ image={image}
+ height={height}
+ width={width}
+ style={style}
+ onClick={onClick}
+ >
+ {children}
+ </ImageButton>
+ </span>
+ </OverlayTrigger>
+ );
+}
+
+//
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/PasteJSONButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/PasteJSONButton.jsx
new file mode 100644
index 00000000000..73dce637500
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/PasteJSONButton.jsx
@@ -0,0 +1,75 @@
+import React, { useContext, useState } from 'react';
+import ImageButton from './ImageButton';
+import pasteImage from '../../assets/img/paste.svg';
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+
+export default function PasteJSONButton() {
+ const { inputs, setInputs, id, setId, levelZeroParameters, childMap } =
+ useContext(QueryInputContext);
+ const [paste, setPaste] = useState(false);
+
+ const handleClick = (e) => {
+ alert('Button is non-functional');
+ // setPaste(true);
+ // window.addEventListener('paste', handlePaste)
+ };
+
+ const handlePaste = (e) => {
+ setPaste(false);
+ const pastedData = e.clipboardData.getData('text');
+ alert('Converting JSON: \n\n ' + pastedData);
+ window.removeEventListener('paste', handlePaste);
+ convertPastedJSON(pastedData);
+ };
+
+ const convertPastedJSON = (pastedData) => {
+ try {
+ var json = JSON.parse(pastedData);
+ setId(1);
+ const newInputs = buildFromJSON(json, id);
+ setInputs(newInputs);
+ } catch (error) {
+ alert('Could not parse JSON, with error-message: \n\n' + error.message);
+ }
+ };
+
+ const buildFromJSON = (json, id) => {
+ let newInputs = [];
+ let keys = Object.keys(json);
+ for (let i = 0; i < keys.length; i++) {
+ let childId = 1;
+ let newInput = { id: id, type: keys[i] };
+ if (json[keys[i]] === Object) {
+ newInput['typeof'] = 'Parent';
+ newInput['input'] = '';
+ newInput['hasChildren'] = true;
+ let tempId = id + '.' + childId;
+ childId += 1;
+ newInput['children'] = buildFromJSON(json[keys[i]], tempId);
+ } else {
+ newInput['typeof'] = levelZeroParameters[keys[i]].type;
+ newInput['input'] = json[keys[i]];
+ newInput['hasChildren'] = false;
+ newInput['children'] = [];
+ }
+ setId(id + 1);
+ console.log(newInput);
+ newInputs.push(newInput);
+ }
+ return newInputs;
+ };
+
+ return (
+ <>
+ <ImageButton
+ id="pasteJSON"
+ className="pasteJSON"
+ image={pasteImage}
+ style={{ marginTop: '-2px', marginRight: '3px' }}
+ onClick={handleClick}
+ >
+ {paste ? 'Press CMD + V' : 'Paste JSON'}
+ </ImageButton>
+ </>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/ShowQueryButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ShowQueryButton.jsx
new file mode 100644
index 00000000000..789fc387b38
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ShowQueryButton.jsx
@@ -0,0 +1,29 @@
+import React, { useContext, useState } from 'react';
+import { QueryContext } from '../Contexts/QueryContext';
+import SimpleButton from './SimpleButton';
+
+export default function ShowQueryButton() {
+ const { query, showQuery, setShowQuery } = useContext(QueryContext);
+
+ const handleClick = () => {
+ setShowQuery(!showQuery);
+ };
+
+ return (
+ <>
+ <SimpleButton className="showJSON" onClick={handleClick}>
+ Show query JSON
+ </SimpleButton>
+ {showQuery && (
+ <textarea
+ id="jsonquery"
+ className="responsebox"
+ readOnly
+ cols="70"
+ rows="15"
+ value={query}
+ ></textarea>
+ )}
+ </>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.jsx b/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.jsx
new file mode 100644
index 00000000000..a153c9577e4
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+export default function SimpleButton({ onClick, children, className, id }) {
+ return (
+ <button id={id} className={className} onClick={onClick}>
+ {children}
+ </button>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryContext.jsx b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryContext.jsx
new file mode 100644
index 00000000000..00644c21d41
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryContext.jsx
@@ -0,0 +1,14 @@
+import React, { createContext, useState } from 'react';
+
+export const QueryContext = createContext();
+
+export const QueryProvider = (prop) => {
+ const [query, setQuery] = useState('');
+ const [showQuery, setShowQuery] = useState(false);
+
+ return (
+ <QueryContext.Provider value={{ query, setQuery, showQuery, setShowQuery }}>
+ {prop.children}
+ </QueryContext.Provider>
+ );
+};
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.jsx b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.jsx
new file mode 100644
index 00000000000..064bf51795b
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.jsx
@@ -0,0 +1,159 @@
+import React, { useState, createContext } from 'react';
+
+export const QueryInputContext = createContext();
+
+export const QueryInputProvider = (prop) => {
+ // This is the id of the newest QueryInput, gets updated each time a new one is added
+ const [id, setId] = useState(1);
+
+ // These are the methods that can be chosen in a QueryInput
+ const levelZeroParameters = {
+ yql: { name: 'yql', type: 'String', hasChildren: false },
+ hits: { name: 'hits', type: 'Integer', hasChildren: false },
+ offset: { name: 'offset', type: 'Integer', hasChildren: false },
+ queryProfile: { name: 'queryProfile', type: 'String', hasChildren: false },
+ noCache: { name: 'noCache', type: 'Boolean', hasChildren: false },
+ groupingSessionCache: {
+ name: 'groupingSessionCache',
+ type: 'Boolean',
+ hasChildren: false,
+ },
+ searchChain: { name: 'searchChain', type: 'String', hasChildren: false },
+ timeout: { name: 'timeout', type: 'Float', hasChildren: false },
+ tracelevel: { name: 'tracelevel', type: 'Parent', hasChildren: true },
+ traceLevel: { name: 'traceLevel', type: 'Integer', hasChildren: false },
+ explainLevel: { name: 'explainLevel', type: 'Integer', hasChildren: false },
+ explainlevel: { name: 'explainlevel', type: 'Integer', hasChildren: false },
+ model: { name: 'model', type: 'Parent', hasChildren: true },
+ ranking: { name: 'ranking', type: 'Parent', hasChildren: true },
+ collapse: { name: 'collapse', type: 'Parent', hasChildren: true },
+ collapsesize: { name: 'collapsesize', type: 'Integer', hasChildren: false },
+ collapsefield: {
+ name: 'collapsefield',
+ type: 'String',
+ hasChildren: false,
+ },
+ presentation: { name: 'presentation', type: 'Parent', hasChildren: true },
+ pos: { name: 'pos', type: 'Parent', hasChildren: true },
+ streaming: { name: 'streaming', type: 'Parent', hasChildren: true },
+ rules: { name: 'rules', type: 'Parent', hasChildren: true },
+ recall: { name: 'recall', type: 'List', hasChildren: false },
+ user: { name: 'user', type: 'String', hasChildren: false },
+ metrics: { name: 'metrics', type: 'Parent', hasChildren: true },
+ };
+
+ // Children of the levelZeroParameters that have child attributes
+ const childMap = {
+ collapse: {
+ summary: { name: 'summary', type: 'String', hasChildren: false },
+ },
+ metrics: {
+ ignore: { name: 'ignore', type: 'Boolean', hasChildren: false },
+ },
+ model: {
+ defaultIndex: {
+ name: 'defaultIndex',
+ type: 'String',
+ hasChildren: false,
+ },
+ encoding: { name: 'encoding', type: 'String', hasChildren: false },
+ language: { name: 'language', type: 'String', hasChildren: false },
+ queryString: { name: 'queryString', type: 'String', hasChildren: false },
+ restrict: { name: 'restrict', type: 'List', hasChildren: false },
+ searchPath: { name: 'searchPath', type: 'String', hasChildren: false },
+ sources: { name: 'sources', type: 'List', hasChildren: false },
+ type: { name: 'type', type: 'String', hasChildren: false },
+ },
+ pos: {
+ ll: { name: 'll', type: 'String', hasChildren: false },
+ radius: { name: 'radius', type: 'String', hasChildren: false },
+ bb: { name: 'bb', type: 'List', hasChildren: false },
+ attribute: { name: 'attribute', type: 'String', hasChildren: false },
+ },
+ presentation: {
+ bolding: { name: 'bolding', type: 'Boolean', hasChildren: false },
+ format: { name: 'format', type: 'String', hasChildren: false },
+ summary: { name: 'summary', type: 'String', hasChildren: false },
+ template: { name: 'template', type: 'String', hasChildren: false },
+ timing: { name: 'timing', type: 'Boolean', hasChildren: false },
+ },
+ ranking: {
+ location: { name: 'location', type: 'String', hasChildren: false },
+ features: { name: 'features', type: 'String', hasChildren: false },
+ listFeatures: {
+ name: 'listFeatures',
+ type: 'Boolean',
+ hasChildren: false,
+ },
+ profile: { name: 'profile', type: 'String', hasChildren: false },
+ properties: { name: 'properties', type: 'String', hasChildren: false },
+ sorting: { name: 'sorting', type: 'String', hasChildren: false },
+ freshness: { name: 'freshness', type: 'String', hasChildren: false },
+ queryCache: { name: 'queryCache', type: 'Boolean', hasChildren: false },
+ matchPhase: { name: 'matchPhase', type: 'Parent', hasChildren: true },
+ },
+ ranking_matchPhase: {
+ maxHits: { name: 'maxHits', type: 'Long', hasChildren: false },
+ attribute: { name: 'attribute', type: 'String', hasChildren: false },
+ ascending: { name: 'ascending', type: 'Boolean', hasChildren: false },
+ diversity: { name: 'diversity', type: 'Parent', hasChildren: true },
+ },
+ ranking_matchPhase_diversity: {
+ attribute: { name: 'attribute', type: 'String', hasChildren: false },
+ minGroups: { name: 'minGroups', type: 'Long', hasChildren: false },
+ },
+ rules: {
+ off: { name: 'off', type: 'Boolean', hasChildren: false },
+ rulebase: { name: 'rulebase', type: 'String', hasChildren: false },
+ },
+ streaming: {
+ userid: { name: 'userid', type: 'Integer', hasChildren: false },
+ groupname: { name: 'groupname', type: 'String', hasChildren: false },
+ selection: { name: 'selection', type: 'String', hasChildren: false },
+ priority: { name: 'priority', type: 'String', hasChildren: false },
+ maxbucketspervisitor: {
+ name: 'maxbucketspervisitor',
+ type: 'Integer',
+ hasChildren: false,
+ },
+ },
+ trace: {
+ timestamps: { name: 'timestamps', type: 'Boolean', hasChildren: false },
+ },
+ tracelevel: {
+ rules: { name: 'rules', type: 'Integer', hasChildren: false },
+ },
+ };
+
+ const firstChoice = levelZeroParameters[Object.keys(levelZeroParameters)[0]];
+
+ const [inputs, setInputs] = useState([
+ {
+ id: '1',
+ type: firstChoice.name,
+ typeof: firstChoice.type,
+ input: '',
+ hasChildren: false,
+ children: [],
+ },
+ ]);
+
+ const [selectedItems, setSelectedItems] = useState([]);
+
+ return (
+ <QueryInputContext.Provider
+ value={{
+ inputs,
+ setInputs,
+ id,
+ setId,
+ levelZeroParameters,
+ childMap,
+ selectedItems,
+ setSelectedItems,
+ }}
+ >
+ {prop.children}
+ </QueryInputContext.Provider>
+ );
+};
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Contexts/ResponseContext.jsx b/client/js/app/src/app/pages/querybuilder/Components/Contexts/ResponseContext.jsx
new file mode 100644
index 00000000000..54f5fc955fd
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Contexts/ResponseContext.jsx
@@ -0,0 +1,13 @@
+import React, { createContext, useState } from 'react';
+
+export const ResponseContext = createContext();
+
+export const ResponseProvider = (prop) => {
+ const [response, setResponse] = useState('');
+
+ return (
+ <ResponseContext.Provider value={{ response, setResponse }}>
+ {prop.children}
+ </ResponseContext.Provider>
+ );
+};
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.jsx b/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.jsx
new file mode 100644
index 00000000000..6ffeae4caed
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.jsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import { Navbar, Container, Nav } from 'react-bootstrap';
+
+function CustomNavbar() {
+ return (
+ <Navbar collapseOnSelect variant="default" expand="sm">
+ <Container>
+ <Navbar.Brand href="https://vespa.ai">
+ Vespa. Big data. Real time.
+ </Navbar.Brand>
+ <Navbar.Toggle aria-controls="responsive-navbar-nav" />
+ <Navbar.Collapse id="responsive-navbar-nav">
+ <Nav>
+ <Nav.Link href="https://blog.vespa.ai/">Blog</Nav.Link>
+ <Nav.Link variant="link" href="https://twitter.com/vespaengine">
+ Twitter
+ </Nav.Link>
+ <Nav.Link variant="link" href="https://docs.vespa.ai">
+ Docs
+ </Nav.Link>
+ <Nav.Link variant="link" href="https://github.com/vespa-engine">
+ GitHub
+ </Nav.Link>
+ <Nav.Link
+ variant="link"
+ href="https://docs.vespa.ai/en/getting-started.html"
+ >
+ Get Started Now
+ </Nav.Link>
+ </Nav>
+ </Navbar.Collapse>
+ </Container>
+ </Navbar>
+ );
+}
+
+export default CustomNavbar;
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.jsx b/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.jsx
new file mode 100644
index 00000000000..b110bce943d
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.jsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { Table } from 'react-bootstrap';
+
+export default function Footer() {
+ return (
+ <footer>
+ <Table borderless size="sm">
+ <thead className="footer-title">
+ <tr>
+ <th>Resources</th>
+ <th>Contact</th>
+ <th>Community</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th>
+ <a href="https://docs.vespa.ai/en/vespa-quick-start.html">
+ Getting Started
+ </a>
+ </th>
+ <th>
+ <a href="https://twitter.com/vespaengine">Twitter</a>
+ </th>
+ <th>
+ <a href="https://github.com/vespa-engine/vespa/blob/master/CONTRIBUTING.md">
+ Contributing
+ </a>
+ </th>
+ </tr>
+ <tr>
+ <th>
+ <a href="https://docs.vespa.ai">Documentation</a>
+ </th>
+ <th>
+ <a href="mailto:info@vespa.ai">info@vespa.ai</a>
+ </th>
+ <th>
+ <a href="https://stackoverflow.com/questions/tagged/vespa">
+ Stack Overflow
+ </a>
+ </th>
+ </tr>
+ <tr>
+ <th>
+ <a href="https://github.com/vespa-engine/vespa">Open source</a>
+ </th>
+ <th>
+ <a href="https://github.com/vespa-engine/vespa/issues">Issues</a>
+ </th>
+ <th>
+ <a href="https://gitter.im/vespa-engine/Lobby">Gitter</a>
+ </th>
+ </tr>
+ </tbody>
+ </Table>
+ <div className="credits">
+ <span>Copyright Yahoo</span>
+ Licensed under{' '}
+ <a href="https://github.com/vespa-engine/vespa/blob/master/LICENSE">
+ Apache License 2.0
+ </a>
+ , <a href="https://github.com/y7kim/agency-jekyll-theme">Theme</a> by
+ Rick K.
+ </div>
+ </footer>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/Info.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/Info.jsx
new file mode 100644
index 00000000000..d921c53a602
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/Info.jsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import { OverlayTrigger, Popover } from 'react-bootstrap';
+import image from '../../assets/img/information.svg';
+
+export default function Info({
+ id,
+ className = 'tip',
+ height = 15,
+ width = 15,
+}) {
+ const popover = (
+ <Popover id={`inf${id}`}>
+ <Popover.Header as="h3">Popover right</Popover.Header>
+ <Popover.Body>Content</Popover.Body>
+ </Popover>
+ );
+
+ return (
+ <>
+ <OverlayTrigger
+ placement="right"
+ delay={{ show: 250, hide: 400 }}
+ overlay={popover}
+ >
+ <span>
+ <img
+ src={image}
+ height={height}
+ width={width}
+ className="information"
+ alt="Missing"
+ />
+ </span>
+ </OverlayTrigger>
+ </>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.jsx
new file mode 100644
index 00000000000..d9a3417752c
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.jsx
@@ -0,0 +1,67 @@
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+import React, { useCallback, useContext, useState } from 'react';
+import SimpleDropDownForm from './SimpleDropDownForm';
+
+export default function QueryDropdownForm({ choices, id, child = false }) {
+ const {
+ inputs,
+ setInputs,
+ levelZeroParameters,
+ childMap,
+ selectedItems,
+ setSelectedItems,
+ } = useContext(QueryInputContext);
+ const [choice, setChoice] = useState();
+
+ /**
+ * Update the state of inputs to reflect the method chosen from the dropdown.
+ * If the prevoiusly chosen method had children they are removed.
+ * @param {Event} e Event containing the new type.
+ */
+ const updateType = (e) => {
+ e.preventDefault();
+ const newType = e.target.value;
+ const newInputs = inputs.slice();
+ let currentId = id.substring(0, 1);
+ let index = newInputs.findIndex((element) => element.id === currentId);
+ if (child) {
+ let parentTypes = newInputs[index].type;
+ let children = newInputs[index].children;
+ let childChoices = childMap[parentTypes];
+ for (let i = 3; i < id.length; i += 2) {
+ currentId = id.substring(0, i);
+ index = children.findIndex((element) => element.id === currentId);
+ let child = children[index];
+ parentTypes = parentTypes + '_' + child.type;
+ childChoices = childMap[parentTypes];
+ children = child.children;
+ }
+ index = children.findIndex((element) => element.id === id);
+ children[index].type = newType;
+ children[index].hasChildren = childChoices[newType].hasChildren;
+ children[index].children = [];
+ children[index].typeof = childChoices[newType].type;
+ setSelectedItems([...selectedItems, newType]);
+ } else {
+ newInputs[index].type = newType;
+ let hasChildren = levelZeroParameters[newType].hasChildren;
+ newInputs[index].hasChildren = hasChildren;
+ newInputs[index].children = [];
+ newInputs[index].typeof = levelZeroParameters[newType].type;
+ setSelectedItems([...selectedItems, newType]);
+ }
+ setInputs(newInputs);
+ setChoice(newType);
+ };
+
+ //TODO: do not display options that have been chosen
+
+ return (
+ <SimpleDropDownForm
+ id={id}
+ onChange={updateType}
+ choices={choices}
+ value={choice}
+ ></SimpleDropDownForm>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.jsx
new file mode 100644
index 00000000000..c5410ae7c4a
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.jsx
@@ -0,0 +1,74 @@
+import React, { useContext } from 'react';
+import SimpleButton from '../Buttons/SimpleButton';
+import SimpleForm from './SimpleForm';
+import { OverlayTrigger, Tooltip } from 'react-bootstrap';
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+import QueryDropdownForm from './QueryDropDownForm';
+import AddPropertyButton from '../Buttons/AddPropertyButton';
+import QueryInputChild from './QueryInputChild';
+
+export default function QueryInput() {
+ const { inputs, setInputs, levelZeroParameters, childMap } =
+ useContext(QueryInputContext);
+
+ function removeRow(id) {
+ const newList = inputs.filter((item) => item.id !== id);
+ setInputs(newList);
+ }
+
+ const updateInput = (e) => {
+ e.preventDefault();
+ const fid = e.target.id.replace('v', '');
+ const newInputs = inputs.slice();
+ const index = newInputs.findIndex((element) => element.id === fid);
+ newInputs[index].input = e.target.value;
+ setInputs(newInputs);
+ };
+
+ const setPlaceholder = (id) => {
+ try {
+ const index = inputs.findIndex((element) => element.id === id);
+ return inputs[index].typeof;
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ const inputList = inputs.map((value) => {
+ return (
+ <div key={value.id} id={value.id} className="queryinput">
+ <QueryDropdownForm choices={levelZeroParameters} id={value.id} />
+ {value.hasChildren ? (
+ <>
+ <AddPropertyButton id={value.id} />
+ <QueryInputChild id={value.id} />
+ </>
+ ) : (
+ <SimpleForm
+ id={`v${value.id}`}
+ size="30"
+ onChange={updateInput}
+ placeholder={setPlaceholder(value.id)}
+ />
+ )}
+ <OverlayTrigger
+ placement="right"
+ delay={{ show: 250, hide: 400 }}
+ overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>}
+ >
+ <span>
+ <SimpleButton
+ id={`b${value.id}`}
+ className="removeRow"
+ onClick={() => removeRow(value.id)}
+ children="-"
+ ></SimpleButton>
+ </span>
+ </OverlayTrigger>
+ <br />
+ </div>
+ );
+ });
+
+ return <>{inputList}</>;
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx
new file mode 100644
index 00000000000..0440d6ef1ba
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx
@@ -0,0 +1,194 @@
+import React, { useContext } from 'react';
+import AddPropertyButton from '../Buttons/AddPropertyButton';
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+import QueryDropdownForm from './QueryDropDownForm';
+import SimpleForm from './SimpleForm';
+import { OverlayTrigger, Tooltip } from 'react-bootstrap';
+import SimpleButton from '../Buttons/SimpleButton';
+
+export default function QueryInputChild({ id }) {
+ const { inputs, setInputs, childMap } = useContext(QueryInputContext);
+
+ let index = inputs.findIndex((element) => element.id === id);
+ let childArray = inputs[index].children;
+ let currentTypes = inputs[index].type;
+
+ /**
+ * Update the state of inputs to reflect what is written into the form.
+ * @param {Event} e Event containing the new input.
+ */
+ const updateInput = (e) => {
+ e.preventDefault();
+ let newInputs = inputs.slice();
+ let iterId = e.target.id.replace('v', '');
+ let currentId = iterId.substring(0, 1);
+ let index = newInputs.findIndex((element) => element.id === currentId);
+ let children = newInputs[index].children;
+ const traversedChildren = traverseChildren(iterId, children, '');
+ children = traversedChildren.children;
+ index = children.findIndex((element) => element.id === iterId);
+ children[index].input = e.target.value;
+ setInputs(newInputs);
+ };
+
+ /**
+ * Returns a placeholder text for a SimpleForm component.
+ * @param {String} id The id of the SimpleForm component.
+ * @returns {String} The placeholder text
+ */
+ const setPlaceHolder = (id) => {
+ let currentId = id.substring(0, 1);
+ let index = inputs.findIndex((element) => element.id === currentId);
+ let combinedType = inputs[index].type;
+ let children = inputs[index].children;
+ if (id.length > 3) {
+ const traversedChildren = traverseChildren(id, children, combinedType);
+ combinedType = traversedChildren.combinedType;
+ children = traversedChildren.children;
+ const currentChoice = childMap[combinedType];
+ index = children.findIndex((element) => element.id === id);
+ combinedType = children[index].type;
+ return currentChoice[combinedType].type;
+ } else {
+ const currentChoice = childMap[combinedType];
+ index = children.findIndex((element) => element.id === id);
+ combinedType = children[index].type;
+ return currentChoice[combinedType].type;
+ }
+ };
+
+ /**
+ * Removes the row with the provided id.
+ * @param {String} id Id of row.
+ */
+ const removeRow = (id) => {
+ let newInputs = inputs.slice();
+ let currentId = id.substring(0, 1);
+ let index = newInputs.findIndex((element) => element.id === currentId);
+ let children = newInputs[index].children;
+ const traversedChildren = traverseChildren(id, children, '');
+ index = traversedChildren.children.findIndex((element) => element === id);
+ traversedChildren.children.splice(index, 1);
+ setInputs(newInputs);
+ };
+
+ /**
+ * Traverses the children until a child with the provided id is reached.
+ * @param {String} id Id of the innermost child.
+ * @param {Array} children Array containing serveral child objects.
+ * @param {String} combinedType The combined type of all traversed children
+ * @returns {Object} An object containing the children of the child with the provided id and the combined type.
+ */
+ function traverseChildren(id, children, combinedType) {
+ let currentId;
+ let index;
+ for (let i = 3; i < id.length; i += 2) {
+ currentId = id.substring(0, i);
+ index = children.findIndex((element) => element.id === currentId);
+ combinedType = combinedType + '_' + children[index].type;
+ children = children[index].children;
+ }
+ return { children: children, combinedType: combinedType };
+ }
+
+ const inputList = childArray.map((child) => {
+ return (
+ <div key={child.id} id={child.id}>
+ <QueryDropdownForm
+ choices={childMap[currentTypes]}
+ id={child.id}
+ child={true}
+ />
+ {child.hasChildren ? (
+ <>
+ <AddPropertyButton id={child.id} />
+ </>
+ ) : (
+ <SimpleForm
+ id={`v${child.id}`}
+ size="30"
+ onChange={updateInput}
+ placeholder={setPlaceHolder(child.id)}
+ />
+ )}
+ <OverlayTrigger
+ placement="right"
+ delay={{ show: 250, hide: 400 }}
+ overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>}
+ >
+ <span>
+ <SimpleButton
+ id={`b${child.id}`}
+ className="removeRow"
+ onClick={() => removeRow(child.id)}
+ children="-"
+ ></SimpleButton>
+ </span>
+ </OverlayTrigger>
+ <br />
+ <Child
+ type={currentTypes + '_' + child.type}
+ child={child}
+ onChange={updateInput}
+ placeholder={setPlaceHolder}
+ removeRow={removeRow}
+ />
+ </div>
+ );
+ });
+
+ return <>{inputList}</>;
+}
+
+function Child({ child, type, onChange, placeholder, removeRow }) {
+ const { childMap } = useContext(QueryInputContext);
+
+ const nestedChildren = (child.children || []).map((child) => {
+ return (
+ <div key={child.id}>
+ <QueryDropdownForm
+ choices={childMap[type]}
+ id={child.id}
+ child={true}
+ />
+ {child.hasChildren ? (
+ <>
+ <AddPropertyButton id={child.id} />
+ </>
+ ) : (
+ <SimpleForm
+ id={`v${child.id}`}
+ size="30"
+ onChange={onChange}
+ placeholder={placeholder(child.id)}
+ />
+ )}
+ <OverlayTrigger
+ placement="right"
+ delay={{ show: 250, hide: 400 }}
+ overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>}
+ >
+ <span>
+ <SimpleButton
+ id={`b${child.id}`}
+ className="removeRow"
+ onClick={() => removeRow(child.id)}
+ children="-"
+ ></SimpleButton>
+ </span>
+ </OverlayTrigger>
+ <br />
+ <Child
+ child={child}
+ id={child.id}
+ type={type + '_' + child.type}
+ onChange={onChange}
+ placeholder={placeholder}
+ removeRow={removeRow}
+ />
+ </div>
+ );
+ });
+
+ return <>{nestedChildren}</>;
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/ResponseBox.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/ResponseBox.jsx
new file mode 100644
index 00000000000..08c65238434
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/ResponseBox.jsx
@@ -0,0 +1,17 @@
+import React, { useContext, useEffect, useState } from 'react';
+import { ResponseContext } from '../Contexts/ResponseContext';
+
+export default function ResponseBox() {
+ const { response } = useContext(ResponseContext);
+
+ return (
+ <textarea
+ id="responsetext"
+ className="responsebox"
+ readOnly
+ cols="70"
+ rows="25"
+ value={response}
+ />
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SendQuery.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/SendQuery.jsx
new file mode 100644
index 00000000000..9ee370de7c9
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SendQuery.jsx
@@ -0,0 +1,108 @@
+import React, { useContext, useEffect, useState } from 'react';
+import SimpleDropDownForm from './SimpleDropDownForm';
+import SimpleButton from '../Buttons/SimpleButton';
+import SimpleForm from './SimpleForm';
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+import { ResponseContext } from '../Contexts/ResponseContext';
+import { QueryContext } from '../Contexts/QueryContext';
+
+export default function SendQuery() {
+ const { inputs } = useContext(QueryInputContext);
+ const { setResponse } = useContext(ResponseContext);
+ const { showQuery, setQuery } = useContext(QueryContext);
+
+ const messageMethods = { post: { name: 'POST' }, get: { name: 'GET' } };
+ const [method, setMethod] = useState(messageMethods.post.name);
+ const [url, setUrl] = useState('http://localhost:8080/search/');
+
+ useEffect(() => {
+ const query = buildJSON(inputs, {});
+ setQuery(JSON.stringify(query, undefined, 4));
+ }, [showQuery]);
+
+ const updateMethod = (e) => {
+ e.preventDefault();
+ const newMethod = e.target.value;
+ setMethod(newMethod);
+ };
+
+ function handleClick() {
+ const json = buildJSON(inputs, {});
+ send(json);
+ }
+
+ async function send(json) {
+ let responses = await fetch(url, {
+ method: method,
+ headers: { 'Content-Type': 'application/json;charset=utf-8' },
+ body: JSON.stringify(json),
+ });
+ if (responses.ok) {
+ let result = await responses.json();
+ let resultObject = JSON.stringify(result, undefined, 4);
+ setResponse(resultObject);
+ }
+ }
+
+ function buildJSON(inputs, json) {
+ let queryJson = json;
+ for (let i = 0; i < inputs.length; i++) {
+ let current = inputs[i];
+ let key = current.type;
+ if (current.hasChildren) {
+ let child = {};
+ child = buildJSON(current.children, child);
+ queryJson[key] = child;
+ } else {
+ queryJson[key] = parseInput(current.input, current.typeof);
+ }
+ }
+ return queryJson;
+ }
+
+ function parseInput(input, type) {
+ switch (type) {
+ case 'Integer':
+ case 'Long':
+ return parseInt(input);
+ break;
+
+ case 'Float':
+ return parseFloat(input);
+ break;
+
+ case 'Boolean':
+ return input.toLowerCase() === 'true' ? true : false;
+ break;
+
+ default:
+ return input;
+ }
+ }
+
+ const updateUrl = (e) => {
+ const newUrl = e.target.value;
+ setUrl(newUrl);
+ };
+
+ return (
+ <>
+ <SimpleDropDownForm
+ choices={messageMethods}
+ id="method"
+ className="methodselector"
+ onChange={updateMethod}
+ />
+ <SimpleForm
+ id="url"
+ className="textbox"
+ initial={url}
+ size="30"
+ onChange={updateUrl}
+ />
+ <SimpleButton id="send" className="button" onClick={handleClick}>
+ Send
+ </SimpleButton>
+ </>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.jsx
new file mode 100644
index 00000000000..01288cea44f
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.jsx
@@ -0,0 +1,38 @@
+import React, { useContext, useEffect } from 'react';
+import { QueryInputContext } from '../Contexts/QueryInputContext';
+
+export default function SimpleDropDownForm({
+ choices,
+ id,
+ className = 'input',
+ onChange,
+ value,
+}) {
+ const { selectedItems } = useContext(QueryInputContext);
+
+ //FIXME: using the filtered list to render options results in dropdown not changing the displayed selection to what was actually selected.
+ let filtered = Object.keys(choices).filter(
+ (choice) => !selectedItems.includes(choice)
+ );
+ useEffect(() => {
+ filtered = Object.keys(choices).filter(
+ (choice) => !selectedItems.includes(choice)
+ );
+ }, [selectedItems]);
+
+ const options = Object.keys(choices).map((choice) => {
+ return (
+ <option className="options" key={choice} value={choices[choice].name}>
+ {choices[choice].name}
+ </option>
+ );
+ });
+
+ return (
+ <form id={id}>
+ <select className={className} id={id} value={value} onChange={onChange}>
+ {options}
+ </select>
+ </form>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.jsx
new file mode 100644
index 00000000000..bb6aaa13529
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { useState } from 'react';
+
+export default function SimpleForm({
+ id,
+ className = 'propvalue',
+ initial,
+ size = '20',
+ onChange,
+ placeholder,
+}) {
+ SimpleForm.defaultProps = {
+ onChange: handleChange,
+ };
+ const [input, setValue] = useState(initial);
+
+ function handleChange(e) {
+ setValue(e.target.value);
+ }
+
+ return (
+ <form className={className} id={id}>
+ <input
+ size={size}
+ type="text"
+ id={id}
+ className={className}
+ defaultValue={initial}
+ onChange={onChange}
+ placeholder={placeholder}
+ />
+ </form>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.jsx b/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.jsx
new file mode 100644
index 00000000000..022b250da7c
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+export default function TextBox({ id, className, children }) {
+ return (
+ <p className={className} id={id}>
+ {children}
+ </p>
+ );
+}
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png b/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png
new file mode 100644
index 00000000000..ac87f8e94d0
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png
Binary files differ
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png b/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png
new file mode 100644
index 00000000000..33063432c20
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png
Binary files differ
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg b/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg
new file mode 100644
index 00000000000..eada154413a
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 488.3 488.3" style="enable-background:new 0 0 488.3 488.3;" xml:space="preserve" width="512px" height="512px" class=""><g><g>
+ <g>
+ <path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/>
+ <path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg b/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg
new file mode 100644
index 00000000000..d78d2f9c17f
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36"><defs><style>.cls-1{clip-path:url(#clip-path);}.cls-2{clip-path:url(#clip-path-2);}.cls-3{clip-path:url(#clip-path-3);}</style><clipPath id="clip-path"><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/></clipPath><clipPath id="clip-path-2"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/></clipPath><clipPath id="clip-path-3"><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/></clipPath></defs><title>down-arrow</title><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/><g class="cls-1"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="-4.15" y="-4.15" width="44.3" height="44.3" fill="#FFFFFF"/></g></g><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/><g class="cls-3"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="4.35" y="8.21" width="27.3" height="21.12" fill="#FFFFFF"/></g></g></svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png b/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png
new file mode 100644
index 00000000000..65702f8b91f
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png
Binary files differ
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/information.svg b/client/js/app/src/app/pages/querybuilder/assets/img/information.svg
new file mode 100644
index 00000000000..da42cf2caf6
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/information.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px"><g><g>
+ <g>
+ <g>
+ <circle cx="256" cy="378.5" r="25" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ <path d="M256,0C114.516,0,0,114.497,0,256c0,141.484,114.497,256,256,256c141.484,0,256-114.497,256-256 C512,114.516,397.503,0,256,0z M256,472c-119.377,0-216-96.607-216-216c0-119.377,96.607-216,216-216 c119.377,0,216,96.607,216,216C472,375.377,375.393,472,256,472z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ <path d="M256,128.5c-44.112,0-80,35.888-80,80c0,11.046,8.954,20,20,20s20-8.954,20-20c0-22.056,17.944-40,40-40 c22.056,0,40,17.944,40,40c0,22.056-17.944,40-40,40c-11.046,0-20,8.954-20,20v50c0,11.046,8.954,20,20,20 c11.046,0,20-8.954,20-20v-32.531c34.466-8.903,60-40.26,60-77.469C336,164.388,300.112,128.5,256,128.5z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ </g>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg b/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg
new file mode 100644
index 00000000000..b2edac680bf
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 561 561" style="enable-background:new 0 0 561 561;" xml:space="preserve" class=""><g><g>
+ <g id="content-paste">
+ <path d="M459,51H351.9c-10.2-30.6-38.25-51-71.4-51c-33.15,0-61.2,20.4-71.4,51H102c-28.05,0-51,22.95-51,51v408 c0,28.05,22.95,51,51,51h357c28.05,0,51-22.95,51-51V102C510,73.95,487.05,51,459,51z M280.5,51c15.3,0,25.5,10.2,25.5,25.5 S295.8,102,280.5,102S255,91.8,255,76.5S265.2,51,280.5,51z M459,510H102V102h51v76.5h255V102h51V510z" data-original="#000000" class="active-path" data-old_color="#F9F6F6" fill="#FBF9F9"/>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg b/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg
new file mode 100644
index 00000000000..c5381f9f232
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px" class=""><g><g>
+ <g>
+ <path d="M482.195,226.196C482.195,101.471,380.725,0,256.001,0S29.805,101.471,29.805,226.196c0,7.409,6.007,13.416,13.416,13.416 s13.416-6.008,13.416-13.416c0-109.93,89.434-199.363,199.363-199.363s199.363,89.434,199.363,199.363 c0,109.928-89.434,199.362-199.363,199.362h-23.276l33.282-37.255c4.937-5.525,4.458-14.007-1.067-18.944 c-5.525-4.937-14.008-4.457-18.944,1.068l-47.576,53.255c-7.788,8.718-7.788,21.866,0,30.584l47.576,53.255 c2.651,2.968,6.322,4.478,10.01,4.478c3.181,0,6.375-1.126,8.934-3.41c5.526-4.937,6.004-13.419,1.067-18.944l-33.282-37.255 h23.276C380.725,452.39,482.195,350.919,482.195,226.196z" data-original="#000000" class="active-path" data-old_color="#F0EDED" fill="#F3F2F2"/>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/query-builder.jsx b/client/js/app/src/app/pages/querybuilder/query-builder.jsx
index fe158fb5b98..472ec1efe49 100644
--- a/client/js/app/src/app/pages/querybuilder/query-builder.jsx
+++ b/client/js/app/src/app/pages/querybuilder/query-builder.jsx
@@ -1,6 +1,71 @@
-import React from 'react';
-import { Container } from 'app/components';
+import React, { useContext } from 'react';
+import SimpleButton from './Components/Buttons/SimpleButton';
+import QueryInput from './Components/Text/QueryInput';
+import TextBox from './Components/Text/TextBox';
+import ImageButton from './Components/Buttons/ImageButton';
+import OverlayImageButton from './Components/Buttons/OverlayImageButton';
+import AddQueryInput from './Components/Buttons/AddQueryInputButton';
+import { QueryInputProvider } from './Components/Contexts/QueryInputContext';
+import SendQuery from './Components/Text/SendQuery';
+import {
+ ResponseContext,
+ ResponseProvider,
+} from './Components/Contexts/ResponseContext';
+import ResponseBox from './Components/Text/ResponseBox';
+
+import copyImage from './assets/img/copy.svg';
+
+import '../../styles/agency.css';
+import '../../styles/vespa.css';
+import ShowQueryButton from './Components/Buttons/ShowQueryButton';
+import { QueryProvider } from './Components/Contexts/QueryContext';
+import PasteJSONButton from './Components/Buttons/PasteJSONButton';
+
+//import 'bootstrap/dist/css/bootstrap.min.css'; //TODO: Find out how to get this css
export function QueryBuilder() {
- return <Container>query builder</Container>;
+ return (
+ <>
+ <header>
+ <div className="intro container">
+ <TextBox className={'intro-lead-in'}>Vespa Search Engine</TextBox>
+ <TextBox className={'intro-long'}>
+ Select the method for sending a request and construct a query.
+ </TextBox>
+ <ResponseProvider>
+ <QueryProvider>
+ <QueryInputProvider>
+ <SendQuery />
+ <br />
+ <div id="request">
+ <QueryInput />
+ </div>
+ <br />
+ <AddQueryInput />
+ <br />
+ <PasteJSONButton />
+ </QueryInputProvider>
+ <ShowQueryButton />
+ </QueryProvider>
+ <TextBox className="response">Response</TextBox>
+ <ResponseBox />
+ </ResponseProvider>
+ <OverlayImageButton
+ className="intro-copy"
+ image={copyImage}
+ height="30"
+ width="30"
+ tooltip="Copy"
+ onClick={() => {
+ alert('Button is non-functional');
+ }}
+ >
+ Copy
+ </OverlayImageButton>
+ <br />
+ <br />
+ </div>
+ </header>
+ </>
+ );
}
diff --git a/client/js/app/src/app/styles/agency.css b/client/js/app/src/app/styles/agency.css
new file mode 100644
index 00000000000..e17809338c6
--- /dev/null
+++ b/client/js/app/src/app/styles/agency.css
@@ -0,0 +1,895 @@
+/*!
+ * Start Bootstrap - Agency Bootstrap Theme (http://startbootstrap.com)
+ * Code licensed under the Apache License v2.0.
+ * For details, see http://www.apache.org/licenses/LICENSE-2.0.
+ */
+:root {
+ --primary: #188fff;
+ --secondary: #003abc;
+ --secondary-dark: #333;
+ --muted: #777;
+
+ --fontprimary: HelveticaNeue, Helvetica, Arial, sans-serif;
+ --fontsecondary: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+}
+
+body {
+ overflow-x: hidden;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ color: var(--secondary-dark);
+}
+
+.text-muted {
+ color: var(--muted);
+}
+
+.text-primary {
+ color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+p {
+ font-size: 14px;
+ line-height: 1.75;
+}
+
+p.large {
+ font-size: 16px;
+}
+
+a,
+a:hover,
+a:focus,
+a:active,
+a.active {
+ outline: 0;
+}
+
+a {
+ color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+a:hover,
+a:focus,
+a:active,
+a.active {
+ color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-weight: 700;
+}
+
+.img-centered {
+ margin: 0 auto;
+}
+
+.bg-light-gray {
+ background-color: #f7f7f7;
+}
+
+.bg-darkest-gray {
+ background-color: #222;
+}
+
+.btn-primary {
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-weight: 700;
+ color: #fff;
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ color: #fff;
+ background-color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+}
+
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ background-image: none;
+}
+
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.btn-primary .badge {
+ color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ background-color: #fff;
+}
+
+.btn-xl {
+ padding: 20px 40px;
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ border-radius: 3px;
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ color: #fff;
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.btn-xl:hover,
+.btn-xl:focus,
+.btn-xl:active,
+.btn-xl.active,
+.open .dropdown-toggle.btn-xl {
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ color: #fff;
+ background-color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+}
+
+.btn-xl:active,
+.btn-xl.active,
+.open .dropdown-toggle.btn-xl {
+ background-image: none;
+}
+
+.btn-xl.disabled,
+.btn-xl[disabled],
+fieldset[disabled] .btn-xl,
+.btn-xl.disabled:hover,
+.btn-xl[disabled]:hover,
+fieldset[disabled] .btn-xl:hover,
+.btn-xl.disabled:focus,
+.btn-xl[disabled]:focus,
+fieldset[disabled] .btn-xl:focus,
+.btn-xl.disabled:active,
+.btn-xl[disabled]:active,
+fieldset[disabled] .btn-xl:active,
+.btn-xl.disabled.active,
+.btn-xl[disabled].active,
+fieldset[disabled] .btn-xl.active {
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.btn-xl .badge {
+ color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ background-color: #fff;
+}
+
+.navbar-default {
+ border-color: transparent;
+ background-color: #222;
+}
+
+.navbar-default .navbar-brand {
+ font-family: 'Kaushan Script', 'Helvetica Neue', Helvetica, Arial, cursive;
+ color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus,
+.navbar-default .navbar-brand:active,
+.navbar-default .navbar-brand.active {
+ color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+}
+
+.navbar-default .navbar-collapse {
+ border-color: rgba(255, 255, 255, 0.02);
+}
+
+.navbar-default .navbar-toggle {
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #fff;
+}
+
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.navbar-default .nav li a {
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-weight: 400;
+ letter-spacing: 1px;
+ color: #fff;
+ -webkit-transition: all ease 0.35s;
+ -moz-transition: all ease 0.35s;
+ transition: all ease 0.35s;
+}
+
+.navbar-default .nav li a:hover,
+.navbar-default .nav li a:focus {
+ outline: 0;
+ color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.navbar-default .navbar-nav > .active > a {
+ border-radius: 0;
+ color: #fff;
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+ color: #fff;
+ background-color: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+}
+
+@media (min-width: 768px) {
+ .navbar-default {
+ padding: 25px 0;
+ border: 0;
+ background-color: transparent;
+ -webkit-transition: padding 0.3s;
+ -moz-transition: padding 0.3s;
+ transition: padding 0.3s;
+ }
+
+ .navbar-default .navbar-brand {
+ font-size: 2em;
+ -webkit-transition: all 0.3s;
+ -moz-transition: all 0.3s;
+ transition: all 0.3s;
+ }
+
+ .navbar-default .navbar-nav > .active > a {
+ border-radius: 3px;
+ }
+
+ .navbar-default.navbar-shrink {
+ padding: 10px 0;
+ background-color: #222;
+ }
+
+ .navbar-default.navbar-shrink .navbar-brand {
+ font-size: 1.5em;
+ }
+}
+
+header {
+ text-align: center;
+ color: #fff;
+ background-attachment: scroll;
+ /*background-image: url('../img/header-bg.png');*/
+ background-position: center center;
+ background-repeat: none;
+ -webkit-background-size: cover;
+ -moz-background-size: cover;
+ background-size: cover;
+ -o-background-size: cover;
+}
+
+header .intro-text {
+ padding-top: 100px;
+ padding-bottom: 50px;
+}
+
+header .intro-text .intro-lead-in {
+ margin-bottom: 25px;
+ font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+ font-size: 22px;
+ font-style: italic;
+ line-height: 22px;
+}
+
+header .intro-text .intro-heading {
+ margin-bottom: 25px;
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-size: 50px;
+ font-weight: 700;
+ line-height: 50px;
+}
+
+@media (min-width: 768px) {
+ header .intro-text {
+ padding-top: 300px;
+ padding-bottom: 200px;
+ }
+
+ header .intro-text .intro-lead-in {
+ margin-bottom: 25px;
+ font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+ font-size: 40px;
+ font-style: italic;
+ line-height: 40px;
+ }
+
+ header .intro-text .intro-heading {
+ margin-bottom: 50px;
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-size: 75px;
+ font-weight: 700;
+ line-height: 75px;
+ }
+}
+
+section {
+ padding: 100px 0;
+}
+
+section h2.section-heading {
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 40px;
+}
+
+section h3.section-subheading {
+ margin-bottom: 75px;
+ text-transform: none;
+ font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+ font-size: 16px;
+ font-style: italic;
+ font-weight: 400;
+}
+
+@media (min-width: 768px) {
+ section {
+ padding: 150px 0;
+ }
+}
+
+.service-heading {
+ margin: 15px 0;
+ text-transform: none;
+}
+
+#portfolio .portfolio-item {
+ right: 0;
+ margin: 0 0 15px;
+}
+
+#portfolio .portfolio-item .portfolio-link {
+ display: block;
+ position: relative;
+ margin: 0 auto;
+ max-width: 400px;
+}
+
+#portfolio .portfolio-item .portfolio-link .portfolio-hover {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ -webkit-transition: all ease 0.5s;
+ -moz-transition: all ease 0.5s;
+ transition: all ease 0.5s;
+ background: rgba(254, 209, 54, 0.9); /* Fallback when no plugin support */
+ background: rgba(var(--primary) | hex_to_rgb | join: ','}}, 0.9);
+}
+
+#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover {
+ opacity: 1;
+}
+
+#portfolio
+ .portfolio-item
+ .portfolio-link
+ .portfolio-hover
+ .portfolio-hover-content {
+ position: absolute;
+ top: 50%;
+ width: 100%;
+ height: 20px;
+ margin-top: -12px;
+ text-align: center;
+ font-size: 20px;
+ color: #fff;
+}
+
+#portfolio
+ .portfolio-item
+ .portfolio-link
+ .portfolio-hover
+ .portfolio-hover-content
+ i {
+ margin-top: -12px;
+}
+
+#portfolio
+ .portfolio-item
+ .portfolio-link
+ .portfolio-hover
+ .portfolio-hover-content
+ h3,
+#portfolio
+ .portfolio-item
+ .portfolio-link
+ .portfolio-hover
+ .portfolio-hover-content
+ h4 {
+ margin: 0;
+}
+
+#portfolio .portfolio-item .portfolio-caption {
+ margin: 0 auto;
+ padding: 25px;
+ max-width: 400px;
+ text-align: center;
+ background-color: #fff;
+}
+
+#portfolio .portfolio-item .portfolio-caption h4 {
+ margin: 0;
+ text-transform: none;
+}
+
+#portfolio .portfolio-item .portfolio-caption p {
+ margin: 0;
+ font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+ font-size: 16px;
+ font-style: italic;
+}
+
+#portfolio * {
+ z-index: 2;
+}
+
+@media (min-width: 767px) {
+ #portfolio .portfolio-item {
+ margin: 0 0 30px;
+ }
+}
+
+.timeline {
+ position: relative;
+ padding: 0;
+ list-style: none;
+}
+
+.timeline:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 40px;
+ width: 2px;
+ margin-left: -1.5px;
+ background-color: #f1f1f1;
+}
+
+.timeline > li {
+ position: relative;
+ margin-bottom: 50px;
+ min-height: 50px;
+}
+
+.timeline > li:before,
+.timeline > li:after {
+ content: ' ';
+ display: table;
+}
+
+.timeline > li:after {
+ clear: both;
+}
+
+.timeline > li .timeline-panel {
+ float: right;
+ position: relative;
+ width: 100%;
+ padding: 0 20px 0 100px;
+ text-align: left;
+}
+
+.timeline > li .timeline-panel:before {
+ right: auto;
+ left: -15px;
+ border-right-width: 15px;
+ border-left-width: 0;
+}
+
+.timeline > li .timeline-panel:after {
+ right: auto;
+ left: -14px;
+ border-right-width: 14px;
+ border-left-width: 0;
+}
+
+.timeline > li .timeline-image {
+ z-index: 100;
+ position: absolute;
+ left: 0;
+ width: 80px;
+ height: 80px;
+ margin-left: 0;
+ border: 7px solid #f1f1f1;
+ border-radius: 100%;
+ text-align: center;
+ color: #fff;
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.timeline > li .timeline-image h4 {
+ margin-top: 12px;
+ font-size: 10px;
+ line-height: 14px;
+}
+
+.timeline > li.timeline-inverted > .timeline-panel {
+ float: right;
+ padding: 0 20px 0 100px;
+ text-align: left;
+}
+
+.timeline > li.timeline-inverted > .timeline-panel:before {
+ right: auto;
+ left: -15px;
+ border-right-width: 15px;
+ border-left-width: 0;
+}
+
+.timeline > li.timeline-inverted > .timeline-panel:after {
+ right: auto;
+ left: -14px;
+ border-right-width: 14px;
+ border-left-width: 0;
+}
+
+.timeline > li:last-child {
+ margin-bottom: 0;
+}
+
+.timeline .timeline-heading h4 {
+ margin-top: 0;
+ color: inherit;
+}
+
+.timeline .timeline-heading h4.subheading {
+ text-transform: none;
+}
+
+.timeline .timeline-body > p,
+.timeline .timeline-body > ul {
+ margin-bottom: 0;
+}
+
+@media (min-width: 768px) {
+ .timeline:before {
+ left: 50%;
+ }
+
+ .timeline > li {
+ margin-bottom: 100px;
+ min-height: 100px;
+ }
+
+ .timeline > li .timeline-panel {
+ float: left;
+ width: 41%;
+ padding: 0 20px 20px 30px;
+ text-align: right;
+ }
+
+ .timeline > li .timeline-image {
+ left: 50%;
+ width: 100px;
+ height: 100px;
+ margin-left: -50px;
+ }
+
+ .timeline > li .timeline-image h4 {
+ margin-top: 16px;
+ font-size: 13px;
+ line-height: 18px;
+ }
+
+ .timeline > li.timeline-inverted > .timeline-panel {
+ float: right;
+ padding: 0 30px 20px 20px;
+ text-align: left;
+ }
+}
+
+@media (min-width: 992px) {
+ .timeline > li {
+ min-height: 150px;
+ }
+
+ .timeline > li .timeline-panel {
+ padding: 0 20px 20px;
+ }
+
+ .timeline > li .timeline-image {
+ width: 150px;
+ height: 150px;
+ margin-left: -75px;
+ }
+
+ .timeline > li .timeline-image h4 {
+ margin-top: 30px;
+ font-size: 18px;
+ line-height: 26px;
+ }
+
+ .timeline > li.timeline-inverted > .timeline-panel {
+ padding: 0 20px 20px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .timeline > li {
+ min-height: 170px;
+ }
+
+ .timeline > li .timeline-panel {
+ padding: 0 20px 20px 100px;
+ }
+
+ .timeline > li .timeline-image {
+ width: 170px;
+ height: 170px;
+ margin-left: -85px;
+ }
+
+ .timeline > li .timeline-image h4 {
+ margin-top: 40px;
+ }
+
+ .timeline > li.timeline-inverted > .timeline-panel {
+ padding: 0 100px 20px 20px;
+ }
+}
+
+.team-member {
+ margin-bottom: 50px;
+ text-align: center;
+}
+
+.team-member img {
+ margin: 0 auto;
+ border: 7px solid #fff;
+}
+
+.team-member h4 {
+ margin-top: 25px;
+ margin-bottom: 0;
+ text-transform: none;
+}
+
+.team-member p {
+ margin-top: 0;
+}
+
+aside.clients img {
+ margin: 50px auto;
+}
+
+section#contact {
+ background-color: #222;
+ /* background-image: url('../img/map-image.png'); */
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+section#contact .section-heading {
+ color: #fff;
+}
+
+section#contact .form-group {
+ margin-bottom: 25px;
+}
+
+section#contact .form-group input,
+section#contact .form-group textarea {
+ padding: 20px;
+}
+
+section#contact .form-group input.form-control {
+ height: auto;
+}
+
+section#contact .form-group textarea.form-control {
+ height: 236px;
+}
+
+section#contact .form-control:focus {
+ border-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+ box-shadow: none;
+}
+
+section#contact::-webkit-input-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact:-moz-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact::-moz-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact:-ms-input-placeholder {
+ text-transform: uppercase;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ font-weight: 700;
+ color: #bbb;
+}
+
+section#contact .text-danger {
+ color: #e74c3c;
+}
+
+footer {
+ padding: 25px 0;
+ text-align: center;
+}
+
+footer span.copyright {
+ text-transform: uppercase;
+ text-transform: none;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ line-height: 40px;
+}
+
+footer ul.quicklinks {
+ margin-bottom: 0;
+ text-transform: uppercase;
+ text-transform: none;
+ font-family: HelveticaNeue, Helvetica, Arial, sans-serif;
+ line-height: 40px;
+}
+
+ul.social-buttons {
+ margin-bottom: 0;
+}
+
+ul.social-buttons li a {
+ display: block;
+ width: 40px;
+ height: 40px;
+ border-radius: 100%;
+ font-size: 20px;
+ line-height: 40px;
+ outline: 0;
+ color: #fff;
+ background-color: #222;
+ -webkit-transition: all 0.3s;
+ -moz-transition: all 0.3s;
+ transition: all 0.3s;
+}
+
+ul.social-buttons li a:hover,
+ul.social-buttons li a:focus,
+ul.social-buttons li a:active {
+ background-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+.btn:focus,
+.btn:active,
+.btn.active,
+.btn:active:focus {
+ outline: 0;
+}
+
+.portfolio-modal .modal-content {
+ padding: 100px 0;
+ min-height: 100%;
+ border: 0;
+ border-radius: 0;
+ text-align: center;
+ background-clip: border-box;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+.portfolio-modal .modal-content h2 {
+ margin-bottom: 15px;
+ font-size: 3em;
+}
+
+.portfolio-modal .modal-content p {
+ margin-bottom: 30px;
+}
+
+.portfolio-modal .modal-content p.item-intro {
+ margin: 20px 0 30px;
+ font-family: HelveticaNeue-Thin, Helvetica, Arial, sans-serif;
+ font-size: 16px;
+ font-style: italic;
+}
+
+.portfolio-modal .modal-content ul.list-inline {
+ margin-top: 0;
+ margin-bottom: 30px;
+}
+
+.portfolio-modal .modal-content img {
+ margin-bottom: 30px;
+}
+
+.portfolio-modal .close-modal {
+ position: absolute;
+ top: 25px;
+ right: 25px;
+ width: 75px;
+ height: 75px;
+ background-color: transparent;
+ cursor: pointer;
+}
+
+.portfolio-modal .close-modal:hover {
+ opacity: 0.3;
+}
+
+.portfolio-modal .close-modal .lr {
+ z-index: 1051;
+ width: 1px;
+ height: 75px;
+ margin-left: 35px;
+ background-color: #222;
+ -webkit-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+.portfolio-modal .close-modal .lr .rl {
+ z-index: 1052;
+ width: 1px;
+ height: 75px;
+ background-color: #222;
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+
+::-moz-selection {
+ text-shadow: none;
+ background: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+::selection {
+ text-shadow: none;
+ background: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
+
+img::selection {
+ background: 0 0;
+}
+
+img::-moz-selection {
+ background: 0 0;
+}
+
+body {
+ webkit-tap-highlight-color: HelveticaNeue, Helvetica, Arial, sans-serif;
+}
diff --git a/client/js/app/src/app/styles/vespa.css b/client/js/app/src/app/styles/vespa.css
new file mode 100644
index 00000000000..4017f0275c3
--- /dev/null
+++ b/client/js/app/src/app/styles/vespa.css
@@ -0,0 +1,752 @@
+/**
+* Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+* Based on vespa.css from https://github.com/vespa-engine/frontpage for header- and footer-elements
+*/
+:root {
+ --primary: #188fff;
+ --secondary: #003abc;
+ --secondary-dark: #333;
+ --muted: #777;
+
+ --fontprimary: 'Hind Madurai', Helvetica, Arial, sans-serif;
+ --fontsecondary: 'Hind Madurai', Helvetica, Arial, sans-serif;
+}
+
+.bg-light-blue {
+ background-image: linear-gradient(
+ -1deg,
+ rgba(63, 157, 216, 0.08) 0%,
+ rgba(163, 195, 215, 0.08) 97%
+ );
+}
+
+.yql {
+}
+
+.yql:focus {
+ outline: none;
+}
+
+.information {
+ margin-left: -40px;
+ margin-right: 28px;
+ margin-top: -2.5px;
+ opacity: 0.6;
+ transition: transform 0.1s; /* Animation */
+}
+
+.information:hover {
+ transform: scale(1.05);
+}
+
+/*** Tooltips! ***/
+.tip {
+ visibility: visible;
+ border-bottom: 1px dotted;
+ position: relative;
+ cursor: help;
+ text-decoration: none;
+ color: #fff !important;
+}
+.tip span {
+ display: none;
+ z-index: 100;
+ position: absolute;
+ padding: 0.6em;
+ padding-left: 1em;
+ top: 1.5em;
+ left: 2.4em;
+ width: 15em;
+ background-color: #4da2d6;
+ border: 1px solid #ffffff;
+ border-radius: 0.5em;
+}
+
+.tip span td {
+ text-align: left;
+ vertical-align: top;
+ padding-left: 5px;
+}
+
+.tip span a {
+ text-decoration: none;
+ color: white;
+}
+
+.tip:hover span {
+ display: inline-block;
+}
+.sg-question-set,
+.sg-type-radio ul.sg-list-vertical li,
+.sg-type-checkbox ul.sg-list-vertical li,
+.sg-question-options,
+.sg-type-radio-likert .sg-question-options,
+.sg-type-table .sg-question-options,
+.sg-instructions {
+ overflow: visible;
+}
+
+section h2.section-heading,
+section h2.section-subheading,
+section h3.section-heading,
+section h3.section-subheading {
+ text-transform: none;
+ font-family: 'Hind Madurai', Helvetica, Arial, sans-serif;
+ font-weight: normal;
+ font-style: normal;
+}
+
+section h2.section-heading,
+section h3.section-heading {
+ font-size: 50px;
+ color: #3f9dd8;
+ margin-bottom: 5px;
+}
+
+section h2.section-subheading,
+section h3.section-subheading {
+ line-height: 26px;
+ margin-top: 0;
+}
+
+header .help-title {
+ color: #ffc43c;
+ text-transform: uppercase;
+ font-weight: bold;
+}
+
+.text-muted {
+ color: #303030;
+}
+
+#banner {
+ background-color: #336699;
+ color: #ffffff;
+ font-weight: bold;
+ padding-bottom: 2px;
+ padding-top: 2px;
+ width: 100%;
+}
+
+#banner a {
+ color: #ffffff;
+ text-decoration: underline;
+}
+
+.navbar-default {
+ background-color: #005a8e;
+ padding: 0;
+ border-bottom: 2px solid rgba(255, 255, 255, 0.2);
+}
+
+.navbar-default .navbar-header {
+ margin-left: 13px;
+}
+
+.navbar-default .navbar-brand {
+ background: transparent url('https://vespa.ai/assets/vespa-logo.png')
+ no-repeat;
+ background-size: contain;
+ direction: ltr;
+ text-indent: -9000px;
+ height: 28px;
+ width: 100px;
+ display: inline-block;
+ font-family: 'Hind Madurai', Helvetica, Arial, sans-serif;
+ font-weight: bold;
+ color: var(--primary);
+ margin-top: 16px;
+ margin-left: 10px;
+}
+
+.navbar-nav .nav-link {
+ font-weight: bold;
+ font-size: 14px;
+ letter-spacing: 1px;
+ color: #ffffff;
+ text-transform: none;
+ font-family: 'Hind Madurai', Helvetica, Arial, sans-serif;
+}
+
+.navbar-default .navbar-nav > a:hover,
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover {
+ color: #ffc43c;
+}
+
+.navbar-default .navbar-toggler-icon {
+ background-image: url('data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 30 30%27%3e%3cpath stroke=%27rgba%280, 0, 0, 0.55%29%27 stroke-linecap=%27round%27 stroke-miterlimit=%2710%27 stroke-width=%272%27 d=%27M4 7h22M4 15h22M4 23h22%27/%3e%3c/svg%3e');
+}
+
+.navbar-default .navbar-toggler {
+ margin-right: 45px;
+ border: 1px solid;
+ border-color: #1a242f;
+}
+.navbar-toggler:hover,
+.navbar-default .navbar-toggler:focus {
+ background-color: #1a242f;
+ box-shadow: 0 0 0 0;
+}
+
+.navbar-toggle {
+ margin-right: 45px;
+}
+
+/* Trick to prevent anchored links to hide heading behind navbar */
+section[id]:before {
+ display: block;
+ content: ' ';
+ margin-top: -80px;
+ height: 100px;
+ visibility: hidden;
+}
+
+header {
+ /*background-image: linear-gradient(0deg, #98C1DB 7%, #3F9DD8 100%); */
+ background-color: #005a8e;
+ min-height: 1150px;
+}
+
+@media (max-height: 1150px) {
+ header {
+ min-height: 100vh;
+ }
+}
+
+header .intro {
+ margin-top: -80px;
+ max-width: 350px;
+ padding-top: 10px;
+ padding-bottom: 50px;
+}
+
+header .intro-logo {
+ max-width: 120px;
+ padding-bottom: 30px;
+}
+
+header .intro-button {
+ box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5);
+ padding-left: 50px;
+ padding-right: 50px;
+}
+
+header .intro-long {
+ line-height: 27px;
+ font-size: 16px;
+ padding-bottom: 20px;
+}
+
+header .intro-param {
+ line-height: 27px;
+ font-size: 16px;
+ padding-bottom: 0px;
+ margin-bottom: 5px;
+}
+header .response {
+ line-height: 27px;
+ font-size: 20px;
+ padding-top: 10px;
+ padding-bottom: 8px;
+}
+
+header .methodselector {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 70px;
+ height: 25px;
+ border-width: 0px;
+ -o-transition: 0.3s;
+ -ms-transition: 0.3s;
+ -moz-transition: 0.3s;
+ -webkit-transition: 0.3s;
+ transition: 0.3s;
+}
+header .intro-help:hover {
+ background-color: #4ea2d6 !important;
+}
+header .methodselector:hover {
+ background-color: #79b4d8;
+}
+header .button:hover {
+ background-color: #79b4d8;
+}
+header .removeRow:hover {
+ background-color: #79b4d8;
+}
+header .addRow:hover {
+ background-color: #79b4d8;
+}
+header .showJSON:hover {
+ background-color: #79b4d8;
+}
+header .copyURL:hover {
+ background-color: #79b4d8;
+}
+header .copyJSON:hover {
+ background-color: #79b4d8;
+}
+header .methodselector:focus {
+ outline: none;
+}
+header .textbox:focus {
+ outline: none;
+}
+header .input:focus {
+ outline: none;
+}
+header .responsebox::selection {
+ background-color: grey;
+}
+header .textbox::selection {
+ background-color: grey;
+}
+header .input::selection {
+ background-color: grey;
+}
+header .propvalue::selection {
+ background-color: grey;
+}
+header .responsebox:focus {
+ outline: none;
+}
+header .responseinfo:focus {
+ outline: none;
+}
+header .propvalue:focus {
+ outline: none;
+}
+header .addpropsbutton:focus {
+ outline: none;
+}
+
+header .input {
+ color: #000;
+ width: 190px;
+ border-width: 0px;
+ margin-right: 2px;
+}
+
+header .input2 {
+ color: #000;
+ width: 175px;
+ border-width: 0px;
+ margin-right: 2px;
+}
+
+header .textbox {
+ color: #000;
+ padding-left: 2px;
+}
+
+header .responsebox {
+ color: #000;
+ background-color: #fff;
+}
+
+header .responseinfo {
+ color: #d6e6f0;
+ text-align: center;
+ border-width: 0px;
+ background-color: transparent;
+}
+
+header .propvalue {
+ color: #000;
+ background-color: #fff;
+ width: 175px;
+ border-width: 0px;
+ margin-bottom: 3px;
+}
+
+header .propvalue::-webkit-input-placeholder {
+ /* Safari, Chrome(, Opera?) */
+ color: gray;
+ font-style: italic;
+}
+header .propvalue:-moz-placeholder {
+ /* Firefox 18- */
+ color: gray;
+ font-style: italic;
+}
+header .propvalue:-moz-placeholder {
+ /* Firefox 19+ */
+ color: gray;
+ font-style: italic;
+}
+header .propvalue-ms-input-placeholder {
+ /* IE (10+?) */
+ color: gray;
+ font-style: italic;
+}
+
+header .button {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 70px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+}
+header .button:focus {
+ outline: none;
+}
+
+header .addpropsbutton {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 175px;
+ height: 23px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+ margin-left: 2px;
+ margin-bottom: 3px;
+}
+header .addRow {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 70px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+}
+header .addRow:focus {
+ outline: none;
+}
+
+header .removeRow {
+ font-size: 16px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 35px;
+ height: 23px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+ padding-bottom: 2px;
+ margin-left: 3px;
+ text-align: center;
+ line-height: normal;
+}
+header .removeRow:focus {
+ outline: none;
+}
+
+header .json:focus {
+ outline: none;
+}
+header .copyJSON:focus {
+ outline: none;
+}
+header .showJSON:focus {
+ outline: none;
+}
+header .copyURL:focus {
+ outline: none;
+}
+header .pasteJSON:focus {
+ outline: none;
+}
+
+header .copyJSON {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 130px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding-left: 1px;
+ margin-top: 10px;
+ margin-bottom: 20px;
+ margin-right: 2px;
+ display: none;
+}
+
+header .showJSON {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 140px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding: 0px;
+ margin-top: 5px;
+ margin-bottom: 10px;
+}
+
+header .pasteJSON {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 140px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding-left: 0px;
+ padding-bottom: 2px;
+ margin-top: 35px;
+ margin-bottom: 10px;
+}
+
+header .copyURL {
+ font-size: 15px;
+ color: #fff;
+ background-color: #99c1da;
+ width: 130px;
+ height: 25px;
+ border-width: 0px;
+ border-radius: 5px;
+ padding-left: 1px;
+ margin-top: 25px;
+ margin-bottom: 10px;
+ margin-left: 2px;
+ display: none;
+}
+
+header .intro .intro-lead-in {
+ font-style: normal;
+ font-size: 22px;
+ line-height: 50px;
+ margin-bottom: 25px;
+ font-family: 'Hind Madurai', Helvetica, Arial, sans-serif;
+}
+
+header .intro .intro-heading {
+ font-style: normal;
+}
+
+header .intro-button {
+ box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5);
+}
+
+header .intro .btn-xl {
+ background: #ffffff;
+ border: 1px solid #3f9dd8;
+ border-radius: 2px;
+ font-size: 20px;
+ color: #3f9dd8;
+ font-weight: normal;
+ text-transform: none;
+ font-family: 'Hind Madurai', Helvetica, Arial, sans-serif;
+}
+
+header span {
+ display: inline-block;
+ position: inherit;
+ margin-top: 10px;
+ /*padding-left: 50px;*/
+ transform: translateX(-50%);
+ background: transparent;
+ border: none !important;
+ font-size: 0;
+}
+
+header .intro-copy {
+ background: transparent;
+ border: none !important;
+ font-size: 0;
+}
+
+header .intro-help {
+ display: inline-block;
+ margin-top: 15px;
+ width: 134px;
+ height: 45px;
+ background-color: transparent;
+ border: none !important;
+ font-size: 0;
+
+ border-top-left-radius: 30%;
+ border-top-right-radius: 29%;
+ border-bottom-right-radius: 24%;
+ border-bottom-left-radius: 26%;
+}
+
+header .intro-refresh {
+ display: inline-block;
+ margin-top: 15px;
+ /* transform: translateX(-50%); */
+ background: transparent;
+ border: none !important;
+ font-size: 0;
+}
+
+@media (min-height: 1150px) {
+ header .intro-down-arrow {
+ /* Hard code top: 1150 (height of top section) - 20 (bottom) - 36 (image height) = 1094 */
+ top: 1094px;
+ }
+}
+
+@media (max-height: 825px) {
+ header .intro-down-arrow {
+ display: none;
+ }
+}
+
+@media (min-width: 768px) {
+ header .intro {
+ padding-top: 200px;
+ padding-bottom: 200px;
+ max-width: 620px;
+ }
+
+ header .intro-logo {
+ max-width: 238px;
+ padding-bottom: 70px;
+ }
+
+ header .intro .intro-lead-in {
+ font-size: 51px;
+ font-style: normal;
+ line-height: 50px;
+ }
+
+ header .intro .intro-long {
+ margin-left: 80px;
+ margin-right: 80px;
+ }
+
+ header .intro .intro-heading {
+ margin-bottom: 25px;
+ font-size: 90px;
+ font-weight: 600;
+ }
+}
+
+section {
+ padding: 50px 0;
+}
+
+section h3.section-subheading {
+ margin-bottom: 50px;
+ font-weight: normal;
+}
+
+@media (min-width: 768px) {
+ section {
+ padding: 50px 0;
+ }
+}
+
+footer .row {
+ font-size: 12px;
+ text-align: left;
+}
+
+footer .row h4 {
+ font-size: 14px;
+ padding-left: 40px;
+}
+
+footer .row .quicklinks {
+ line-height: 25px;
+ list-style: none;
+ text-align: left;
+}
+
+footer .credits {
+ font-size: 10px;
+ text-align: center;
+}
+
+#description p {
+ font-weight: 300;
+ font-size: 25px;
+}
+
+footer {
+ font-size: 14px;
+ color: #ffffff;
+ background-color: #3f9dd8;
+ line-height: 22px;
+ letter-spacing: 0;
+ text-align: left;
+ display: flex;
+ flex-wrap: wrap;
+}
+
+footer .footer-row {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ flex-wrap: wrap;
+}
+
+footer quicklinks {
+ font-size: 14px;
+ color: #ffffff;
+}
+
+footer ul.quicklinks {
+ margin: 0;
+ padding: 0;
+}
+
+footer .footer-title {
+ color: #ffc43c;
+ text-transform: uppercase;
+ font-weight: bold;
+}
+
+footer ul.quicklinks a {
+ font-size: 14px;
+ color: #ffffff;
+}
+
+footer .quicklink-section {
+ padding: 0;
+ min-width: 120px;
+}
+
+footer .credits {
+ float: left;
+ font-size: 12px;
+ font-weight: normal;
+ text-align: right;
+ font-family: 'Hind Madurai', Helvetica, Arial, sans-serif;
+}
+
+footer .credits a {
+ color: #ffffff;
+}
+
+footer .credits span {
+ color: #ffc43c;
+}
+
+footer a {
+ text-decoration: none;
+}
+
+footer .table {
+ width: fit-content;
+ margin-right: auto;
+ margin-left: auto;
+ float: left;
+ margin-bottom: 0em;
+}
+
+footer .table tbody th {
+ font-size: 13px;
+ padding-right: 2em;
+}
+
+footer table thead {
+ color: #ffc43c;
+}
+
+footer table tbody a {
+ color: #ffffff;
+}
diff --git a/client/js/app/yarn.lock b/client/js/app/yarn.lock
index f1cee14e50e..2261621f31b 100644
--- a/client/js/app/yarn.lock
+++ b/client/js/app/yarn.lock
@@ -208,6 +208,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.13.16", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580"
+ integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
@@ -463,7 +470,7 @@
clsx "^1.1.1"
csstype "3.0.9"
-"@popperjs/core@^2.9.3":
+"@popperjs/core@^2.10.1", "@popperjs/core@^2.9.3":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
@@ -558,6 +565,35 @@
dependencies:
"@babel/runtime" "^7.13.10"
+"@react-aria/ssr@^3.0.1":
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.2.0.tgz#88460384b43204f91c972d5b0de24ee44d6a2984"
+ integrity sha512-wwJFdkl+Q8NU5yJ4NvdAOqx5LM3QtUVoSjuK7Ey8jZ4WS4bB0EqT3Kr3IInBs257HzZ5nXCiKXKE4NGXXuIRWA==
+ dependencies:
+ "@babel/runtime" "^7.6.2"
+
+"@restart/hooks@^0.4.0", "@restart/hooks@^0.4.6":
+ version "0.4.7"
+ resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.7.tgz#d79ca6472c01ce04389fc73d4a79af1b5e33cd39"
+ integrity sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A==
+ dependencies:
+ dequal "^2.0.2"
+
+"@restart/ui@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@restart/ui/-/ui-1.2.0.tgz#fb90251aa25f99b41ccedc78a91d2a15f3c5e0fb"
+ integrity sha512-oIh2t3tG8drZtZ9SlaV5CY6wGsUViHk8ZajjhcI+74IQHyWy+AnxDv8rJR5wVgsgcgrPBUvGNkC1AEdcGNPaLQ==
+ dependencies:
+ "@babel/runtime" "^7.13.16"
+ "@popperjs/core" "^2.10.1"
+ "@react-aria/ssr" "^3.0.1"
+ "@restart/hooks" "^0.4.0"
+ "@types/warning" "^3.0.0"
+ dequal "^2.0.2"
+ dom-helpers "^5.2.0"
+ uncontrollable "^7.2.1"
+ warning "^4.0.3"
+
"@rollup/pluginutils@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
@@ -588,6 +624,13 @@
dependencies:
"@types/react" "*"
+"@types/react-transition-group@^4.4.4":
+ version "4.4.4"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
+ integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*":
version "18.0.13"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.13.tgz#0f5bd24a5f26593e04e450fe85ff43f51c1524ff"
@@ -597,7 +640,7 @@
"@types/scheduler" "*"
csstype "^3.0.2"
-"@types/react@^18":
+"@types/react@>=16.9.11", "@types/react@^18":
version "18.0.14"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.14.tgz#e016616ffff51dba01b04945610fe3671fdbe06d"
integrity sha512-x4gGuASSiWmo0xjDLpm5mPb52syZHJx02VKbqUKdLmKtAwIh63XClGsiTI1K6DO5q7ox4xAsQrU+Gl3+gGXF9Q==
@@ -611,6 +654,11 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+"@types/warning@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
+ integrity sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==
+
"@vitejs/plugin-react@^1":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz#2fcf0b6ce9bcdcd4cec5c760c199779d5657ece1"
@@ -782,6 +830,11 @@ chalk@^4.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
+classnames@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
+ integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
+
clsx@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
@@ -876,6 +929,11 @@ define-properties@^1.1.3, define-properties@^1.1.4:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
+dequal@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
+ integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==
+
doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
@@ -890,6 +948,14 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
electron-to-chromium@^1.4.147:
version "1.4.158"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.158.tgz#abbdaaf64676bfa4bc0307522125db34424a0ada"
@@ -1546,6 +1612,13 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
+invariant@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+ dependencies:
+ loose-envify "^1.0.0"
+
is-bigint@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
@@ -2011,7 +2084,15 @@ pretty-quick@^3:
mri "^1.1.5"
multimatch "^4.0.0"
-prop-types@^15.8.1:
+prop-types-extra@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b"
+ integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==
+ dependencies:
+ react-is "^16.3.2"
+ warning "^4.0.0"
+
+prop-types@^15.6.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -2033,6 +2114,24 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+react-bootstrap@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-2.4.0.tgz#99bf9656e2e7a23ae1ae135d18fd5ad7c344b416"
+ integrity sha512-dn599jNK1Fg5GGjJH+lQQDwELVzigh/MdusKpB/0el+sCjsO5MZDH5gRMmBjRhC+vb7VlCDr6OXffPIDSkNMLw==
+ dependencies:
+ "@babel/runtime" "^7.17.2"
+ "@restart/hooks" "^0.4.6"
+ "@restart/ui" "^1.2.0"
+ "@types/react-transition-group" "^4.4.4"
+ classnames "^2.3.1"
+ dom-helpers "^5.2.1"
+ invariant "^2.2.4"
+ prop-types "^15.8.1"
+ prop-types-extra "^1.1.0"
+ react-transition-group "^4.4.2"
+ uncontrollable "^7.2.1"
+ warning "^4.0.3"
+
react-dom@^18:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@@ -2046,11 +2145,16 @@ react-fast-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
-react-is@^16.13.1, react-is@^16.7.0:
+react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+react-lifecycles-compat@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+ integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
react-popper@^2.2.5:
version "2.3.0"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"
@@ -2064,7 +2168,7 @@ react-refresh@^0.13.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.13.0.tgz#cbd01a4482a177a5da8d44c9755ebb1f26d5a1c1"
integrity sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==
-react-router-dom@^6:
+react-router-dom@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d"
integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==
@@ -2088,6 +2192,16 @@ react-textarea-autosize@^8.3.2:
use-composed-ref "^1.3.0"
use-latest "^1.2.1"
+react-transition-group@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
+ integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
react@^18:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@@ -2328,6 +2442,16 @@ unbox-primitive@^1.0.2:
has-symbols "^1.0.3"
which-boxed-primitive "^1.0.2"
+uncontrollable@^7.2.1:
+ version "7.2.1"
+ resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738"
+ integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==
+ dependencies:
+ "@babel/runtime" "^7.6.3"
+ "@types/react" ">=16.9.11"
+ invariant "^2.2.4"
+ react-lifecycles-compat "^3.0.4"
+
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -2358,9 +2482,9 @@ v8-compile-cache@^2.0.3:
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
vite@^2:
- version "2.9.12"
- resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.12.tgz#b1d636b0a8ac636afe9d83e3792d4895509a941b"
- integrity sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew==
+ version "2.9.13"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.13.tgz#859cb5d4c316c0d8c6ec9866045c0f7858ca6abc"
+ integrity sha512-AsOBAaT0AD7Mhe8DuK+/kE4aWYFMx/i0ZNi98hJclxb4e0OhQcZYUrvLjIaQ8e59Ui7txcvKMiJC1yftqpQoDw==
dependencies:
esbuild "^0.14.27"
postcss "^8.4.13"
@@ -2369,7 +2493,7 @@ vite@^2:
optionalDependencies:
fsevents "~2.3.2"
-warning@^4.0.2:
+warning@^4.0.0, warning@^4.0.2, warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
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 5e9f1f52a0c..4536257f8a3 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
@@ -77,13 +77,19 @@ public interface ModelContext {
@ModelFeatureFlag(owners = {"baldersheim"}, comment = "Select sequencer type use while feeding") default String feedSequencerType() { return "THROUGHPUT"; }
@ModelFeatureFlag(owners = {"baldersheim"}) default String responseSequencerType() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default int defaultNumResponseThreads() { return 2; }
- @ModelFeatureFlag(owners = {"baldersheim"}) default boolean skipCommunicationManagerThread() { return true; }
- @ModelFeatureFlag(owners = {"baldersheim"}) default boolean skipMbusRequestThread() { return true; }
- @ModelFeatureFlag(owners = {"baldersheim"}) default boolean skipMbusReplyThread() { return true; }
- @ModelFeatureFlag(owners = {"baldersheim"}) default boolean mbusDispatchOnDecode() { return true; }
- @ModelFeatureFlag(owners = {"baldersheim"}) default boolean mbusDispatchOnEncode() { return true; }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter="7.last") default boolean skipCommunicationManagerThread() { return true; }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter="7.last") default boolean skipMbusRequestThread() { return true; }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter="7.last") default boolean skipMbusReplyThread() { return true; }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter="8.15") default boolean mbusDispatchOnDecode() { return true; }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter="8.15") default boolean mbusDispatchOnEncode() { return true; }
@ModelFeatureFlag(owners = {"baldersheim"}) default int mbusNetworkThreads() { return 1; }
- @ModelFeatureFlag(owners = {"baldersheim"}) default int mbusThreads() { return 4; }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter="8.15") default int mbusThreads() { return 4; }
+ @ModelFeatureFlag(owners = {"baldersheim"}) default int mbusJavaRpcNumTargets() { return 1; }
+ @ModelFeatureFlag(owners = {"baldersheim"}) default int mbusJavaEventsBeforeWakeup() { return 1; }
+ @ModelFeatureFlag(owners = {"baldersheim"}) default int mbusCppRpcNumTargets() { return 1; }
+ @ModelFeatureFlag(owners = {"baldersheim"}) default int mbusCppEventsBeforeWakeup() { return 1; }
+ @ModelFeatureFlag(owners = {"baldersheim"}) default int rpcNumTargets() { return 1; }
+ @ModelFeatureFlag(owners = {"baldersheim"}) default int rpcEventsBeforeWakeup() { return 1; }
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean useAsyncMessageHandlingOnSchedule() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default double feedConcurrency() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default double feedNiceness() { return 0.0; }
@@ -108,7 +114,7 @@ public interface ModelContext {
@ModelFeatureFlag(owners = {"arnej"}) default boolean useV8GeoPositions() { return false; }
@ModelFeatureFlag(owners = {"baldersheim", "geirst", "toregge"}) default int maxCompactBuffers() { return 1; }
@ModelFeatureFlag(owners = {"arnej", "andreer"}) default List<String> ignoredHttpUserAgents() { return List.of(); }
- @ModelFeatureFlag(owners = {"bjorncs"}) default boolean enableServerOcspStapling() { return false; }
+ @ModelFeatureFlag(owners = {"bjorncs"}, removeAfter="7.last") default boolean enableServerOcspStapling() { return true; }
@ModelFeatureFlag(owners = {"vekterli"}) default String mergeThrottlingPolicy() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"vekterli"}) default double persistenceThrottlingWsDecrementFactor() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"vekterli"}) default double persistenceThrottlingWsBackoff() { throw new UnsupportedOperationException("TODO specify default value"); }
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 5f189a63701..400aea86834 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
@@ -78,9 +78,12 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
private boolean enableBitVectors = false;
private boolean loadCodeAsHugePages = false;
private boolean sharedStringRepoNoReclaim = false;
- private boolean mbus_dispatch_on_decode = true;
- private boolean mbus_dispatch_on_encode = true;
- private int mbus_threads = 4;
+ private int mbus_java_num_targets = 1;
+ private int mbus_java_events_before_wakeup = 1;
+ private int mbus_cpp_num_targets = 1;
+ private int mbus_cpp_events_before_wakeup = 1;
+ private int rpc_num_targets = 1;
+ private int rpc_events_before_wakeup = 1;
private int mbus_network_threads = 1;
private Architecture adminClusterNodeResourcesArchitecture = Architecture.getDefault();
@@ -137,10 +140,13 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
@Override public Architecture adminClusterArchitecture() { return adminClusterNodeResourcesArchitecture; }
@Override public boolean sharedStringRepoNoReclaim() { return sharedStringRepoNoReclaim; }
@Override public boolean loadCodeAsHugePages() { return loadCodeAsHugePages; }
- @Override public boolean mbusDispatchOnDecode() { return mbus_dispatch_on_decode; }
- @Override public boolean mbusDispatchOnEncode() { return mbus_dispatch_on_encode; }
@Override public int mbusNetworkThreads() { return mbus_network_threads; }
- @Override public int mbusThreads() { return mbus_threads; }
+ @Override public int mbusJavaRpcNumTargets() { return mbus_java_num_targets; }
+ @Override public int mbusJavaEventsBeforeWakeup() { return mbus_java_events_before_wakeup; }
+ @Override public int mbusCppRpcNumTargets() { return mbus_cpp_num_targets; }
+ @Override public int mbusCppEventsBeforeWakeup() { return mbus_cpp_events_before_wakeup; }
+ @Override public int rpcNumTargets() { return rpc_num_targets; }
+ @Override public int rpcEventsBeforeWakeup() { return rpc_events_before_wakeup; }
public TestProperties sharedStringRepoNoReclaim(boolean sharedStringRepoNoReclaim) {
@@ -367,24 +373,35 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
return this;
}
- public TestProperties setMbusDispatchOnDecode(boolean value) {
- this.mbus_dispatch_on_decode = value;
+ public TestProperties setMbusNetworkThreads(int value) {
+ this.mbus_network_threads = value;
return this;
}
- public TestProperties setMbusDispatchOnEncode(boolean value) {
- this.mbus_dispatch_on_encode = value;
+ public TestProperties setMbusJavaRpcNumTargets(int value) {
+ this.mbus_java_num_targets = value;
return this;
}
-
- public TestProperties setMbusThreads(int value) {
- this.mbus_threads = value;
+ public TestProperties setMbusJavaEventsBeforeWakeup(int value) {
+ this.mbus_java_events_before_wakeup = value;
return this;
}
-
- public TestProperties setMbusNetworkThreads(int value) {
- this.mbus_network_threads = value;
+ public TestProperties setMbusCppEventsBeforeWakeup(int value) {
+ this.mbus_cpp_events_before_wakeup = value;
return this;
}
+ public TestProperties setMbusCppRpcNumTargets(int value) {
+ this.mbus_cpp_num_targets = value;
+ return this;
+ }
+ public TestProperties setRpcNumTargets(int value) {
+ this.rpc_num_targets = value;
+ return this;
+ }
+ public TestProperties setRpcEventsBeforeWakeup(int value) {
+ this.rpc_events_before_wakeup = value;
+ return this;
+ }
+
public TestProperties setAdminClusterNodeResourcesArchitecture(Architecture architecture) {
this.adminClusterNodeResourcesArchitecture = architecture;
return this;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java
index 15a0e060e1c..33c125dcecf 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java
@@ -7,10 +7,12 @@ import com.yahoo.container.handler.threadpool.ContainerThreadpoolConfig;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.ContainerThreadpool;
+import com.yahoo.vespa.model.container.PlatformBundles;
import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
import com.yahoo.vespa.model.container.component.UserBindingPattern;
+import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
@@ -21,6 +23,8 @@ import java.util.Collections;
public class ContainerDocumentApi {
public static final String DOCUMENT_V1_PREFIX = "/document/v1";
+ public static final Path VESPACLIENT_CONTAINER_BUNDLE =
+ PlatformBundles.absoluteBundlePath("vespaclient-container-plugin");
private final boolean ignoreUndefinedFields;
@@ -28,6 +32,11 @@ public class ContainerDocumentApi {
this.ignoreUndefinedFields = ignoreUndefinedFields;
addRestApiHandler(cluster, handlerOptions);
addFeedHandler(cluster, handlerOptions);
+ addVespaClientContainerBundle(cluster);
+ }
+
+ public static void addVespaClientContainerBundle(ContainerCluster<?> c) {
+ c.addPlatformBundle(VESPACLIENT_CONTAINER_BUNDLE);
}
private static void addFeedHandler(ContainerCluster<?> cluster, HandlerOptions handlerOptions) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
index 47dab37cc14..9997b20d205 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
@@ -27,7 +27,6 @@ public final class ApplicationContainer extends Container implements
private static final String defaultHostedJVMArgs = "-XX:+SuppressFatalErrorMessage";
private final boolean isHostedVespa;
- private final boolean enableServerOcspStapling;
public ApplicationContainer(AbstractConfigProducer<?> parent, String name, int index, DeployState deployState) {
this(parent, name, false, index, deployState);
@@ -36,7 +35,6 @@ public final class ApplicationContainer extends Container implements
public ApplicationContainer(AbstractConfigProducer<?> parent, String name, boolean retired, int index, DeployState deployState) {
super(parent, name, retired, index, deployState);
this.isHostedVespa = deployState.isHosted();
- this.enableServerOcspStapling = deployState.featureFlags().enableServerOcspStapling();
addComponent(new SimpleComponent("com.yahoo.container.jdisc.messagebus.NetworkMultiplexerHolder"));
addComponent(new SimpleComponent("com.yahoo.container.jdisc.messagebus.NetworkMultiplexerProvider"));
@@ -68,12 +66,10 @@ public final class ApplicationContainer extends Container implements
if (hasDocproc()) {
b.append(ApplicationContainer.defaultHostedJVMArgs).append(' ');
}
- if (enableServerOcspStapling) {
- b.append("-Djdk.tls.server.enableStatusRequestExtension=true ")
- .append("-Djdk.tls.stapling.responseTimeout=2000 ")
- .append("-Djdk.tls.stapling.cacheSize=256 ")
- .append("-Djdk.tls.stapling.cacheLifetime=3600 ");
- }
+ b.append("-Djdk.tls.server.enableStatusRequestExtension=true ")
+ .append("-Djdk.tls.stapling.responseTimeout=2000 ")
+ .append("-Djdk.tls.stapling.cacheSize=256 ")
+ .append("-Djdk.tls.stapling.cacheLifetime=3600 ");
}
String jvmArgs = super.getJvmOptions();
if (!jvmArgs.isBlank()) {
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 cb4fe8f67ca..937d7cf58d3 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
@@ -78,7 +78,6 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
public static final int heapSizePercentageOfTotalNodeMemory = 70;
public static final int heapSizePercentageOfTotalNodeMemoryWhenCombinedCluster = 18;
-
private final Set<FileReference> applicationBundles = new LinkedHashSet<>();
private final Set<String> previousHosts;
@@ -89,6 +88,8 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
private MbusParams mbusParams;
private boolean messageBusEnabled = true;
+ private final int transport_events_before_wakeup;
+ private final int transport_connections_per_target;
private Integer memoryPercentage = null;
@@ -114,14 +115,8 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
addMetricsHandlers();
addTestrunnerComponentsIfTester(deployState);
- addPlatformBundlesForApplicationCluster();
- }
-
- private void addPlatformBundlesForApplicationCluster() {
- Set<String> bundles = Set.of(
- "container-search-and-docproc", "container-search-gui", "docprocs",
- "linguistics-components", "vespaclient-container-plugin");
- bundles.forEach(b -> addPlatformBundle(PlatformBundles.absoluteBundlePath(b)));
+ transport_connections_per_target = deployState.featureFlags().mbusJavaRpcNumTargets();
+ transport_events_before_wakeup = deployState.featureFlags().mbusJavaEventsBeforeWakeup();
}
@Override
@@ -269,6 +264,8 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
}
if (getDocproc() != null)
getDocproc().getConfig(builder);
+ builder.transport_events_before_wakeup(transport_events_before_wakeup);
+ builder.numconnectionspertarget(transport_connections_per_target);
}
@Override
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 4b1c03a170c..63c323bfdaf 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
@@ -1,7 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container;
-import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.deploy.DeployState;
@@ -148,19 +147,11 @@ public abstract class Container extends AbstractService implements
return (parent instanceof ContainerCluster) ? ((ContainerCluster<?>) parent).getHttp() : null;
}
+ @SuppressWarnings("unused") // used by amenders
public JettyHttpServer getDefaultHttpServer() {
return defaultHttpServer;
}
- public JettyHttpServer getHttpServer() {
- Http http = getHttp();
- if (http == null) {
- return defaultHttpServer;
- } else {
- return http.getHttpServer().orElse(null);
- }
- }
-
/** Returns the index of this node. The index of a given node is stable through changes with best effort. */
public final int index() { return index; }
@@ -381,7 +372,7 @@ public abstract class Container extends AbstractService implements
@Override
public void getConfig(ContainerMbusConfig.Builder builder) {
- builder.enabled(messageBusEnabled()).port(getMessagingPort());
+ builder.port(getMessagingPort());
}
@Override
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
index 5adea6d50e8..2385b5b3812 100755
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
@@ -70,6 +70,8 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Parent class for all container cluster types.
@@ -126,6 +128,10 @@ public abstract class ContainerCluster<CONTAINER extends Container>
public static final BindingPattern VIP_HANDLER_BINDING = SystemBindingPattern.fromHttpPath("/status.html");
+ public static final Set<Path> SEARCH_AND_DOCPROC_BUNDLES = Stream.of(
+ PlatformBundles.searchAndDocprocBundle, "container-search-gui", "docprocs", "linguistics-components")
+ .map(PlatformBundles::absoluteBundlePath).collect(Collectors.toSet());
+
private final String name;
protected List<CONTAINER> containers = new ArrayList<>();
@@ -388,6 +394,18 @@ public abstract class ContainerCluster<CONTAINER extends Container>
return Collections.unmodifiableCollection(allComponents);
}
+ /*
+ Add all search/docproc/feed related platform bundles.
+ This is only required for configured containers as the platform bundle set is not allowed to change between config generations.
+ For standalone container platform bundles can be added on features enabled as an update of application package requires restart.
+ */
+ public void addAllPlatformBundles() {
+ ContainerDocumentApi.addVespaClientContainerBundle(this);
+ addSearchAndDocprocBundles();
+ }
+
+ public void addSearchAndDocprocBundles() { SEARCH_AND_DOCPROC_BUNDLES.forEach(this::addPlatformBundle); }
+
private void recursivelyFindAllComponents(Collection<Component<?, ?>> allComponents, AbstractConfigProducer<?> current) {
for (AbstractConfigProducer<?> child: current.getChildren().values()) {
if (child instanceof Component)
@@ -412,8 +430,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
@Override
public void getConfig(DocumentmanagerConfig.Builder builder) {
- if (containerDocproc != null && containerDocproc.isCompressDocuments())
- builder.enablecompression(true);
if (containerDocumentApi != null)
builder.ignoreundefinedfields(containerDocumentApi.ignoreUndefinedFields());
}
@@ -539,9 +555,7 @@ public abstract class ContainerCluster<CONTAINER extends Container>
@Override
public void getConfig(IlscriptsConfig.Builder builder) {
- List<SearchCluster> searchClusters = new ArrayList<>();
- searchClusters.addAll(Content.getSearchClusters(getRoot().configModelRepo()));
- for (SearchCluster searchCluster : searchClusters) {
+ for (SearchCluster searchCluster : Content.getSearchClusters(getRoot().configModelRepo())) {
searchCluster.getConfig(builder);
}
}
@@ -613,8 +627,6 @@ public abstract class ContainerCluster<CONTAINER extends Container>
public void setEnvironmentVars(String environmentVars) { this.environmentVars = environmentVars; }
- public String getEnvironmentVars() { return environmentVars; }
-
public Optional<String> getJvmGCOptions() { return Optional.ofNullable(jvmGCOptions); }
public final void setRpcServerEnabled(boolean rpcServerEnabled) { this.rpcServerEnabled = rpcServerEnabled; }
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java
index 8be02f77ab3..fee10b965aa 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/ContainerDocproc.java
@@ -27,29 +27,29 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
public final Options options;
// Whether or not to prefer sending to a local node.
- private boolean preferLocalNode = false;
+ private final boolean preferLocalNode = false;
// The number of nodes to use per client.
- private int numNodesPerClient = 0;
+ private final int numNodesPerClient = 0;
- private Map<Pair<String, String>, String> fieldNameSchemaMap = new HashMap<>();
+ private final Map<Pair<String, String>, String> fieldNameSchemaMap = new HashMap<>();
- public ContainerDocproc(ContainerCluster cluster, DocprocChains chains) {
- this(cluster, chains, new Options(false, null, null, null, null, null, null));
+ public ContainerDocproc(ContainerCluster<?> cluster, DocprocChains chains) {
+ this(cluster, chains, new Options( null, null, null, null, null, null));
}
- public ContainerDocproc(ContainerCluster cluster, DocprocChains chains, Options options) {
+ public ContainerDocproc(ContainerCluster<?> cluster, DocprocChains chains, Options options) {
this(cluster, chains, options, true);
}
private void addSource(
- final ContainerCluster cluster, final String name, final SessionConfig.Type.Enum type) {
+ final ContainerCluster<?> cluster, final String name, final SessionConfig.Type.Enum type) {
final MbusClient mbusClient = new MbusClient(name, type);
mbusClient.addClientBindings(SystemBindingPattern.fromPattern("mbus://*/" + mbusClient.getSessionName()));
cluster.addComponent(mbusClient);
}
- public ContainerDocproc(ContainerCluster cluster, DocprocChains chains, Options options, boolean addSourceClientProvider) {
+ public ContainerDocproc(ContainerCluster<?> cluster, DocprocChains chains, Options options, boolean addSourceClientProvider) {
super(chains);
assert (options != null) : "Null Options for " + this + " under cluster " + cluster.getName();
this.options = options;
@@ -58,10 +58,7 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
addSource(cluster, "source", SessionConfig.Type.SOURCE);
addSource(cluster, MbusRequestContext.internalNoThrottledSource, SessionConfig.Type.INTERNAL);
}
- }
-
- public boolean isCompressDocuments() {
- return options.compressDocuments;
+ cluster.addSearchAndDocprocBundles();
}
public boolean isPreferLocalNode() {
@@ -75,8 +72,6 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
@Override
public void getConfig(ContainerMbusConfig.Builder builder) {
builder.maxpendingcount(getMaxMessagesInQueue());
- if (getMaxQueueMbSize() != null)
- builder.maxpendingsize(getMaxQueueMbSize()); //yes, this shall be set in megabytes.
}
private int getMaxMessagesInQueue() {
@@ -137,8 +132,6 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
}
public static class Options {
- // Whether or not to compress documents after processing them.
- public final boolean compressDocuments;
public final Integer maxMessagesInQueue;
public final Integer maxQueueMbSize;
@@ -148,8 +141,7 @@ public class ContainerDocproc extends ContainerSubsystem<DocprocChains>
public final Double documentExpansionFactor;
public final Integer containerCoreMemory;
- public Options(boolean compressDocuments, Integer maxMessagesInQueue, Integer maxQueueMbSize, Integer maxQueueTimeMs, Double maxConcurrentFactor, Double documentExpansionFactor, Integer containerCoreMemory) {
- this.compressDocuments = compressDocuments;
+ public Options(Integer maxMessagesInQueue, Integer maxQueueMbSize, Integer maxQueueTimeMs, Double maxConcurrentFactor, Double documentExpansionFactor, Integer containerCoreMemory) {
this.maxMessagesInQueue = maxMessagesInQueue;
this.maxQueueMbSize = maxQueueMbSize;
this.maxQueueTimeMs = maxQueueTimeMs;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
index 58151063956..3ac12381a1f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
@@ -59,6 +59,7 @@ public class ContainerSearch extends ContainerSubsystem<SearchChains>
owningCluster.addComponent(Component.fromClassAndBundle(QUERY_PROFILE_REGISTRY_CLASS, searchAndDocprocBundle));
owningCluster.addComponent(Component.fromClassAndBundle(com.yahoo.search.schema.SchemaInfo.class.getName(), searchAndDocprocBundle));
owningCluster.addComponent(Component.fromClassAndBundle(SearchStatusExtension.class.getName(), searchAndDocprocBundle));
+ cluster.addSearchAndDocprocBundles();
}
public void connectSearchClusters(Map<String, SearchCluster> searchClusters) {
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 7cc1109f25f..1c47f1d7c9c 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
@@ -205,6 +205,8 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
addServerProviders(deployState, spec, cluster);
+ if (!standaloneBuilder) cluster.addAllPlatformBundles();
+
// Must be added after nodes:
addDeploymentSpecConfig(cluster, context, deployState.getDeployLogger());
addZooKeeper(cluster, spec);
@@ -595,6 +597,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
Element processingElement = XML.getChild(spec, "processing");
if (processingElement == null) return;
+ cluster.addSearchAndDocprocBundles();
addIncludes(processingElement);
cluster.setProcessingChains(new DomProcessingBuilder(null).build(deployState, cluster, processingElement),
serverBindings(processingElement, ProcessingChains.defaultBindings).toArray(BindingPattern[]::new));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocprocOptionsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocprocOptionsBuilder.java
index 1239cbf9bdc..e0dffca2bc5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocprocOptionsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/DocprocOptionsBuilder.java
@@ -10,7 +10,6 @@ import org.w3c.dom.Element;
public class DocprocOptionsBuilder {
public static ContainerDocproc.Options build(Element spec) {
return new ContainerDocproc.Options(
- getCompression(spec),
getMaxMessagesInQueue(spec),
getSizeInMegabytes(spec.getAttribute("maxqueuebytesize")),
getTime(spec.getAttribute("maxqueuewait")),
@@ -25,10 +24,6 @@ public class DocprocOptionsBuilder {
Integer.parseInt(integer);
}
- private static boolean getCompression(Element spec) {
- return (spec.hasAttribute("compressdocuments") && spec.getAttribute("compressdocuments").equals("true"));
- }
-
private static Double getFactor(String factor) {
return factor == null || factor.trim().isEmpty() ?
null :
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java b/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java
index d4595b3adfd..22923a724a6 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java
@@ -298,6 +298,7 @@ public class Content extends ConfigModel {
content.ownedIndexingCluster = Optional.of(indexingCluster);
indexingCluster.addDefaultHandlersWithVip();
+ indexingCluster.addAllPlatformBundles();
addDocproc(indexingCluster);
List<ApplicationContainer> nodes = new ArrayList<>();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java
index 0b0a512a1c8..c52bb6fa2de 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentNode.java
@@ -21,19 +21,21 @@ public abstract class ContentNode extends AbstractService
private final int distributionKey;
private final String rootDirectory;
- private final boolean dispatch_on_encode;
- private final boolean dispatch_on_decode;
- private final int mbus_threads;
private final int mbus_network_threads;
+ private final int mbus_rpc_targets;
+ private final int mbus_events_before_wakeup;
+ private final int rpc_num_targets;
+ private final int rpc_events_before_wakeup;
public ContentNode(ModelContext.FeatureFlags featureFlags, AbstractConfigProducer<?> parent, String clusterName, String rootDirectory, int distributionKey) {
super(parent, "" + distributionKey);
this.distributionKey = distributionKey;
this.rootDirectory = rootDirectory;
- dispatch_on_decode = featureFlags.mbusDispatchOnDecode();
- dispatch_on_encode = featureFlags.mbusDispatchOnEncode();
- mbus_threads = featureFlags.mbusThreads();
mbus_network_threads = featureFlags.mbusNetworkThreads();
+ mbus_rpc_targets = featureFlags.mbusCppRpcNumTargets();
+ mbus_events_before_wakeup = featureFlags.mbusCppEventsBeforeWakeup();
+ rpc_num_targets = featureFlags.rpcNumTargets();
+ rpc_events_before_wakeup = featureFlags.rpcEventsBeforeWakeup();
initialize();
setProp("clustertype", "content");
@@ -77,10 +79,11 @@ public abstract class ContentNode extends AbstractService
public void getConfig(StorCommunicationmanagerConfig.Builder builder) {
builder.mbusport(getRelativePort(0));
builder.rpcport(getRelativePort(1));
- builder.mbus.dispatch_on_decode(dispatch_on_decode);
- builder.mbus.dispatch_on_encode(dispatch_on_encode);
- builder.mbus.num_threads(mbus_threads);
builder.mbus.num_network_threads(mbus_network_threads);
+ builder.mbus.num_rpc_targets(mbus_rpc_targets);
+ builder.mbus.events_before_wakeup(mbus_events_before_wakeup);
+ builder.rpc.num_targets_per_node(rpc_num_targets);
+ builder.rpc.events_before_wakeup(rpc_events_before_wakeup);
}
@Override
diff --git a/config-model/src/main/resources/schema/docproc.rnc b/config-model/src/main/resources/schema/docproc.rnc
index 1e7e28b2002..42902f7180f 100644
--- a/config-model/src/main/resources/schema/docproc.rnc
+++ b/config-model/src/main/resources/schema/docproc.rnc
@@ -22,6 +22,7 @@ DocProcV3 = attribute version { "3.0" },
GenericConfig*
)
+# TODO Here we need a thorough cleaning
DocprocClusterAttributes = attribute compressdocuments { xsd:boolean }? &
attribute numnodesperclient { xsd:positiveInteger }? &
attribute preferlocalnode { xsd:boolean }? &
@@ -32,6 +33,7 @@ DocprocClusterAttributes = attribute compressdocuments { xsd:boolean }? &
attribute documentexpansionfactor { xsd:double { minExclusive = "0.0" } }? &
attribute containercorememory { xsd:nonNegativeInteger }?
+# TODO Here we need a thorough cleaning
ClusterV3 = element cluster {
attribute name { xsd:NCName } &
DocprocClusterAttributes? &
diff --git a/config-model/src/test/configmodel/types/documentmanager.cfg b/config-model/src/test/configmodel/types/documentmanager.cfg
index c471935d9da..0269efcc4ca 100644
--- a/config-model/src/test/configmodel/types/documentmanager.cfg
+++ b/config-model/src/test/configmodel/types/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/documenttypes.cfg b/config-model/src/test/configmodel/types/documenttypes.cfg
index d35928a7a4b..b1c3075df2c 100644
--- a/config-model/src/test/configmodel/types/documenttypes.cfg
+++ b/config-model/src/test/configmodel/types/documenttypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/documenttypes_with_doc_field.cfg b/config-model/src/test/configmodel/types/documenttypes_with_doc_field.cfg
index f8c7dacd3af..be4a18f2ab2 100644
--- a/config-model/src/test/configmodel/types/documenttypes_with_doc_field.cfg
+++ b/config-model/src/test/configmodel/types/documenttypes_with_doc_field.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/references/documentmanager_multiple_imported_fields.cfg b/config-model/src/test/configmodel/types/references/documentmanager_multiple_imported_fields.cfg
index bf2ae28c417..778fe2a44c4 100644
--- a/config-model/src/test/configmodel/types/references/documentmanager_multiple_imported_fields.cfg
+++ b/config-model/src/test/configmodel/types/references/documentmanager_multiple_imported_fields.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/references/documentmanager_refs_to_other_types.cfg b/config-model/src/test/configmodel/types/references/documentmanager_refs_to_other_types.cfg
index 7f93eea8e90..4d0a8396d91 100644
--- a/config-model/src/test/configmodel/types/references/documentmanager_refs_to_other_types.cfg
+++ b/config-model/src/test/configmodel/types/references/documentmanager_refs_to_other_types.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/references/documentmanager_refs_to_same_type.cfg b/config-model/src/test/configmodel/types/references/documentmanager_refs_to_same_type.cfg
index ecb499b5ed3..17d6264e138 100644
--- a/config-model/src/test/configmodel/types/references/documentmanager_refs_to_same_type.cfg
+++ b/config-model/src/test/configmodel/types/references/documentmanager_refs_to_same_type.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/references/documenttypes_multiple_imported_fields.cfg b/config-model/src/test/configmodel/types/references/documenttypes_multiple_imported_fields.cfg
index 6073faca616..9e76cb14b20 100644
--- a/config-model/src/test/configmodel/types/references/documenttypes_multiple_imported_fields.cfg
+++ b/config-model/src/test/configmodel/types/references/documenttypes_multiple_imported_fields.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/references/documenttypes_ref_to_self_type.cfg b/config-model/src/test/configmodel/types/references/documenttypes_ref_to_self_type.cfg
index 92bde94f54a..e8398b159dd 100644
--- a/config-model/src/test/configmodel/types/references/documenttypes_ref_to_self_type.cfg
+++ b/config-model/src/test/configmodel/types/references/documenttypes_ref_to_self_type.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
documenttype[].id 2987301
documenttype[].name "ad"
diff --git a/config-model/src/test/configmodel/types/references/documenttypes_refs_to_other_types.cfg b/config-model/src/test/configmodel/types/references/documenttypes_refs_to_other_types.cfg
index 5ea07de4124..0d817fd4e07 100644
--- a/config-model/src/test/configmodel/types/references/documenttypes_refs_to_other_types.cfg
+++ b/config-model/src/test/configmodel/types/references/documenttypes_refs_to_other_types.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/configmodel/types/references/documenttypes_refs_to_same_type.cfg b/config-model/src/test/configmodel/types/references/documenttypes_refs_to_same_type.cfg
index f31e5f6e7c0..f681a8de9ba 100644
--- a/config-model/src/test/configmodel/types/references/documenttypes_refs_to_same_type.cfg
+++ b/config-model/src/test/configmodel/types/references/documenttypes_refs_to_same_type.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/derived/advanced/documentmanager.cfg b/config-model/src/test/derived/advanced/documentmanager.cfg
index 1ad50f57e93..375d42bc63e 100644
--- a/config-model/src/test/derived/advanced/documentmanager.cfg
+++ b/config-model/src/test/derived/advanced/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationsimplicitstruct/documentmanager.cfg b/config-model/src/test/derived/annotationsimplicitstruct/documentmanager.cfg
index 1ca66a7aea2..c5acbbf87e3 100644
--- a/config-model/src/test/derived/annotationsimplicitstruct/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationsimplicitstruct/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationsinheritance/documentmanager.cfg b/config-model/src/test/derived/annotationsinheritance/documentmanager.cfg
index 8dc07ae8eab..edf5fd13d1b 100644
--- a/config-model/src/test/derived/annotationsinheritance/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationsinheritance/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationsinheritance2/documentmanager.cfg b/config-model/src/test/derived/annotationsinheritance2/documentmanager.cfg
index 67c47032995..d931cae2048 100644
--- a/config-model/src/test/derived/annotationsinheritance2/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationsinheritance2/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationspolymorphy/documentmanager.cfg b/config-model/src/test/derived/annotationspolymorphy/documentmanager.cfg
index dbe6054ce6c..76882afd3c5 100644
--- a/config-model/src/test/derived/annotationspolymorphy/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationspolymorphy/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationsreference/documentmanager.cfg b/config-model/src/test/derived/annotationsreference/documentmanager.cfg
index d27ff5c9d07..ef0166ba4a7 100644
--- a/config-model/src/test/derived/annotationsreference/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationsreference/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationssimple/documentmanager.cfg b/config-model/src/test/derived/annotationssimple/documentmanager.cfg
index a52837c5c06..a076d5a7479 100644
--- a/config-model/src/test/derived/annotationssimple/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationssimple/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationsstruct/documentmanager.cfg b/config-model/src/test/derived/annotationsstruct/documentmanager.cfg
index 4ab3f376d9d..d0b527ddaf3 100644
--- a/config-model/src/test/derived/annotationsstruct/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationsstruct/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/annotationsstructarray/documentmanager.cfg b/config-model/src/test/derived/annotationsstructarray/documentmanager.cfg
index c2078dfa671..80792768c34 100644
--- a/config-model/src/test/derived/annotationsstructarray/documentmanager.cfg
+++ b/config-model/src/test/derived/annotationsstructarray/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/arrays/documentmanager.cfg b/config-model/src/test/derived/arrays/documentmanager.cfg
index 836f3903079..ef3841f180b 100644
--- a/config-model/src/test/derived/arrays/documentmanager.cfg
+++ b/config-model/src/test/derived/arrays/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/attributeprefetch/documentmanager.cfg b/config-model/src/test/derived/attributeprefetch/documentmanager.cfg
index a5c063108e4..5733c7d1bbd 100644
--- a/config-model/src/test/derived/attributeprefetch/documentmanager.cfg
+++ b/config-model/src/test/derived/attributeprefetch/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/complex/documentmanager.cfg b/config-model/src/test/derived/complex/documentmanager.cfg
index dd0b7095d93..f44ab48c255 100644
--- a/config-model/src/test/derived/complex/documentmanager.cfg
+++ b/config-model/src/test/derived/complex/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/declstruct/documentmanager.cfg b/config-model/src/test/derived/declstruct/documentmanager.cfg
index 992d210dbe2..7b95c78d584 100644
--- a/config-model/src/test/derived/declstruct/documentmanager.cfg
+++ b/config-model/src/test/derived/declstruct/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/duplicate_struct/documentmanager.cfg b/config-model/src/test/derived/duplicate_struct/documentmanager.cfg
index 4742a75205a..af20f058eed 100644
--- a/config-model/src/test/derived/duplicate_struct/documentmanager.cfg
+++ b/config-model/src/test/derived/duplicate_struct/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/duplicate_struct/documenttypes.cfg b/config-model/src/test/derived/duplicate_struct/documenttypes.cfg
index 45c1bd8700f..b751b1ebbbf 100644
--- a/config-model/src/test/derived/duplicate_struct/documenttypes.cfg
+++ b/config-model/src/test/derived/duplicate_struct/documenttypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/emptydefault/documentmanager.cfg b/config-model/src/test/derived/emptydefault/documentmanager.cfg
index bf2a39df89f..63732728b05 100644
--- a/config-model/src/test/derived/emptydefault/documentmanager.cfg
+++ b/config-model/src/test/derived/emptydefault/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/id/documentmanager.cfg b/config-model/src/test/derived/id/documentmanager.cfg
index 33cdbe5b996..583ca8497b8 100644
--- a/config-model/src/test/derived/id/documentmanager.cfg
+++ b/config-model/src/test/derived/id/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/imported_fields_inherited_reference/documenttypes.cfg b/config-model/src/test/derived/imported_fields_inherited_reference/documenttypes.cfg
index c6cd1a2949d..aa960e70326 100644
--- a/config-model/src/test/derived/imported_fields_inherited_reference/documenttypes.cfg
+++ b/config-model/src/test/derived/imported_fields_inherited_reference/documenttypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/indexswitches/documentmanager.cfg b/config-model/src/test/derived/indexswitches/documentmanager.cfg
index fa91cb0e554..a902a5954a5 100644
--- a/config-model/src/test/derived/indexswitches/documentmanager.cfg
+++ b/config-model/src/test/derived/indexswitches/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/inheritance/documentmanager.cfg b/config-model/src/test/derived/inheritance/documentmanager.cfg
index 52cac7bfa79..fd4994dbefb 100644
--- a/config-model/src/test/derived/inheritance/documentmanager.cfg
+++ b/config-model/src/test/derived/inheritance/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/inheritdiamond/documentmanager.cfg b/config-model/src/test/derived/inheritdiamond/documentmanager.cfg
index 7d5dacbe00f..9f1207b77a3 100644
--- a/config-model/src/test/derived/inheritdiamond/documentmanager.cfg
+++ b/config-model/src/test/derived/inheritdiamond/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/inheritfromgrandparent/documentmanager.cfg b/config-model/src/test/derived/inheritfromgrandparent/documentmanager.cfg
index 537c452c38c..68503ebd5aa 100644
--- a/config-model/src/test/derived/inheritfromgrandparent/documentmanager.cfg
+++ b/config-model/src/test/derived/inheritfromgrandparent/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/inheritfromparent/documentmanager.cfg b/config-model/src/test/derived/inheritfromparent/documentmanager.cfg
index d246e886a3d..c8fdeaedf11 100644
--- a/config-model/src/test/derived/inheritfromparent/documentmanager.cfg
+++ b/config-model/src/test/derived/inheritfromparent/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/inheritfromparent/documenttypes.cfg b/config-model/src/test/derived/inheritfromparent/documenttypes.cfg
index efd8170d95e..fd9623f716a 100644
--- a/config-model/src/test/derived/inheritfromparent/documenttypes.cfg
+++ b/config-model/src/test/derived/inheritfromparent/documenttypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/mail/onlydoc/documentmanager.cfg b/config-model/src/test/derived/mail/onlydoc/documentmanager.cfg
index 27ac015e630..6bf972c1c80 100644
--- a/config-model/src/test/derived/mail/onlydoc/documentmanager.cfg
+++ b/config-model/src/test/derived/mail/onlydoc/documentmanager.cfg
@@ -1,4 +1,3 @@
-enablecompression false
usev8geopositions false
datatype[].id 1381038251
datatype[].structtype[].name "position"
diff --git a/config-model/src/test/derived/multi_struct/documentmanager.cfg b/config-model/src/test/derived/multi_struct/documentmanager.cfg
index e37a3dc51c6..753196b4d02 100644
--- a/config-model/src/test/derived/multi_struct/documentmanager.cfg
+++ b/config-model/src/test/derived/multi_struct/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/multi_struct/documenttypes.cfg b/config-model/src/test/derived/multi_struct/documenttypes.cfg
index 93452602f86..4926675582b 100644
--- a/config-model/src/test/derived/multi_struct/documenttypes.cfg
+++ b/config-model/src/test/derived/multi_struct/documenttypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/namecollision/documentmanager.cfg b/config-model/src/test/derived/namecollision/documentmanager.cfg
index d8cf44a9a3d..79807197b48 100644
--- a/config-model/src/test/derived/namecollision/documentmanager.cfg
+++ b/config-model/src/test/derived/namecollision/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/prefixexactattribute/documentmanager.cfg b/config-model/src/test/derived/prefixexactattribute/documentmanager.cfg
index d516eaf7886..3158348d5be 100644
--- a/config-model/src/test/derived/prefixexactattribute/documentmanager.cfg
+++ b/config-model/src/test/derived/prefixexactattribute/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/ranktypes/documentmanager.cfg b/config-model/src/test/derived/ranktypes/documentmanager.cfg
index 46457fb479d..ade7795c3ca 100644
--- a/config-model/src/test/derived/ranktypes/documentmanager.cfg
+++ b/config-model/src/test/derived/ranktypes/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/reference_from_several/documentmanager.cfg b/config-model/src/test/derived/reference_from_several/documentmanager.cfg
index 28f40aeee5b..bdcb6a04236 100644
--- a/config-model/src/test/derived/reference_from_several/documentmanager.cfg
+++ b/config-model/src/test/derived/reference_from_several/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/schemainheritance/documentmanager.cfg b/config-model/src/test/derived/schemainheritance/documentmanager.cfg
index 1fe61cf2bd2..a706c57909e 100644
--- a/config-model/src/test/derived/schemainheritance/documentmanager.cfg
+++ b/config-model/src/test/derived/schemainheritance/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/streamingstruct/documentmanager.cfg b/config-model/src/test/derived/streamingstruct/documentmanager.cfg
index b94f23a9c7b..c29ff0b3489 100644
--- a/config-model/src/test/derived/streamingstruct/documentmanager.cfg
+++ b/config-model/src/test/derived/streamingstruct/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/structandfieldset/documentmanager.cfg b/config-model/src/test/derived/structandfieldset/documentmanager.cfg
index e1169e3ca5d..e9bbc06d77a 100644
--- a/config-model/src/test/derived/structandfieldset/documentmanager.cfg
+++ b/config-model/src/test/derived/structandfieldset/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/structanyorder/documentmanager.cfg b/config-model/src/test/derived/structanyorder/documentmanager.cfg
index eac63515944..a1e8bb41a97 100644
--- a/config-model/src/test/derived/structanyorder/documentmanager.cfg
+++ b/config-model/src/test/derived/structanyorder/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/structinheritance/documentmanager.cfg b/config-model/src/test/derived/structinheritance/documentmanager.cfg
index 37240887e3b..5897b00c07b 100644
--- a/config-model/src/test/derived/structinheritance/documentmanager.cfg
+++ b/config-model/src/test/derived/structinheritance/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/structinheritance/documenttypes.cfg b/config-model/src/test/derived/structinheritance/documenttypes.cfg
index 16521b920a7..3f9fdf85734 100644
--- a/config-model/src/test/derived/structinheritance/documenttypes.cfg
+++ b/config-model/src/test/derived/structinheritance/documenttypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/tensor/documentmanager.cfg b/config-model/src/test/derived/tensor/documentmanager.cfg
index f52fe073208..bae2db34040 100644
--- a/config-model/src/test/derived/tensor/documentmanager.cfg
+++ b/config-model/src/test/derived/tensor/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/tensor/documenttypes.cfg b/config-model/src/test/derived/tensor/documenttypes.cfg
index 3081a5bd0c4..d10ecd37c8f 100644
--- a/config-model/src/test/derived/tensor/documenttypes.cfg
+++ b/config-model/src/test/derived/tensor/documenttypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/derived/types/documentmanager.cfg b/config-model/src/test/derived/types/documentmanager.cfg
index 118f7d279e6..ace183e1ab0 100644
--- a/config-model/src/test/derived/types/documentmanager.cfg
+++ b/config-model/src/test/derived/types/documentmanager.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[].name "document"
doctype[].idx 10000
diff --git a/config-model/src/test/examples/fieldoftypedocument-doctypes.cfg b/config-model/src/test/examples/fieldoftypedocument-doctypes.cfg
index 2efc2f40d21..ebc72c2d73b 100644
--- a/config-model/src/test/examples/fieldoftypedocument-doctypes.cfg
+++ b/config-model/src/test/examples/fieldoftypedocument-doctypes.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/examples/fieldoftypedocument.cfg b/config-model/src/test/examples/fieldoftypedocument.cfg
index 10c66ce3e93..5aca758a5f4 100644
--- a/config-model/src/test/examples/fieldoftypedocument.cfg
+++ b/config-model/src/test/examples/fieldoftypedocument.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/examples/structresult.cfg b/config-model/src/test/examples/structresult.cfg
index 2e3904b7110..c2c84eb46b0 100644
--- a/config-model/src/test/examples/structresult.cfg
+++ b/config-model/src/test/examples/structresult.cfg
@@ -1,5 +1,4 @@
ignoreundefinedfields false
-enablecompression false
usev8geopositions false
doctype[0].name "document"
doctype[0].idx 10000
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
index 198b5713876..b634356fcb6 100755
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
@@ -162,15 +162,15 @@ public class ContainerClusterTest {
addContainer(root, cluster, "c1", "host-c1");
assertEquals(1, cluster.getContainers().size());
ApplicationContainer container = cluster.getContainers().get(0);
- verifyJvmArgs(isHosted, hasDocProc, "", container.getJvmOptions());
+ verifyJvmArgs(isHosted, hasDocProc, expectedJvmArgs(isHosted, ""), container.getJvmOptions());
container.setJvmOptions("initial");
- verifyJvmArgs(isHosted, hasDocProc, "initial", container.getJvmOptions());
+ verifyJvmArgs(isHosted, hasDocProc, expectedJvmArgs(isHosted, "initial"), container.getJvmOptions());
container.prependJvmOptions("ignored");
- verifyJvmArgs(isHosted, hasDocProc, "ignored initial", container.getJvmOptions());
+ verifyJvmArgs(isHosted, hasDocProc, expectedJvmArgs(isHosted, "ignored initial"), container.getJvmOptions());
container.appendJvmOptions("override");
- verifyJvmArgs(isHosted, hasDocProc, "ignored initial override", container.getJvmOptions());
+ verifyJvmArgs(isHosted, hasDocProc, expectedJvmArgs(isHosted, "ignored initial override"), container.getJvmOptions());
container.setJvmOptions(null);
- verifyJvmArgs(isHosted, hasDocProc, "", container.getJvmOptions());
+ verifyJvmArgs(isHosted, hasDocProc, expectedJvmArgs(isHosted, ""), container.getJvmOptions());
}
@Test
@@ -510,4 +510,13 @@ public class ContainerClusterTest {
return new ClusterInfoConfig(builder);
}
+ private static String expectedJvmArgs(boolean isHosted, String extra) {
+ if (!isHosted) return extra;
+ return "-Djdk.tls.server.enableStatusRequestExtension=true " +
+ "-Djdk.tls.stapling.responseTimeout=2000 " +
+ "-Djdk.tls.stapling.cacheSize=256 " +
+ "-Djdk.tls.stapling.cacheLifetime=3600" +
+ (extra.isEmpty() ? "" : " " + extra);
+ }
+
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/docproc/StandaloneDocprocContainerTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/docproc/StandaloneDocprocContainerTest.java
deleted file mode 100644
index 5bb93255a8f..00000000000
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/docproc/StandaloneDocprocContainerTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.container.docproc;
-
-import com.yahoo.component.ComponentId;
-import com.yahoo.config.model.deploy.DeployState;
-import com.yahoo.config.model.builder.xml.test.DomBuilderTest;
-import com.yahoo.vespa.model.container.ContainerCluster;
-import com.yahoo.vespa.model.container.ContainerModel;
-import com.yahoo.vespa.model.container.component.Component;
-import com.yahoo.vespa.model.container.xml.ContainerModelBuilder;
-import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking;
-import org.junit.Test;
-import org.w3c.dom.Element;
-
-import java.util.Map;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author Einar M R Rosenvinge
- */
-public class StandaloneDocprocContainerTest extends DomBuilderTest {
-
- public ContainerCluster setupCluster(boolean standalone) {
- ContainerModelBuilder builder = new ContainerModelBuilder(standalone, Networking.disable);
- ContainerModel model = builder.build(DeployState.createTestState(), null, null, root, servicesXml());
-
- if (!standalone)
- model.getCluster().getDocproc().getChains().addServersAndClientsForChains();
-
- root.freezeModelTopology();
- return model.getCluster();
- }
-
- private Element servicesXml() {
- return parse("" +
- "<container version=\"1.0\">\n" +
- " <document-processing>\n" +
- " <chain id=\"foo\">\n" +
- " <documentprocessor id=\"MyDocproc\"/>\n" +
- " </chain>\n" +
- " </document-processing>\n" +
- " <nodes>\n" +
- " <node hostalias=\"node01\"/>\n" +
- " </nodes>\n" +
- "</container>\n");
- }
-
- @Test
- public void requireMbusProvidersWhenNonStandalone() {
- ContainerCluster containerCluster = setupCluster(false);
- Map<ComponentId, Component<?, ?>> components = containerCluster.getComponentsMap();
-
- boolean foundAtLeastOneClient = false;
- boolean foundAtLeastOneServer = false;
-
- for (ComponentId componentId : components.keySet()) {
- if (componentId.stringValue().contains("MbusClient")) foundAtLeastOneClient = true;
- if (componentId.stringValue().contains("MbusServer")) foundAtLeastOneServer = true;
- }
- assertTrue(foundAtLeastOneClient);
- assertTrue(foundAtLeastOneServer);
-
- }
-
- @Test
- public void requireNoMbusProvidersWhenStandalone() {
- ContainerCluster containerCluster = setupCluster(true);
- Map<ComponentId, Component<?, ?>> components = containerCluster.getComponentsMap();
-
- boolean foundAtLeastOneClient = false;
- boolean foundAtLeastOneServer = false;
-
- for (ComponentId componentId : components.keySet()) {
- if (componentId.stringValue().contains("MbusClient")) foundAtLeastOneClient = true;
- if (componentId.stringValue().contains("MbusServer")) foundAtLeastOneServer = true;
- }
- assertFalse(foundAtLeastOneClient);
- assertFalse(foundAtLeastOneServer);
- }
-}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/DocprocBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/DocprocBuilderTest.java
index 77681489dac..2044fd2ab39 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/DocprocBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/DocprocBuilderTest.java
@@ -1,14 +1,12 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.xml;
-import com.yahoo.config.docproc.DocprocConfig;
import com.yahoo.config.docproc.SchemamappingConfig;
import com.yahoo.config.model.builder.xml.test.DomBuilderTest;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.container.ComponentsConfig;
import com.yahoo.container.core.ChainsConfig;
import com.yahoo.container.jdisc.ContainerMbusConfig;
-import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.HostPorts;
import com.yahoo.vespa.model.container.ApplicationContainer;
@@ -38,12 +36,10 @@ import static org.junit.Assert.assertTrue;
public class DocprocBuilderTest extends DomBuilderTest {
private ApplicationContainerCluster cluster;
- private DocumentmanagerConfig documentmanagerConfig;
private ContainerMbusConfig containerMbusConfig;
private ComponentsConfig componentsConfig;
private ChainsConfig chainsConfig;
private SchemamappingConfig schemamappingConfig;
- private DocprocConfig docprocConfig;
private QrStartConfig qrStartConfig;
@Before
@@ -58,10 +54,8 @@ public class DocprocBuilderTest extends DomBuilderTest {
chainsConfig = root.getConfig(ChainsConfig.class,
cluster.getConfigId() + "/component/com.yahoo.docproc.jdisc.DocumentProcessingHandler");
- documentmanagerConfig = root.getConfig(DocumentmanagerConfig.class, cluster.getConfigId());
schemamappingConfig = root.getConfig(SchemamappingConfig.class, cluster.getContainers().get(0).getConfigId());
qrStartConfig = root.getConfig(QrStartConfig.class, cluster.getConfigId());
- docprocConfig = root.getConfig(DocprocConfig.class, cluster.getConfigId());
}
private Element servicesXml() {
@@ -70,7 +64,7 @@ public class DocprocBuilderTest extends DomBuilderTest {
" <nodes>",
" <node hostalias='mockhost' baseport='1500' />",
" </nodes>",
- " <document-processing compressdocuments='true' preferlocalnode='true' numnodesperclient='2' maxqueuebytesize='100m' maxmessagesinqueue='300' maxqueuewait='200'>",
+ " <document-processing preferlocalnode='true' numnodesperclient='2' maxqueuebytesize='100m' maxmessagesinqueue='300' maxqueuewait='200'>",
" <documentprocessor id='docproc1' class='com.yahoo.Docproc1' bundle='docproc1bundle'/>",
" <chain id='chein'>",
" <documentprocessor id='docproc2'/>",
@@ -83,7 +77,6 @@ public class DocprocBuilderTest extends DomBuilderTest {
@Test
public void testDocprocCluster() {
assertEquals("banan", cluster.getName());
- assertTrue(cluster.getDocproc().isCompressDocuments());
//assertTrue(cluster.getContainerDocproc().isPreferLocalNode());
//assertEquals(2, cluster.getContainerDocproc().getNumNodesPerClient());
List<ApplicationContainer> services = cluster.getContainers();
@@ -105,16 +98,9 @@ public class DocprocBuilderTest extends DomBuilderTest {
}
@Test
- public void testDocumentManagerConfig() {
- assertTrue(documentmanagerConfig.enablecompression());
- }
-
- @Test
public void testContainerMbusConfig() {
- assertTrue(containerMbusConfig.enabled());
assertTrue(containerMbusConfig.port() >= HostPorts.BASE_PORT);
assertEquals(300, containerMbusConfig.maxpendingcount());
- assertEquals(100, containerMbusConfig.maxpendingsize());
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
index 2031e74bd5b..015356d6088 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/DistributorTest.java
@@ -273,7 +273,6 @@ public class DistributorTest {
cluster.getChildren().get("0").getConfig(builder);
StorCommunicationmanagerConfig config = new StorCommunicationmanagerConfig(builder);
- assertTrue(config.mbus().dispatch_on_encode());
assertEquals(14066, config.rpcport());
}
@@ -290,9 +289,6 @@ public class DistributorTest {
cluster.getChildren().get("0").getConfig(builder);
StorCommunicationmanagerConfig config = new StorCommunicationmanagerConfig(builder);
- assertTrue(config.mbus().dispatch_on_encode());
- assertTrue(config.mbus().dispatch_on_decode());
- assertEquals(4, config.mbus().num_threads());
assertEquals(1, config.mbus().num_network_threads());
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
index 5748f260bf8..c494ba0394a 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
@@ -101,9 +101,6 @@ public class StorageClusterTest {
StorCommunicationmanagerConfig.Builder builder = new StorCommunicationmanagerConfig.Builder();
storage.getChildren().get("0").getConfig(builder);
StorCommunicationmanagerConfig config = new StorCommunicationmanagerConfig(builder);
- assertTrue(config.mbus().dispatch_on_encode());
- assertTrue(config.mbus().dispatch_on_decode());
- assertEquals(4, config.mbus().num_threads());
assertEquals(1, config.mbus().num_network_threads());
}
@@ -154,23 +151,26 @@ public class StorageClusterTest {
@Test
public void verifyDefaultMbusConfig() {
var confg = communicationmanagerConfigFromProperties(new TestProperties());
- assertTrue(confg.mbus().dispatch_on_decode());
- assertTrue(confg.mbus().dispatch_on_encode());
- assertEquals(4, confg.mbus().num_threads());
assertEquals(1, confg.mbus().num_network_threads());
+ assertEquals(1, confg.mbus().num_rpc_targets());
+ assertEquals(1, confg.mbus().events_before_wakeup());
+ assertEquals(1, confg.rpc().num_targets_per_node());
+ assertEquals(1, confg.rpc().events_before_wakeup());
}
@Test
public void verifyDefaultMbusConfigControl() {
var confg = communicationmanagerConfigFromProperties(new TestProperties()
- .setMbusDispatchOnDecode(false)
- .setMbusDispatchOnEncode(false)
- .setMbusThreads(3)
- .setMbusNetworkThreads(7));
- assertFalse(confg.mbus().dispatch_on_decode());
- assertFalse(confg.mbus().dispatch_on_encode());
- assertEquals(3, confg.mbus().num_threads());
+ .setMbusNetworkThreads(7)
+ .setRpcNumTargets(11)
+ .setRpcEventsBeforeWakeup(12)
+ .setMbusCppRpcNumTargets(8)
+ .setMbusCppEventsBeforeWakeup(9));
assertEquals(7, confg.mbus().num_network_threads());
+ assertEquals(8, confg.mbus().num_rpc_targets());
+ assertEquals(9, confg.mbus().events_before_wakeup());
+ assertEquals(11, confg.rpc().num_targets_per_node());
+ assertEquals(12, confg.rpc().events_before_wakeup());
}
@Test
diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java
index 68570722117..edd16c3d23d 100644
--- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileDistributionAndUrlDownload.java
@@ -6,11 +6,15 @@ import com.yahoo.config.subscription.ConfigSourceSet;
import com.yahoo.jrt.Supervisor;
import com.yahoo.vespa.filedistribution.FileDistributionConnectionPool;
import com.yahoo.vespa.filedistribution.FileDownloader;
-
import java.time.Duration;
+import java.util.Arrays;
+import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
/**
* Keeps track of file distribution and url download rpc servers.
@@ -45,9 +49,16 @@ public class FileDistributionAndUrlDownload {
}
private FileDownloader createDownloader(Supervisor supervisor, ConfigSourceSet source) {
+ Set<CompressionType> acceptedCompressionTypes = Set.of(CompressionType.gzip);
+ String env = System.getenv("VESPA_FILE_DISTRIBUTION_ACCEPTED_COMPRESSION_TYPES");
+ if (env != null && ! env.isEmpty()) {
+ String[] types = env.split(",");
+ acceptedCompressionTypes = Arrays.stream(types).map(CompressionType::valueOf).collect(Collectors.toSet());
+ }
return new FileDownloader(new FileDistributionConnectionPool(source, supervisor),
supervisor,
- Duration.ofMinutes(5));
+ Duration.ofMinutes(5),
+ acceptedCompressionTypes);
}
}
diff --git a/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java b/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java
index aabfd211fac..316edda1ee8 100755
--- a/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java
+++ b/config/src/main/java/com/yahoo/config/subscription/ConfigGetter.java
@@ -1,7 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.subscription;
-
import com.yahoo.config.ConfigInstance;
/**
@@ -13,7 +12,9 @@ import com.yahoo.config.ConfigInstance;
* command-line tools.
*
* @author gjoranv
+ * @deprecated Use config builders where possible
*/
+@Deprecated
public class ConfigGetter<T extends ConfigInstance> {
private final Class<T> clazz;
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java
index fb116729640..68b5d6a37d3 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java
@@ -12,6 +12,7 @@ import static org.junit.Assert.assertTrue;
*
* @author gjoranv
*/
+@SuppressWarnings("deprecation")
public class ConfigGetterTest {
private final ConfigSourceSet sourceSet = new ConfigSourceSet("config-getter-test");
diff --git a/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
index 42c2c599899..8656c0e945f 100644
--- a/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
@@ -2,20 +2,16 @@
package com.yahoo.config.subscription;
import com.yahoo.foo.FunctionTestConfig;
-
import org.junit.Before;
import org.junit.Test;
-
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
-import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -28,6 +24,7 @@ import static org.junit.Assert.fail;
*
* @author gjoranv
*/
+@SuppressWarnings("deprecation")
public class FunctionTest {
public static final String PATH = "src/test/resources/configs/function-test/";
diff --git a/config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java b/config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java
index 963f3de5e43..2e7beb0f0f4 100644
--- a/config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java
@@ -12,6 +12,7 @@ import static org.junit.Assert.assertEquals;
public class NamespaceTest {
@Test
+ @SuppressWarnings("deprecation")
public void verifyConfigClassWithExplicitNamespace() {
NamespaceConfig config = new ConfigGetter<>(NamespaceConfig.class).getConfig("raw: a 0\n");
assertEquals(0, config.a());
diff --git a/config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java b/config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java
index 8864f114d87..c1d98ac7e3e 100644
--- a/config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java
@@ -22,6 +22,7 @@ public class UnicodeTest {
* received correctly from the server
*/
@Test
+ @SuppressWarnings("deprecation")
public void testUnicodeConfigReading() {
ConfigGetter<UnicodeConfig> getter = new ConfigGetter<>(UnicodeConfig.class);
UnicodeConfig config = getter.getConfig("file:src/test/resources/configs/unicode/unicode.cfg");
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 47d1193cd4c..068323f7784 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
@@ -197,7 +197,6 @@ public class ModelContextImpl implements ModelContext {
private final boolean useV8GeoPositions;
private final int maxCompactBuffers;
private final List<String> ignoredHttpUserAgents;
- private final boolean enableServerOcspStapling;
private final String mergeThrottlingPolicy;
private final double persistenceThrottlingWsDecrementFactor;
private final double persistenceThrottlingWsBackoff;
@@ -215,6 +214,12 @@ public class ModelContextImpl implements ModelContext {
private final boolean mbus_dispatch_on_encode;
private final int mbus_threads;
private final int mbus_network_threads;
+ private int mbus_java_num_targets;
+ private int mbus_java_events_before_wakeup;
+ private int mbus_cpp_num_targets;
+ private int mbus_cpp_events_before_wakeup;
+ private int rpc_num_targets;
+ private int rpc_events_before_wakeup;
public FeatureFlags(FlagSource source, ApplicationId appId, Version version) {
this.defaultTermwiseLimit = flagValue(source, appId, version, Flags.DEFAULT_TERM_WISE_LIMIT);
@@ -252,7 +257,6 @@ public class ModelContextImpl implements ModelContext {
this.useV8GeoPositions = flagValue(source, appId, version, Flags.USE_V8_GEO_POSITIONS);
this.maxCompactBuffers = flagValue(source, appId, version, Flags.MAX_COMPACT_BUFFERS);
this.ignoredHttpUserAgents = flagValue(source, appId, version, PermanentFlags.IGNORED_HTTP_USER_AGENTS);
- this.enableServerOcspStapling = flagValue(source, appId, version, Flags.ENABLE_SERVER_OCSP_STAPLING);
this.mergeThrottlingPolicy = flagValue(source, appId, version, Flags.MERGE_THROTTLING_POLICY);
this.persistenceThrottlingWsDecrementFactor = flagValue(source, appId, version, Flags.PERSISTENCE_THROTTLING_WS_DECREMENT_FACTOR);
this.persistenceThrottlingWsBackoff = flagValue(source, appId, version, Flags.PERSISTENCE_THROTTLING_WS_BACKOFF);
@@ -266,6 +270,12 @@ public class ModelContextImpl implements ModelContext {
this.enableProxyProtocolMixedMode = flagValue(source, appId, version, Flags.ENABLE_PROXY_PROTOCOL_MIXED_MODE);
this.sharedStringRepoNoReclaim = flagValue(source, appId, version, Flags.SHARED_STRING_REPO_NO_RECLAIM);
this.logFileCompressionAlgorithm = flagValue(source, appId, version, Flags.LOG_FILE_COMPRESSION_ALGORITHM);
+ this.mbus_java_num_targets = flagValue(source, appId, version, Flags.MBUS_JAVA_NUM_TARGETS);
+ this.mbus_java_events_before_wakeup = flagValue(source, appId, version, Flags.MBUS_JAVA_EVENTS_BEFORE_WAKEUP);
+ this.mbus_cpp_num_targets = flagValue(source, appId, version, Flags.MBUS_CPP_NUM_TARGETS);
+ this.mbus_cpp_events_before_wakeup = flagValue(source, appId, version, Flags.MBUS_CPP_EVENTS_BEFORE_WAKEUP);
+ this.rpc_num_targets = flagValue(source, appId, version, Flags.RPC_NUM_TARGETS);
+ this.rpc_events_before_wakeup = flagValue(source, appId, version, Flags.RPC_EVENTS_BEFORE_WAKEUP);
}
@Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; }
@@ -305,7 +315,6 @@ public class ModelContextImpl implements ModelContext {
@Override public boolean useV8GeoPositions() { return useV8GeoPositions; }
@Override public int maxCompactBuffers() { return maxCompactBuffers; }
@Override public List<String> ignoredHttpUserAgents() { return ignoredHttpUserAgents; }
- @Override public boolean enableServerOcspStapling() { return enableServerOcspStapling; }
@Override public String mergeThrottlingPolicy() { return mergeThrottlingPolicy; }
@Override public double persistenceThrottlingWsDecrementFactor() { return persistenceThrottlingWsDecrementFactor; }
@Override public double persistenceThrottlingWsBackoff() { return persistenceThrottlingWsBackoff; }
@@ -318,6 +327,12 @@ public class ModelContextImpl implements ModelContext {
@Override public Architecture adminClusterArchitecture() { return adminClusterArchitecture; }
@Override public boolean enableProxyProtocolMixedMode() { return enableProxyProtocolMixedMode; }
@Override public boolean sharedStringRepoNoReclaim() { return sharedStringRepoNoReclaim; }
+ @Override public int mbusJavaRpcNumTargets() { return mbus_java_num_targets; }
+ @Override public int mbusJavaEventsBeforeWakeup() { return mbus_java_events_before_wakeup; }
+ @Override public int mbusCppRpcNumTargets() { return mbus_cpp_num_targets; }
+ @Override public int mbusCppEventsBeforeWakeup() { return mbus_cpp_events_before_wakeup; }
+ @Override public int rpcNumTargets() { return rpc_num_targets; }
+ @Override public int rpcEventsBeforeWakeup() { return rpc_events_before_wakeup; }
@Override public String logFileCompressionAlgorithm(String defVal) {
var fflag = this.logFileCompressionAlgorithm;
if (fflag != null && ! fflag.equals("")) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
index d3b7e53157d..7f120a88a05 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileDirectory.java
@@ -141,7 +141,7 @@ public class FileDirectory {
File destination = new File(tempDestinationDir.toFile(), source.getName());
if (!destinationDir.exists()) {
destinationDir.mkdir();
- log.log(Level.FINE, () -> "file reference ' " + reference.value() + "', source: " + source.getAbsolutePath() );
+ log.log(Level.FINE, () -> "file reference '" + reference.value() + "', source: " + source.getAbsolutePath() );
if (source.isDirectory()) {
log.log(Level.FINE, () -> "Copying source " + source.getAbsolutePath() + " to " + destination.getAbsolutePath());
IOUtils.copyDirectory(source, destination, -1);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
index f0c34e83713..770352e6bfc 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/filedistribution/FileServer.java
@@ -21,6 +21,8 @@ import com.yahoo.vespa.filedistribution.FileReferenceData;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
import com.yahoo.vespa.filedistribution.LazyFileReferenceData;
import com.yahoo.vespa.filedistribution.LazyTemporaryStorageFileReferenceData;
+import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.yolean.Exceptions;
import java.io.File;
import java.io.IOException;
@@ -28,6 +30,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -35,12 +38,13 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.getOtherConfigServersInCluster;
import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.gzip;
-import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.lz4;
import static com.yahoo.vespa.filedistribution.FileReferenceData.Type.compressed;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.Type;
public class FileServer {
@@ -52,6 +56,7 @@ public class FileServer {
private final FileDirectory root;
private final ExecutorService executor;
private final FileDownloader downloader;
+ private final List<CompressionType> compressionTypes; // compression types to use, in preferred order
// TODO: Move to filedistribution module, so that it can be used by both clients and servers
private enum FileApiErrorCodes {
@@ -86,21 +91,24 @@ public class FileServer {
@SuppressWarnings("WeakerAccess") // Created by dependency injection
@Inject
- public FileServer(ConfigserverConfig configserverConfig) {
+ public FileServer(ConfigserverConfig configserverConfig, FlagSource flagSource) {
this(new File(Defaults.getDefaults().underVespaHome(configserverConfig.fileReferencesDir())),
- createFileDownloader(getOtherConfigServersInCluster(configserverConfig)));
+ createFileDownloader(getOtherConfigServersInCluster(configserverConfig),
+ compressionTypes(Flags.FILE_DISTRIBUTION_ACCEPTED_COMPRESSION_TYPES.bindTo(flagSource).value())),
+ compressionTypesAsList(Flags.FILE_DISTRIBUTION_COMPRESSION_TYPES_TO_SERVE.bindTo(flagSource).value()));
}
// For testing only
public FileServer(File rootDir) {
- this(rootDir, createFileDownloader(List.of()));
+ this(rootDir, createFileDownloader(List.of(), Set.of(gzip)), List.of(gzip));
}
- public FileServer(File rootDir, FileDownloader fileDownloader) {
+ FileServer(File rootDir, FileDownloader fileDownloader, List<CompressionType> compressionTypes) {
this.downloader = fileDownloader;
this.root = new FileDirectory(rootDir);
this.executor = Executors.newFixedThreadPool(Math.max(8, Runtime.getRuntime().availableProcessors()),
new DaemonThreadFactory("file-server-"));
+ this.compressionTypes = compressionTypes;
}
boolean hasFile(String fileReference) {
@@ -145,10 +153,11 @@ public class FileServer {
if (file.isDirectory()) {
Path tempFile = Files.createTempFile("filereferencedata", reference.value());
CompressionType compressionType = chooseCompressionType(acceptedCompressionTypes);
+ log.log(Level.FINE, () -> "accepted compression types=" + acceptedCompressionTypes + ", compression type to use=" + compressionType);
File compressedFile = new FileReferenceCompressor(compressed, compressionType).compress(file.getParentFile(), tempFile.toFile());
- return new LazyTemporaryStorageFileReferenceData(reference, file.getName(), compressed, compressedFile);
+ return new LazyTemporaryStorageFileReferenceData(reference, file.getName(), compressed, compressedFile, compressionType);
} else {
- return new LazyFileReferenceData(reference, file.getName(), FileReferenceData.Type.file, file);
+ return new LazyFileReferenceData(reference, file.getName(), Type.file, file, gzip);
}
}
@@ -195,9 +204,14 @@ public class FileServer {
return (fileExists ? FileApiErrorCodes.OK : FileApiErrorCodes.NOT_FOUND);
}
- // TODO: Use lz4 for testing only, add zstd when we have support for (de)compressing zstd input and output streams
+ /* Choose the first compression type (list is in preferred order) that matches an accepted compression type, or fail */
private CompressionType chooseCompressionType(Set<CompressionType> acceptedCompressionTypes) {
- return acceptedCompressionTypes.contains(lz4) ? lz4 : gzip;
+ for (CompressionType compressionType : compressionTypes) {
+ if (acceptedCompressionTypes.contains(compressionType))
+ return compressionType;
+ }
+ throw new RuntimeException("Could not find a compression type that can be used. Accepted compression types: " +
+ acceptedCompressionTypes + ", compression types server can use: " + compressionTypes);
}
boolean hasFileDownloadIfNeeded(FileReferenceDownload fileReferenceDownload) {
@@ -228,14 +242,27 @@ public class FileServer {
executor.shutdown();
}
- private static FileDownloader createFileDownloader(List<String> configServers) {
+ private static FileDownloader createFileDownloader(List<String> configServers, Set<CompressionType> acceptedCompressionTypes) {
Supervisor supervisor = new Supervisor(new Transport("filedistribution-pool")).setDropEmptyBuffers(true);
return new FileDownloader(configServers.isEmpty()
? FileDownloader.emptyConnectionPool()
: createConnectionPool(configServers, supervisor),
supervisor,
- timeout);
+ timeout,
+ acceptedCompressionTypes);
+ }
+
+ private static LinkedHashSet<CompressionType> compressionTypes(List<String> compressionTypes) {
+ return compressionTypes.stream()
+ .map(CompressionType::valueOf)
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ }
+
+ private static List<CompressionType> compressionTypesAsList(List<String> compressionTypes) {
+ return compressionTypes.stream()
+ .map(CompressionType::valueOf)
+ .collect(Collectors.toList());
}
private static ConnectionPool createConnectionPool(List<String> configServers, Supervisor supervisor) {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java
index f7042b49c3f..0d4baa7dc56 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/ProxyResponse.java
@@ -38,4 +38,9 @@ class ProxyResponse extends HttpResponse {
}
}
+ @Override
+ public long maxPendingBytes() {
+ return 1 << 25; // 32MB
+ }
+
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
index ae4b205c06e..12972e5c465 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
@@ -18,20 +18,23 @@ import com.yahoo.vespa.filedistribution.FileDistributionConnectionPool;
import com.yahoo.vespa.filedistribution.FileDownloader;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
import com.yahoo.vespa.flags.FlagSource;
-
+import com.yahoo.vespa.flags.Flags;
import java.io.File;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.fileReferenceExistsOnDisk;
import static com.yahoo.vespa.config.server.filedistribution.FileDistributionUtil.getOtherConfigServersInCluster;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
/**
* Verifies that all active sessions has an application package on local disk.
* If not, the package is downloaded with file distribution. This can happen e.g.
- * if a configserver is down when the application is deployed.
+ * if a config server is down when the application is deployed.
*
* @author gjoranv
*/
@@ -53,7 +56,10 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer {
this.applicationRepository = applicationRepository;
this.configserverConfig = applicationRepository.configserverConfig();
this.downloadDirectory = new File(Defaults.getDefaults().underVespaHome(configserverConfig.fileReferencesDir()));
- this.fileDownloader = createFileDownloader(configserverConfig, downloadDirectory, supervisor);
+ this.fileDownloader = createFileDownloader(configserverConfig,
+ downloadDirectory,
+ supervisor,
+ Flags.FILE_DISTRIBUTION_ACCEPTED_COMPRESSION_TYPES.bindTo(flagSource).value());
}
@Override
@@ -94,14 +100,18 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer {
private static FileDownloader createFileDownloader(ConfigserverConfig configserverConfig,
File downloadDirectory,
- Supervisor supervisor) {
+ Supervisor supervisor,
+ List<String> flagValues) {
List<String> otherConfigServersInCluster = getOtherConfigServersInCluster(configserverConfig);
ConfigSourceSet configSourceSet = new ConfigSourceSet(otherConfigServersInCluster);
ConnectionPool connectionPool = (otherConfigServersInCluster.isEmpty())
? FileDownloader.emptyConnectionPool()
: new FileDistributionConnectionPool(configSourceSet, supervisor);
- return new FileDownloader(connectionPool, supervisor, downloadDirectory, Duration.ofSeconds(300));
+ Set<CompressionType> acceptedCompressionTypes = flagValues.stream()
+ .map(CompressionType::valueOf)
+ .collect(Collectors.toSet());
+ return new FileDownloader(connectionPool, supervisor, downloadDirectory, Duration.ofSeconds(300), acceptedCompressionTypes);
}
@Override
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
index 687cb1d3cca..7993e58d06e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/RpcServer.java
@@ -491,6 +491,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
sendParts(session, fileData);
sendEof(session, fileData, status);
}
+
private void sendParts(int session, FileReferenceData fileData) {
ByteBuffer bb = ByteBuffer.allocate(0x100000);
for (int partId = 0, read = fileData.nextContent(bb); read >= 0; partId++, read = fileData.nextContent(bb)) {
@@ -504,12 +505,9 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
bb.clear();
}
}
+
private int sendMeta(FileReferenceData fileData) {
- Request request = new Request(FileReceiver.RECEIVE_META_METHOD);
- request.parameters().add(new StringValue(fileData.fileReference().value()));
- request.parameters().add(new StringValue(fileData.filename()));
- request.parameters().add(new StringValue(fileData.type().name()));
- request.parameters().add(new Int64Value(fileData.size()));
+ Request request = createMetaRequest(fileData);
invokeRpcIfValidConnection(request);
if (request.isError()) {
log.warning("Failed delivering meta for reference '" + fileData.fileReference().value() + "' with file '" + fileData.filename() + "' to " +
@@ -522,6 +520,20 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
return request.returnValues().get(1).asInt32();
}
}
+
+ // non-private for testing
+ static Request createMetaRequest(FileReferenceData fileData) {
+ Request request = new Request(FileReceiver.RECEIVE_META_METHOD);
+ request.parameters().add(new StringValue(fileData.fileReference().value()));
+ request.parameters().add(new StringValue(fileData.filename()));
+ request.parameters().add(new StringValue(fileData.type().name()));
+ request.parameters().add(new Int64Value(fileData.size()));
+ // Only add paramter if not gzip, this is default and old clients will not handle the extra parameter
+ if (fileData.compressionType() != CompressionType.gzip)
+ request.parameters().add(new StringValue(fileData.compressionType().name()));
+ return request;
+ }
+
private void sendPart(int session, FileReference ref, int partId, byte [] buf) {
Request request = new Request(FileReceiver.RECEIVE_PART_METHOD);
request.parameters().add(new StringValue(ref.value()));
@@ -538,6 +550,7 @@ public class RpcServer implements Runnable, ReloadListener, TenantListener {
}
}
}
+
private void sendEof(int session, FileReferenceData fileData, FileServer.ReplayStatus status) {
Request request = new Request(FileReceiver.RECEIVE_EOF_METHOD);
request.parameters().add(new StringValue(fileData.fileReference().value()));
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 a144940e443..d803488cb0a 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
@@ -44,13 +44,11 @@ import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.StringFlag;
import com.yahoo.yolean.Exceptions;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.zookeeper.KeeperException;
-
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
@@ -83,6 +81,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
import static com.yahoo.vespa.curator.Curator.CompletionWaiter;
+import static com.yahoo.vespa.flags.FetchVector.Dimension.APPLICATION_ID;
import static java.nio.file.Files.readAttributes;
/**
@@ -128,7 +127,6 @@ public class SessionRepository {
private final ModelFactoryRegistry modelFactoryRegistry;
private final ConfigDefinitionRepo configDefinitionRepo;
private final int maxNodeSize;
- private final StringFlag failDeploymentForFilesWithUnknownExtension;
public SessionRepository(TenantName tenantName,
TenantApplications applicationRepo,
@@ -172,7 +170,6 @@ public class SessionRepository {
this.modelFactoryRegistry = modelFactoryRegistry;
this.configDefinitionRepo = configDefinitionRepo;
this.maxNodeSize = maxNodeSize;
- this.failDeploymentForFilesWithUnknownExtension = Flags.APPLICATION_FILES_WITH_UNKNOWN_EXTENSION.bindTo(flagSource);
loadSessions(); // Needs to be done before creating cache below
this.directoryCache = curator.createDirectoryCache(sessionsPath.getAbsolute(), false, false, zkCacheExecutor);
@@ -684,7 +681,10 @@ public class SessionRepository {
try {
app.validateFileExtensions();
} catch (IllegalArgumentException e) {
- switch (failDeploymentForFilesWithUnknownExtension.value()) {
+ String flag = Flags.APPLICATION_FILES_WITH_UNKNOWN_EXTENSION.bindTo(flagSource)
+ .with(APPLICATION_ID, applicationId.serializedForm())
+ .value();
+ switch (flag) {
case "FAIL":
throw e;
case "LOG":
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java
index 9498db1d1e0..39219471bb1 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/filedistribution/FileServerTest.java
@@ -8,8 +8,10 @@ import com.yahoo.jrt.Supervisor;
import com.yahoo.jrt.Transport;
import com.yahoo.net.HostName;
import com.yahoo.vespa.filedistribution.FileDownloader;
+import com.yahoo.vespa.filedistribution.FileReferenceCompressor;
import com.yahoo.vespa.filedistribution.FileReferenceData;
import com.yahoo.vespa.filedistribution.FileReferenceDownload;
+import com.yahoo.vespa.flags.InMemoryFlagSource;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -39,7 +41,7 @@ public class FileServerTest {
@Before
public void setup() throws IOException {
File rootDir = new File(temporaryFolder.newFolder("fileserver-root").getAbsolutePath());
- fileServer = new FileServer(rootDir, new MockFileDownloader(rootDir));
+ fileServer = new FileServer(rootDir, new MockFileDownloader(rootDir), List.of(gzip, lz4));
}
@Test
@@ -79,11 +81,25 @@ public class FileServerTest {
CompletableFuture<byte []> content = new CompletableFuture<>();
fileServer.startFileServing(new FileReference("12y"), new FileReceiver(content), Set.of(gzip));
assertEquals(new String(content.get()), "dummy-data");
+ }
- IOUtils.writeFile(dir + "/12z/f1", "dummy-data-2", true);
- content = new CompletableFuture<>();
- fileServer.startFileServing(new FileReference("12z"), new FileReceiver(content), Set.of(gzip, lz4));
- assertEquals(new String(content.get()), "dummy-data-2");
+ @Test
+ public void requireThatWeCanReplayDirWithLz4() throws IOException, InterruptedException, ExecutionException {
+ File rootDir = new File(temporaryFolder.newFolder("fileserver-root-3").getAbsolutePath());
+ fileServer = new FileServer(rootDir, new MockFileDownloader(rootDir), List.of(lz4, gzip)); // prefer lz4
+ File dir = getFileServerRootDir();
+ IOUtils.writeFile(dir + "/subdir/12z/f1", "dummy-data-2", true);
+ CompletableFuture<byte []> content = new CompletableFuture<>();
+ fileServer.startFileServing(new FileReference("subdir"), new FileReceiver(content), Set.of(gzip, lz4));
+
+ // Decompress with lz4 and check contents
+ var compressor = new FileReferenceCompressor(FileReferenceData.Type.compressed, lz4);
+ File downloadedFileCompressed = new File(dir + "/downloaded-file-compressed");
+ IOUtils.writeFile(downloadedFileCompressed, content.get());
+ File downloadedFileUncompressed = new File(dir + "/downloaded-file-uncompressed");
+ compressor.decompress(downloadedFileCompressed, downloadedFileUncompressed);
+ assertTrue(downloadedFileUncompressed.isDirectory());
+ assertEquals("dummy-data-2", IOUtils.readFile(new File(downloadedFileUncompressed, "12z/f1")));
}
@Test
@@ -124,7 +140,7 @@ public class FileServerTest {
private FileServer createFileServer(ConfigserverConfig.Builder configBuilder) throws IOException {
File fileReferencesDir = temporaryFolder.newFolder();
configBuilder.fileReferencesDir(fileReferencesDir.getAbsolutePath());
- return new FileServer(new ConfigserverConfig(configBuilder));
+ return new FileServer(new ConfigserverConfig(configBuilder), new InMemoryFlagSource());
}
private static class FileReceiver implements FileServer.Receiver {
@@ -149,7 +165,8 @@ public class FileServerTest {
new Supervisor(new Transport("mock")).setDropEmptyBuffers(true),
downloadDirectory,
Duration.ofMillis(100),
- Duration.ofMillis(100));
+ Duration.ofMillis(100),
+ Set.of(gzip));
}
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java
index 7ad237e45ed..8607fc0e2dc 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java
@@ -5,6 +5,7 @@ import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.cloud.config.LbServicesConfig;
import com.yahoo.cloud.config.SentinelConfig;
import com.yahoo.component.Version;
+import com.yahoo.config.FileReference;
import com.yahoo.config.SimpletypesConfig;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
@@ -27,16 +28,20 @@ import com.yahoo.vespa.config.server.application.Application;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import com.yahoo.vespa.config.server.session.PrepareParams;
+import com.yahoo.vespa.filedistribution.LazyFileReferenceData;
import com.yahoo.vespa.model.VespaModel;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.xml.sax.SAXException;
-
import java.io.File;
import java.io.IOException;
import java.util.Optional;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.gzip;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.lz4;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.Type.compressed;
+import static com.yahoo.vespa.config.server.rpc.RpcServer.ChunkedFileReceiver.createMetaRequest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -97,6 +102,25 @@ public class RpcServerTest {
}
}
+ @Test
+ public void testFileReceiverMetaRequest() throws IOException {
+ File file = temporaryFolder.newFile();
+ Request request = createMetaRequest(new LazyFileReferenceData(new FileReference("foo"), "fileA", compressed, file, gzip));
+ assertEquals(4, request.parameters().size());
+ assertEquals("foo", request.parameters().get(0).asString());
+ assertEquals("fileA", request.parameters().get(1).asString());
+ assertEquals("compressed", request.parameters().get(2).asString());
+ assertEquals(0, request.parameters().get(3).asInt64());
+
+ request = createMetaRequest(new LazyFileReferenceData(new FileReference("foo"), "fileA", compressed, file, lz4));
+ assertEquals(5, request.parameters().size());
+ assertEquals("foo", request.parameters().get(0).asString());
+ assertEquals("fileA", request.parameters().get(1).asString());
+ assertEquals("compressed", request.parameters().get(2).asString());
+ assertEquals(0, request.parameters().get(3).asInt64());
+ assertEquals("lz4", request.parameters().get(4).asString());
+ }
+
private JRTClientConfigRequest createSimpleRequest() {
ConfigKey<?> key = new ConfigKey<>(SimpletypesConfig.class, "");
JRTClientConfigRequest clientReq = createRequest(new RawConfig(key, SimpletypesConfig.getDefMd5()));
diff --git a/container-core/abi-spec.json b/container-core/abi-spec.json
index 1378008b546..9c14c03ba21 100644
--- a/container-core/abi-spec.json
+++ b/container-core/abi-spec.json
@@ -1091,6 +1091,7 @@
"public com.yahoo.jdisc.http.ConnectorConfig$HealthCheckProxy$Builder enable(boolean)",
"public com.yahoo.jdisc.http.ConnectorConfig$HealthCheckProxy$Builder port(int)",
"public com.yahoo.jdisc.http.ConnectorConfig$HealthCheckProxy$Builder clientTimeout(double)",
+ "public com.yahoo.jdisc.http.ConnectorConfig$HealthCheckProxy$Builder handlerTimeout(double)",
"public com.yahoo.jdisc.http.ConnectorConfig$HealthCheckProxy$Builder cacheExpiry(double)",
"public com.yahoo.jdisc.http.ConnectorConfig$HealthCheckProxy build()"
],
@@ -1108,6 +1109,7 @@
"public boolean enable()",
"public int port()",
"public double clientTimeout()",
+ "public double handlerTimeout()",
"public double cacheExpiry()"
],
"fields": []
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
index b949edefb31..8fa658bf7fc 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
@@ -22,9 +22,9 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import static com.yahoo.container.jdisc.state.JsonUtil.sanitizeDouble;
@@ -61,6 +61,7 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
private final Timer timer;
private final SnapshotProvider snapshotProvider;
private final String applicationName;
+ private final String hostDimension;
@Inject
public MetricsPacketsHandler(StateMonitor monitor,
@@ -71,6 +72,7 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
this.timer = timer;
snapshotProvider = getSnapshotProviderOrThrow(snapshotProviders);
applicationName = config.application();
+ hostDimension = config.hostname();
}
@@ -173,16 +175,16 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
}
private void addDimensions(MetricDimensions metricDimensions, ObjectNode packet) {
- if (metricDimensions == null) return;
-
- Iterator<Map.Entry<String, String>> dimensionsIterator = metricDimensions.iterator();
- if (dimensionsIterator.hasNext()) {
- ObjectNode jsonDim = jsonMapper.createObjectNode();
- packet.set(DIMENSIONS_KEY, jsonDim);
- for (Map.Entry<String, String> dimensionEntry : metricDimensions) {
- jsonDim.put(dimensionEntry.getKey(), dimensionEntry.getValue());
- }
+ if (metricDimensions == null && hostDimension.isEmpty()) return;
+
+ ObjectNode jsonDim = jsonMapper.createObjectNode();
+ packet.set(DIMENSIONS_KEY, jsonDim);
+ Iterable<Map.Entry<String, String>> dimensionIterator = metricDimensions == null ? Set.of() : metricDimensions;
+ for (Map.Entry<String, String> dimensionEntry : dimensionIterator) {
+ jsonDim.put(dimensionEntry.getKey(), dimensionEntry.getValue());
}
+ if (!hostDimension.isEmpty() && !jsonDim.has("host"))
+ jsonDim.put("host", hostDimension);
}
private void addMetrics(MetricSet metricSet, ObjectNode packet) {
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java
index fcb243e094d..f559a368fe3 100644
--- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java
@@ -66,8 +66,10 @@ class HealthCheckProxyHandler extends HandlerWrapper {
ConnectorConfig.HealthCheckProxy proxyConfig = connector.connectorConfig().healthCheckProxy();
if (proxyConfig.enable()) {
Duration targetTimeout = Duration.ofMillis((int) (proxyConfig.clientTimeout() * 1000));
+ Duration handlerTimeout = Duration.ofMillis((int) (proxyConfig.handlerTimeout() * 1000));
Duration cacheExpiry = Duration.ofMillis((int) (proxyConfig.cacheExpiry() * 1000));
- ProxyTarget target = createProxyTarget(proxyConfig.port(), targetTimeout, cacheExpiry, connectors);
+ ProxyTarget target = createProxyTarget(
+ proxyConfig.port(), targetTimeout, handlerTimeout, cacheExpiry, connectors);
mapping.put(connector.listenPort(), target);
log.info(String.format("Port %1$d is configured as a health check proxy for port %2$d. " +
"HTTP requests to '%3$s' on %1$d are proxied as HTTPS to %2$d.",
@@ -77,8 +79,8 @@ class HealthCheckProxyHandler extends HandlerWrapper {
return mapping;
}
- private static ProxyTarget createProxyTarget(int targetPort, Duration targetTimeout, Duration cacheExpiry,
- List<JDiscServerConnector> connectors) {
+ private static ProxyTarget createProxyTarget(int targetPort, Duration clientTimeout, Duration handlerTimeout,
+ Duration cacheExpiry, List<JDiscServerConnector> connectors) {
JDiscServerConnector targetConnector = connectors.stream()
.filter(connector -> connector.listenPort() == targetPort)
.findAny()
@@ -91,7 +93,7 @@ class HealthCheckProxyHandler extends HandlerWrapper {
.orElseThrow(() -> new IllegalArgumentException("Health check proxy can only target https port"));
ConnectorConfig.ProxyProtocol proxyProtocolCfg = targetConnector.connectorConfig().proxyProtocol();
boolean proxyProtocol = proxyProtocolCfg.enabled() && !proxyProtocolCfg.mixedMode();
- return new ProxyTarget(targetPort, targetTimeout, cacheExpiry, sslContextFactory, proxyProtocol);
+ return new ProxyTarget(targetPort, clientTimeout,handlerTimeout, cacheExpiry, sslContextFactory, proxyProtocol);
}
@Override
@@ -103,7 +105,7 @@ class HealthCheckProxyHandler extends HandlerWrapper {
ServletOutputStream out = servletResponse.getOutputStream();
if (servletRequest.getRequestURI().equals(HEALTH_CHECK_PATH)) {
ProxyRequestTask task = new ProxyRequestTask(asyncContext, proxyTarget, servletResponse, out);
- asyncContext.setTimeout(proxyTarget.timeout.plusSeconds(1).toMillis()); // add additional time for response sending
+ asyncContext.setTimeout(proxyTarget.handlerTimeout.toMillis());
asyncContext.addListener(new AsyncListener() {
@Override public void onStartAsync(AsyncEvent event) {}
@Override public void onComplete(AsyncEvent event) {}
@@ -212,20 +214,22 @@ class HealthCheckProxyHandler extends HandlerWrapper {
private static class ProxyTarget implements AutoCloseable {
final int port;
- final Duration timeout;
+ final Duration clientTimeout;
+ final Duration handlerTimeout;
final Duration cacheExpiry;
final SslContextFactory.Server serverSsl;
final boolean proxyProtocol;
volatile HttpClient client;
volatile StatusResponse lastResponse;
- ProxyTarget(int port, Duration timeout, Duration cacheExpiry, SslContextFactory.Server serverSsl,
- boolean proxyProtocol) {
+ ProxyTarget(int port, Duration clientTimeout, Duration handlerTimeout, Duration cacheExpiry,
+ SslContextFactory.Server serverSsl, boolean proxyProtocol) {
this.port = port;
- this.timeout = timeout;
+ this.clientTimeout = clientTimeout;
this.cacheExpiry = cacheExpiry;
this.serverSsl = serverSsl;
this.proxyProtocol = proxyProtocol;
+ this.handlerTimeout = handlerTimeout;
}
StatusResponse requestStatusHtml() {
@@ -239,8 +243,8 @@ class HealthCheckProxyHandler extends HandlerWrapper {
private StatusResponse getStatusResponse() {
try {
var request = client().newRequest("https://localhost:" + port + HEALTH_CHECK_PATH);
- request.timeout(timeout.toMillis(), TimeUnit.MILLISECONDS);
- request.idleTimeout(timeout.toMillis(), TimeUnit.MILLISECONDS);
+ request.timeout(clientTimeout.toMillis(), TimeUnit.MILLISECONDS);
+ request.idleTimeout(clientTimeout.toMillis(), TimeUnit.MILLISECONDS);
if (proxyProtocol) {
request.tag(new ProxyProtocolClientConnectionFactory.V1.Tag());
}
@@ -265,7 +269,7 @@ class HealthCheckProxyHandler extends HandlerWrapper {
if (client == null) {
synchronized (this) {
if (client == null) {
- int timeoutMillis = (int) timeout.toMillis();
+ int timeoutMillis = (int) clientTimeout.toMillis();
SslContextFactory.Client clientSsl = new SslContextFactory.Client();
clientSsl.setHostnameVerifier((__, ___) -> true);
clientSsl.setSslContext(getSslContext(serverSsl));
diff --git a/container-core/src/main/resources/configdefinitions/container.jdisc.state.metrics-packets-handler.def b/container-core/src/main/resources/configdefinitions/container.jdisc.state.metrics-packets-handler.def
index 9ec81b7db1b..ab0362c3995 100644
--- a/container-core/src/main/resources/configdefinitions/container.jdisc.state.metrics-packets-handler.def
+++ b/container-core/src/main/resources/configdefinitions/container.jdisc.state.metrics-packets-handler.def
@@ -4,3 +4,5 @@ namespace=container.jdisc.state
# The name of the application that is reporting metrics.
application string
+# Optional hostname to add as dimension
+hostname string default="" \ No newline at end of file
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 2a8e7159b2f..e808b565e8b 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
@@ -101,9 +101,12 @@ healthCheckProxy.enable bool default=false
# Which port to proxy
healthCheckProxy.port int default=8080
-# Low-level timeout for proxy client (socket connect, socket read, connection pool). Aggregate timeout will be longer.
+# Low-level timeout for proxy client (socket connect, socket read, connection pool).
healthCheckProxy.clientTimeout double default=1.0
+# Servlet async request timeout. Must be larger than 'clientTimeout' to cover cost of queueing and response handling.
+healthCheckProxy.handlerTimeout double default=1.5
+
# Expiry for cached health response
healthCheckProxy.cacheExpiry double default=1.0
diff --git a/container-core/src/main/sh/find-pid b/container-core/src/main/sh/find-pid
index 4db6562db1b..57393140e0b 100755
--- a/container-core/src/main/sh/find-pid
+++ b/container-core/src/main/sh/find-pid
@@ -74,7 +74,7 @@ findhost
# END environment bootstrap section
-set -euo pipefail
+set -uo pipefail
if (( $# != 1 )); then
echo "Usage: $0 <service-name-or-config-id>" >&2
diff --git a/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java b/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
index 4d44281658c..f8f567e1890 100644
--- a/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
+++ b/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
@@ -50,6 +50,7 @@ public class ComponentGraphTest {
super();
}
+ @SuppressWarnings("deprecation")
public <T extends ConfigInstance> ConfigMap add(Class<T> clazz, String configId) {
ConfigKey<T> key = new ConfigKey<>(clazz, configId);
put(key, ConfigGetter.getConfig(key.getConfigClass(), key.getConfigId()));
diff --git a/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java b/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java
index 29452f7babe..2215fcdf4dd 100644
--- a/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java
+++ b/container-core/src/test/java/com/yahoo/container/di/componentgraph/core/ReuseComponentsTest.java
@@ -62,6 +62,7 @@ public class ReuseComponentsTest {
SimpleComponent throwsException = getComponent(newGraph, SimpleComponent.class);
}
+ @SuppressWarnings("deprecation")
@Test
public void require_that_component_is_not_reused_when_config_is_changed() {
Class<ComponentTakingConfig> componentClass = ComponentTakingConfig.class;
@@ -80,6 +81,7 @@ public class ReuseComponentsTest {
assertNotSame(instance2, instance);
}
+ @SuppressWarnings("deprecation")
@Test
public void require_that_component_is_not_reused_when_injected_component_is_changed() {
Function<String, ComponentGraph> buildGraph = config -> {
@@ -144,6 +146,7 @@ public class ReuseComponentsTest {
assertNotSame(newSimpleComponentRegistry, oldSimpleComponentRegistry);
}
+ @SuppressWarnings("deprecation")
@Test
public void require_that_injected_component_is_reused_even_when_dependent_component_is_changed() {
Function<String, ComponentGraph> buildGraph = config -> {
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java
index 99a31640429..6c05af95289 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java
@@ -27,13 +27,14 @@ import static org.junit.Assert.assertTrue;
public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
private static final String APPLICATION_NAME = "state-handler-test-base";
+ private static final String HOST_DIMENSION = "some-hostname";
private static MetricsPacketsHandler metricsPacketsHandler;
@Before
public void setupHandler() {
metricsPacketsHandlerConfig = new MetricsPacketsHandlerConfig(new MetricsPacketsHandlerConfig.Builder()
- .application(APPLICATION_NAME));
+ .application(APPLICATION_NAME).hostname(HOST_DIMENSION));
metricsPacketsHandler = new MetricsPacketsHandler(monitor, timer, snapshotProviderRegistry, metricsPacketsHandlerConfig);
testDriver = new RequestHandlerTestDriver(metricsPacketsHandler);
}
@@ -138,6 +139,26 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
assertEquals(3, packets.size());
}
+
+ @Test
+ public void host_dimension_only_created_if_absent() throws Exception {
+ var context1 = StateMetricContext.newInstance(Map.of("dim1", "value1", "host", "foo.bar"));
+ var context2 = StateMetricContext.newInstance(Map.of("dim2", "value2"));
+ var snapshot = new MetricSnapshot();
+ snapshot.add(context1, "counter1", 1);
+ snapshot.add(context2, "counter2", 2);
+ snapshotProvider.setSnapshot(snapshot);
+
+ var packets = incrementTimeAndGetJsonPackets();
+ assertEquals(3, packets.size());
+
+ packets.forEach(packet -> {
+ if (!packet.has(DIMENSIONS_KEY)) return;
+ var dimensions = packet.get(DIMENSIONS_KEY);
+ if (dimensions.has("dim1")) assertDimension(packet, "host", "foo.bar");
+ if (dimensions.has("dim2")) assertDimension(packet, "host", HOST_DIMENSION);
+ });
+ }
private List<JsonNode> incrementTimeAndGetJsonPackets() throws Exception {
advanceToNextSnapshot();
@@ -163,6 +184,13 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
assertEquals(expected, counterMetrics.get(metricName).asLong());
}
+ private void assertDimension(JsonNode metricsPacket, String dimensionName, String expectedDimensionValue) {
+ assertTrue(metricsPacket.has(DIMENSIONS_KEY));
+ var dimensions = metricsPacket.get(DIMENSIONS_KEY);
+ assertTrue(dimensions.has(dimensionName));
+ assertEquals(expectedDimensionValue, dimensions.get(dimensionName).asText());
+ }
+
private void createSnapshotWithCountMetric(String name, Number value, MetricDimensions context) {
var snapshot = new MetricSnapshot();
snapshot.add(context, name, value);
diff --git a/container-messagebus/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java b/container-messagebus/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java
index e3042310ad0..cf50eeb2c8d 100644
--- a/container-messagebus/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java
+++ b/container-messagebus/src/main/java/com/yahoo/messagebus/shared/SharedMessageBus.java
@@ -1,12 +1,9 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.messagebus.shared;
+import com.yahoo.cloud.config.SlobroksConfig;
import com.yahoo.config.subscription.ConfigGetter;
import com.yahoo.jdisc.AbstractResource;
-
-import java.util.Objects;
-import java.util.logging.Level;
-
import com.yahoo.messagebus.DestinationSessionParams;
import com.yahoo.messagebus.IntermediateSessionParams;
import com.yahoo.messagebus.MessageBus;
@@ -15,8 +12,8 @@ import com.yahoo.messagebus.SourceSessionParams;
import com.yahoo.messagebus.network.Network;
import com.yahoo.messagebus.network.rpc.RPCNetwork;
import com.yahoo.messagebus.network.rpc.RPCNetworkParams;
-import com.yahoo.cloud.config.SlobroksConfig;
-
+import java.util.Objects;
+import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -57,6 +54,7 @@ public class SharedMessageBus extends AbstractResource {
return new SharedMessageBus(new MessageBus(newNetwork(netParams), mbusParams));
}
+ @SuppressWarnings("deprecation")
private static Network newNetwork(RPCNetworkParams params) {
SlobroksConfig cfg = params.getSlobroksConfig();
if (cfg == null) {
diff --git a/container-messagebus/src/main/resources/configdefinitions/container.jdisc.container-mbus.def b/container-messagebus/src/main/resources/configdefinitions/container.jdisc.container-mbus.def
index 143e02a30ef..bd43c13aba3 100644
--- a/container-messagebus/src/main/resources/configdefinitions/container.jdisc.container-mbus.def
+++ b/container-messagebus/src/main/resources/configdefinitions/container.jdisc.container-mbus.def
@@ -22,10 +22,6 @@ transport_events_before_wakeup int default=1
# Dynamic throttling is used, and works better than anything else.
maxpendingcount int default=2048
-enabled bool default=false
-#maxpendingsize is set in megabytes!
-maxpendingsize int default=100
-
#The amount of input data that the service can process concurrently
maxConcurrentFactor double default=0.2 range=[0.0-1.0]
diff --git a/container-search/src/main/java/com/yahoo/search/handler/Json2SingleLevelMap.java b/container-search/src/main/java/com/yahoo/search/handler/Json2SingleLevelMap.java
index 5b8c99506c5..bf0272f4f66 100644
--- a/container-search/src/main/java/com/yahoo/search/handler/Json2SingleLevelMap.java
+++ b/container-search/src/main/java/com/yahoo/search/handler/Json2SingleLevelMap.java
@@ -22,7 +22,7 @@ import java.util.Map;
* @author baldersheim
*/
class Json2SingleLevelMap {
- private static final ObjectMapper jsonMapper = new ObjectMapper();
+ private static final ObjectMapper jsonMapper = new ObjectMapper().configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
private final byte [] buf;
private final JsonParser parser;
Json2SingleLevelMap(InputStream data) {
diff --git a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
index 76702a1d4e0..3da3f57cb21 100644
--- a/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
+++ b/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java
@@ -41,7 +41,6 @@ import com.yahoo.search.searchchain.ExecutionFactory;
import com.yahoo.search.searchchain.SearchChainRegistry;
import com.yahoo.search.statistics.ElapsedTime;
import com.yahoo.slime.Inspector;
-import com.yahoo.slime.ObjectTraverser;
import com.yahoo.yolean.Exceptions;
import com.yahoo.yolean.trace.TraceNode;
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
index 31278af9579..5b30e3c383d 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
@@ -21,6 +21,7 @@ import java.util.Set;
*/
public class QueryProfileConfigurer {
+ @SuppressWarnings("deprecation")
public static QueryProfileRegistry createFromConfigId(String configId) {
return createFromConfig(ConfigGetter.getConfig(QueryProfilesConfig.class, configId));
}
diff --git a/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java b/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java
index 88cc066b665..482c9e3c6ba 100644
--- a/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java
+++ b/container-search/src/test/java/com/yahoo/prelude/IndexFactsFactory.java
@@ -23,6 +23,7 @@ public abstract class IndexFactsFactory {
}
+ @SuppressWarnings("deprecation")
private static <T extends ConfigInstance> T resolveConfig(Class<T> configClass, String configId) {
if (configId == null) return null;
return ConfigGetter.getConfig(configClass, configId);
diff --git a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParsingTester.java b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParsingTester.java
index 229416f7a85..42bc1c22529 100644
--- a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParsingTester.java
+++ b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParsingTester.java
@@ -62,6 +62,7 @@ public class ParsingTester {
* Returns an unfrozen version of the IndexFacts this will use.
* This can be used to add new indexes and passing the resulting IndexFacts to the constructor of this.
*/
+ @SuppressWarnings("deprecation")
public static IndexFacts createIndexFacts() {
String indexInfoConfigID = "file:src/test/java/com/yahoo/prelude/query/parser/test/parseindexinfo.cfg";
ConfigGetter<IndexInfoConfig> getter = new ConfigGetter<>(IndexInfoConfig.class);
diff --git a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java
index 55ce7ed4e99..0d7b0caa65b 100644
--- a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java
@@ -96,6 +96,7 @@ public class StemmingSearcherTestCase {
"WEAKAND(100) notnoun:tower notnoun:tower notnoun:tow");
}
+ @SuppressWarnings("deprecation")
@Test
public void testEmptyIndexInfo() {
String indexInfoConfigID = "file:src/test/java/com/yahoo/prelude/querytransform/test/emptyindexinfo.cfg";
diff --git a/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java
index ef3526f2fb1..0b926f5e9b7 100644
--- a/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java
@@ -4,21 +4,18 @@ package com.yahoo.prelude.searcher.test;
import com.yahoo.component.ComponentId;
import com.yahoo.component.chain.Chain;
import com.yahoo.config.subscription.ConfigGetter;
-import com.yahoo.language.simple.SimpleLinguistics;
+import com.yahoo.prelude.fastsearch.FastHit;
+import com.yahoo.prelude.hitfield.HitField;
import com.yahoo.prelude.searcher.QrQuotetableConfig;
-import com.yahoo.search.rendering.RendererRegistry;
-import com.yahoo.search.result.Hit;
-import com.yahoo.search.result.Relevance;
+import com.yahoo.prelude.searcher.QuotingSearcher;
import com.yahoo.search.Query;
import com.yahoo.search.Result;
-import com.yahoo.prelude.fastsearch.FastHit;
-import com.yahoo.prelude.hitfield.HitField;
import com.yahoo.search.Searcher;
-import com.yahoo.prelude.searcher.QuotingSearcher;
+import com.yahoo.search.result.Hit;
+import com.yahoo.search.result.Relevance;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.search.searchchain.testutil.DocumentSourceSearcher;
import org.junit.Test;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -34,6 +31,7 @@ import static org.junit.Assert.assertTrue;
*/
public class QuotingSearcherTestCase {
+ @SuppressWarnings("deprecation")
public static QuotingSearcher createQuotingSearcher(String configId) {
QrQuotetableConfig config = new ConfigGetter<>(QrQuotetableConfig.class).getConfig(configId);
return new QuotingSearcher(new ComponentId("QuotingSearcher"), config);
diff --git a/container-search/src/test/java/com/yahoo/prelude/searcher/test/ValidateSortingSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/searcher/test/ValidateSortingSearcherTestCase.java
index 84b8ef32871..6b8c268f4ca 100644
--- a/container-search/src/test/java/com/yahoo/prelude/searcher/test/ValidateSortingSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/searcher/test/ValidateSortingSearcherTestCase.java
@@ -2,24 +2,23 @@
package com.yahoo.prelude.searcher.test;
import com.yahoo.component.chain.Chain;
-import com.yahoo.language.simple.SimpleLinguistics;
-import com.yahoo.search.Searcher;
-import com.yahoo.search.rendering.RendererRegistry;
-import com.yahoo.search.searchchain.Execution;
-import com.yahoo.vespa.config.search.AttributesConfig;
-import com.yahoo.search.config.ClusterConfig;
import com.yahoo.config.subscription.ConfigGetter;
import com.yahoo.container.QrSearchersConfig;
import com.yahoo.prelude.searcher.ValidateSortingSearcher;
import com.yahoo.search.Query;
import com.yahoo.search.Result;
+import com.yahoo.search.Searcher;
+import com.yahoo.search.config.ClusterConfig;
+import com.yahoo.search.searchchain.Execution;
import com.yahoo.search.test.QueryTestCase;
+import com.yahoo.vespa.config.search.AttributesConfig;
import org.junit.Test;
-
import java.util.ArrayList;
import java.util.List;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
/**
* Check sorting validation behaves OK.
@@ -30,6 +29,7 @@ public class ValidateSortingSearcherTestCase {
private final ValidateSortingSearcher searcher;
+ @SuppressWarnings("deprecation")
public ValidateSortingSearcherTestCase() {
QrSearchersConfig.Builder qrsCfg = new QrSearchersConfig.Builder();
qrsCfg.searchcluster(new QrSearchersConfig.Searchcluster.Builder().name("giraffes"));
diff --git a/container-search/src/test/java/com/yahoo/prelude/semantics/test/ConfigurationTestCase.java b/container-search/src/test/java/com/yahoo/prelude/semantics/test/ConfigurationTestCase.java
index 0515417f515..7d9ea07339f 100644
--- a/container-search/src/test/java/com/yahoo/prelude/semantics/test/ConfigurationTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/semantics/test/ConfigurationTestCase.java
@@ -4,18 +4,16 @@ package com.yahoo.prelude.semantics.test;
import com.yahoo.component.chain.Chain;
import com.yahoo.config.subscription.ConfigGetter;
import com.yahoo.language.simple.SimpleLinguistics;
-import com.yahoo.prelude.semantics.SemanticRulesConfig;
-import com.yahoo.search.Query;
import com.yahoo.prelude.semantics.RuleBase;
import com.yahoo.prelude.semantics.RuleBaseException;
+import com.yahoo.prelude.semantics.SemanticRulesConfig;
import com.yahoo.prelude.semantics.SemanticSearcher;
+import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.Searcher;
-import com.yahoo.search.rendering.RendererRegistry;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.search.test.QueryTestCase;
import org.junit.Test;
-
import java.util.ArrayList;
import java.util.List;
@@ -28,6 +26,7 @@ import static org.junit.Assert.fail;
*
* @author bratseth
*/
+@SuppressWarnings("deprecation")
public class ConfigurationTestCase {
private static final String root="src/test/java/com/yahoo/prelude/semantics/test/rulebases/";
diff --git a/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java b/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java
index 0f7c9526533..6a54492f5e5 100644
--- a/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java
@@ -1,39 +1,29 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.prelude.test;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
import com.google.common.collect.ImmutableList;
import com.yahoo.config.subscription.ConfigGetter;
-import com.yahoo.container.QrSearchersConfig;
-import com.yahoo.search.config.IndexInfoConfig;
-import com.yahoo.search.config.IndexInfoConfig.Indexinfo;
-import com.yahoo.search.config.IndexInfoConfig.Indexinfo.Alias;
-import com.yahoo.search.config.IndexInfoConfig.Indexinfo.Command;
import com.yahoo.language.process.StemMode;
import com.yahoo.prelude.Index;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.IndexModel;
import com.yahoo.prelude.SearchDefinition;
import com.yahoo.search.Query;
+import com.yahoo.search.config.IndexInfoConfig;
import com.yahoo.search.searchchain.Execution;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
/**
* Tests using synthetic index names for IndexFacts class.
@@ -45,6 +35,7 @@ public class IndexFactsTestCase {
private static final String INDEXFACTS_TESTING = "file:src/test/java/com/yahoo/prelude/test/indexfactstesting.cfg";
+ @SuppressWarnings("deprecation")
private IndexFacts createIndexFacts() {
ConfigGetter<IndexInfoConfig> getter = new ConfigGetter<>(IndexInfoConfig.class);
IndexInfoConfig config = getter.getConfig(INDEXFACTS_TESTING);
diff --git a/container-search/src/test/java/com/yahoo/search/handler/Json2SinglelevelMapTestCase.java b/container-search/src/test/java/com/yahoo/search/handler/Json2SinglelevelMapTestCase.java
index d8db88323c7..c50b063ff44 100644
--- a/container-search/src/test/java/com/yahoo/search/handler/Json2SinglelevelMapTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/handler/Json2SinglelevelMapTestCase.java
@@ -29,4 +29,10 @@ public class Json2SinglelevelMapTestCase {
assertEquals("null", m.get("n"));
assertEquals("[0.786, 0.193]", m.get("a"));
}
+ @Test
+ public void testThatWeAllowSingleQuotes() {
+ Map<String, String> m = new Json2SingleLevelMap(new ByteArrayInputStream("{'yql':'text'}".getBytes(StandardCharsets.UTF_8))).parse();
+ assertTrue(m.containsKey("yql"));
+ assertEquals("text", m.get("yql"));
+ }
}
diff --git a/container-search/src/test/java/com/yahoo/search/query/rewrite/test/QueryRewriteSearcherTestUtils.java b/container-search/src/test/java/com/yahoo/search/query/rewrite/test/QueryRewriteSearcherTestUtils.java
index da005871539..c12a3fb0e14 100644
--- a/container-search/src/test/java/com/yahoo/search/query/rewrite/test/QueryRewriteSearcherTestUtils.java
+++ b/container-search/src/test/java/com/yahoo/search/query/rewrite/test/QueryRewriteSearcherTestUtils.java
@@ -40,6 +40,7 @@ public class QueryRewriteSearcherTestUtils {
*
* @param configPath path for the searcher config
*/
+ @SuppressWarnings("deprecation")
public static RewritesConfig createConfigObj(String configPath) {
return new ConfigGetter<>(RewritesConfig.class).getConfig(configPath);
}
diff --git a/container-search/src/test/java/com/yahoo/search/searchers/ValidateFuzzySearcherTestCase.java b/container-search/src/test/java/com/yahoo/search/searchers/ValidateFuzzySearcherTestCase.java
index 587b40dfd03..577963ef2b3 100644
--- a/container-search/src/test/java/com/yahoo/search/searchers/ValidateFuzzySearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchers/ValidateFuzzySearcherTestCase.java
@@ -1,7 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.searchers;
-import com.yahoo.config.subscription.ConfigGetter;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.IndexModel;
import com.yahoo.prelude.SearchDefinition;
@@ -13,10 +12,9 @@ import com.yahoo.search.query.parser.ParserEnvironment;
import com.yahoo.search.result.ErrorMessage;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.search.yql.YqlParser;
-import com.yahoo.vespa.config.search.AttributesConfig.Attribute;
import com.yahoo.vespa.config.search.AttributesConfig;
+import com.yahoo.vespa.config.search.AttributesConfig.Attribute;
import org.junit.Test;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -33,35 +31,25 @@ public class ValidateFuzzySearcherTestCase {
List<String> attributes;
public ValidateFuzzySearcherTestCase() {
- int i = 0;
attributes = new ArrayList<>();
- StringBuilder attributeConfig = new StringBuilder();
+ AttributesConfig.Builder configBuilder = new AttributesConfig.Builder();
+ List<AttributesConfig.Attribute.Builder> attributesList = new ArrayList<>();
for (Attribute.Datatype.Enum attr: Attribute.Datatype.Enum.values()) {
for (Attribute.Collectiontype.Enum ctype: Attribute.Collectiontype.Enum.values()) {
+ AttributesConfig.Attribute.Builder attributesBuilder = new AttributesConfig.Attribute.Builder();
String attributeName = attr.name().toLowerCase() + "_" + ctype.name().toLowerCase();
+ attributesBuilder.name(attributeName);
+ attributesBuilder.datatype(attr);
+ attributesBuilder.collectiontype(ctype);
+ attributesList.add(attributesBuilder);
- attributeConfig.append("attribute[" + i + "].name ");
- attributeConfig.append(attributeName);
- attributeConfig.append("\n");
-
- attributeConfig.append("attribute[" + i + "].datatype ");
- attributeConfig.append(attr.name());
- attributeConfig.append("\n");
-
- attributeConfig.append("attribute[" + i + "].collectiontype ");
- attributeConfig.append(ctype.name());
- attributeConfig.append("\n");
-
- i += 1;
attributes.add(attributeName);
}
}
+ configBuilder.attribute(attributesList);
+ AttributesConfig config = configBuilder.build();
- searcher = new ValidateFuzzySearcher(ConfigGetter.getConfig(
- AttributesConfig.class,
- "raw: " +
- "attribute[" + attributes.size() + "]\n" +
- attributeConfig));
+ searcher = new ValidateFuzzySearcher(config);
}
private String makeQuery(String attribute, String query, int maxEditDistance, int prefixLength) {
diff --git a/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java b/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java
index 9a760bfb0cb..c2f4ee31aa8 100644
--- a/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java
@@ -2,28 +2,22 @@
package com.yahoo.search.searchers;
import com.yahoo.config.subscription.ConfigGetter;
-import com.yahoo.config.subscription.FileSource;
-import com.yahoo.config.subscription.RawSource;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.IndexModel;
import com.yahoo.prelude.SearchDefinition;
import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.query.QueryTree;
import com.yahoo.search.query.parser.Parsable;
import com.yahoo.search.query.parser.ParserEnvironment;
-import com.yahoo.search.query.QueryTree;
-import com.yahoo.search.Result;
import com.yahoo.search.result.ErrorMessage;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.search.yql.YqlParser;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
import com.yahoo.vespa.config.search.AttributesConfig;
-
-import com.yahoo.vespa.config.search.RankProfilesConfig;
import org.junit.Test;
-import java.io.File;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -34,6 +28,7 @@ public class ValidateNearestNeighborTestCase {
ValidateNearestNeighborSearcher searcher;
+ @SuppressWarnings("deprecation")
public ValidateNearestNeighborTestCase() {
searcher = new ValidateNearestNeighborSearcher(
ConfigGetter.getConfig(AttributesConfig.class,
diff --git a/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java b/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java
index 01e360858c1..1b3689a94b0 100644
--- a/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java
@@ -3,7 +3,6 @@ package com.yahoo.search.searchers.test;
import com.yahoo.component.chain.Chain;
import com.yahoo.config.subscription.ConfigGetter;
-import com.yahoo.config.subscription.RawSource;
import com.yahoo.search.Searcher;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.search.searchers.ValidateMatchPhaseSearcher;
@@ -15,7 +14,7 @@ import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
/**
* @author baldersheim
@@ -24,6 +23,7 @@ public class ValidateMatchPhaseSearcherTestCase {
private final ValidateMatchPhaseSearcher searcher;
+ @SuppressWarnings("deprecation")
public ValidateMatchPhaseSearcherTestCase() {
searcher = new ValidateMatchPhaseSearcher(
ConfigGetter.getConfig(AttributesConfig.class,
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 a381bcd5bed..3355f765f42 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
@@ -41,6 +41,7 @@ public class Node {
private final Version wantedVersion;
private final Version currentOsVersion;
private final Version wantedOsVersion;
+ private final boolean deferOsUpgrade;
private final DockerImage currentDockerImage;
private final DockerImage wantedDockerImage;
private final ServiceState serviceState;
@@ -76,7 +77,7 @@ public class Node {
private Node(String id, HostName hostname, Optional<HostName> parentHostname, State state, NodeType type,
NodeResources resources, Optional<ApplicationId> owner, Version currentVersion, Version wantedVersion,
- Version currentOsVersion, Version wantedOsVersion, Optional<Instant> currentFirmwareCheck,
+ Version currentOsVersion, Version wantedOsVersion, boolean deferOsUpgrade, Optional<Instant> currentFirmwareCheck,
Optional<Instant> wantedFirmwareCheck, ServiceState serviceState, Optional<Instant> suspendedSince,
long restartGeneration, long wantedRestartGeneration, long rebootGeneration,
long wantedRebootGeneration, int cost, int failCount, Optional<String> flavor, String clusterId,
@@ -97,6 +98,7 @@ public class Node {
this.wantedVersion = Objects.requireNonNull(wantedVersion, "wantedVersion must be non-null");
this.currentOsVersion = Objects.requireNonNull(currentOsVersion, "currentOsVersion must be non-null");
this.wantedOsVersion = Objects.requireNonNull(wantedOsVersion, "wantedOsVersion must be non-null");
+ this.deferOsUpgrade = deferOsUpgrade;
this.currentFirmwareCheck = Objects.requireNonNull(currentFirmwareCheck, "currentFirmwareCheck must be non-null");
this.wantedFirmwareCheck = Objects.requireNonNull(wantedFirmwareCheck, "wantedFirmwareCheck must be non-null");
this.serviceState = Objects.requireNonNull(serviceState, "serviceState must be non-null");
@@ -184,6 +186,11 @@ public class Node {
return wantedOsVersion;
}
+ /** Returns whether the node is currently deferring any OS upgrade */
+ public boolean deferOsUpgrade() {
+ return deferOsUpgrade;
+ }
+
/** The container image of this is currently running */
public DockerImage currentDockerImage() {
return currentDockerImage;
@@ -455,6 +462,7 @@ public class Node {
private Version wantedVersion = Version.emptyVersion;
private Version currentOsVersion = Version.emptyVersion;
private Version wantedOsVersion = Version.emptyVersion;
+ private boolean deferOsUpgrade = false;
private DockerImage currentDockerImage = DockerImage.EMPTY;
private DockerImage wantedDockerImage = DockerImage.EMPTY;
private Optional<Instant> currentFirmwareCheck = Optional.empty();
@@ -502,6 +510,7 @@ public class Node {
this.wantedVersion = node.wantedVersion;
this.currentOsVersion = node.currentOsVersion;
this.wantedOsVersion = node.wantedOsVersion;
+ this.deferOsUpgrade = node.deferOsUpgrade;
this.currentDockerImage = node.currentDockerImage;
this.wantedDockerImage = node.wantedDockerImage;
this.serviceState = node.serviceState;
@@ -599,6 +608,11 @@ public class Node {
return this;
}
+ public Builder deferOsUpgrade(boolean deferOsUpgrade) {
+ this.deferOsUpgrade = deferOsUpgrade;
+ return this;
+ }
+
public Builder currentDockerImage(DockerImage currentDockerImage) {
this.currentDockerImage = currentDockerImage;
return this;
@@ -761,7 +775,7 @@ public class Node {
public Node build() {
return new Node(id, hostname, parentHostname, state, type, resources, owner, currentVersion, wantedVersion,
- currentOsVersion, wantedOsVersion, currentFirmwareCheck, wantedFirmwareCheck, serviceState,
+ currentOsVersion, wantedOsVersion, deferOsUpgrade, currentFirmwareCheck, wantedFirmwareCheck, serviceState,
suspendedSince, restartGeneration, wantedRestartGeneration, rebootGeneration,
wantedRebootGeneration, cost, failCount, flavor, clusterId, clusterType, group, index, retired,
wantToRetire, wantToDeprovision, wantToRebuild, down, reservedTo, exclusiveTo, wantedDockerImage,
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
index 09ddcf4fa5e..b6905a97b5f 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
@@ -161,7 +161,7 @@ public final class JobType implements Comparable<JobType> {
/** A serialized form of this: {@code &lt;environment&gt;.&lt;region&gt;[.test]}; the inverse of {@link #ofSerialized(String)} */
public String serialized() {
- return zone.environment().value() + "." + zone.region().value() + (isProductionTest ? ".test" : "");
+ return zone().value() + (isProductionTest ? ".test" : "");
}
public String jobName() {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/TestReport.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/TestReport.java
index 789a909b780..60cb4248035 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/TestReport.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/TestReport.java
@@ -1,10 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.deployment;
-import com.yahoo.slime.SlimeUtils;
-
-import java.nio.charset.StandardCharsets;
-
/**
* @author mortent
*/
@@ -21,7 +17,6 @@ public class TestReport {
}
public static TestReport fromJson(String report) {
- SlimeUtils.jsonToSlimeOrThrow(report.getBytes(StandardCharsets.UTF_8)); // Verify structure.
return new TestReport(report);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java
index 8964e1b5127..d8a804ecd2c 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java
@@ -63,6 +63,8 @@ public class NodeRepositoryNode {
private String currentOsVersion;
@JsonProperty("wantedOsVersion")
private String wantedOsVersion;
+ @JsonProperty("deferOsUpgrade")
+ private Boolean deferOsUpgrade;
@JsonProperty("currentFirmwareCheck")
private Long currentFirmwareCheck;
@JsonProperty("wantedFirmwareCheck")
@@ -250,6 +252,14 @@ public class NodeRepositoryNode {
this.wantedVespaVersion = wantedVespaVersion;
}
+ public Boolean getDeferOsUpgrade() {
+ return deferOsUpgrade;
+ }
+
+ public void setDeferOsUpgrade(Boolean deferOsUpgrade) {
+ this.deferOsUpgrade = deferOsUpgrade;
+ }
+
public Integer getFailCount() {
return failCount;
}
@@ -441,12 +451,13 @@ public class NodeRepositoryNode {
// --- end
+
@Override
public String toString() {
return "NodeRepositoryNode{" +
"url='" + url + '\'' +
", id='" + id + '\'' +
- ", state=" + state +
+ ", state='" + state + '\'' +
", hostname='" + hostname + '\'' +
", ipAddresses=" + ipAddresses +
", additionalIpAddresses=" + additionalIpAddresses +
@@ -464,11 +475,12 @@ public class NodeRepositoryNode {
", wantedVespaVersion='" + wantedVespaVersion + '\'' +
", currentOsVersion='" + currentOsVersion + '\'' +
", wantedOsVersion='" + wantedOsVersion + '\'' +
+ ", deferOsUpgrade=" + deferOsUpgrade +
", currentFirmwareCheck=" + currentFirmwareCheck +
", wantedFirmwareCheck=" + wantedFirmwareCheck +
", failCount=" + failCount +
- ", environment=" + environment +
- ", type=" + type +
+ ", environment='" + environment + '\'' +
+ ", type='" + type + '\'' +
", wantedDockerImage='" + wantedDockerImage + '\'' +
", currentDockerImage='" + currentDockerImage + '\'' +
", parentHostname='" + parentHostname + '\'' +
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 045e43f532c..b9432fdc375 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
@@ -237,7 +237,7 @@ public class ApplicationController {
}
/** Sets the default target major version. Set to empty to determine target version normally (by confidence) */
- public void setTargetMajorVersion(Optional<Integer> targetMajorVersion) {
+ public void setTargetMajorVersion(OptionalInt targetMajorVersion) {
curator.writeTargetMajorVersion(targetMajorVersion);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
index 7ceeda08d3a..d83f552ab25 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
@@ -228,10 +228,9 @@ public class DeploymentTrigger {
Instance instance = application.require(applicationId.instance());
JobId job = new JobId(instance.id(), jobType);
JobStatus jobStatus = jobs.jobStatus(new JobId(applicationId, jobType));
- Versions versions = jobStatus.lastTriggered()
- .orElseThrow(() -> new IllegalArgumentException(job + " has never been triggered"))
- .versions();
- trigger(deploymentJob(instance, versions, jobType, jobStatus, clock.instant()), reason);
+ Run last = jobStatus.lastTriggered()
+ .orElseThrow(() -> new IllegalArgumentException(job + " has never been triggered"));
+ trigger(deploymentJob(instance, last.versions(), last.id().type(), jobStatus.isNodeAllocationFailure(), clock.instant()), reason);
return job;
}
@@ -259,7 +258,12 @@ public class DeploymentTrigger {
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
jobs.forEach((jobId, versionsList) -> {
- trigger(deploymentJob(application.require(job.application().instance()), versionsList.get(0).versions(), jobId.type(), status.jobs().get(jobId).get(), clock.instant()), reason);
+ trigger(deploymentJob(application.require(job.application().instance()),
+ versionsList.get(0).versions(),
+ jobId.type(),
+ status.jobs().get(jobId).get().isNodeAllocationFailure(),
+ clock.instant()),
+ reason);
});
return List.copyOf(jobs.keySet());
}
@@ -388,7 +392,7 @@ public class DeploymentTrigger {
jobs.add(deploymentJob(status.application().require(jobId.application().instance()),
job.versions(),
job.type(),
- status.instanceJobs(jobId.application().instance()).get(jobId.type()),
+ status.instanceJobs(jobId.application().instance()).get(jobId.type()).isNodeAllocationFailure(),
job.readyAt().get()));
});
return Collections.unmodifiableList(jobs);
@@ -475,8 +479,8 @@ public class DeploymentTrigger {
// ---------- Version and job helpers ----------
- private Job deploymentJob(Instance instance, Versions versions, JobType jobType, JobStatus jobStatus, Instant availableSince) {
- return new Job(instance, versions, jobType, availableSince, jobStatus.isNodeAllocationFailure(), instance.change().revision().isPresent());
+ private Job deploymentJob(Instance instance, Versions versions, JobType jobType, boolean isNodeAllocationFailure, Instant availableSince) {
+ return new Job(instance, versions, jobType, availableSince, isNodeAllocationFailure, instance.change().revision().isPresent());
}
// ---------- Data containers ----------
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
index 19b2afb3af9..881107fa0f9 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
@@ -158,7 +158,7 @@ public class JobController {
/** Stores the given log entries for the given run and step. */
public void log(RunId id, Step step, List<LogEntry> entries) {
locked(id, __ -> {
- logs.append(id.application(), id.type(), step, entries);
+ logs.append(id.application(), id.type(), step, entries, true);
return __;
});
}
@@ -211,7 +211,7 @@ public class JobController {
if (log.isEmpty())
return run;
- logs.append(id.application(), id.type(), Step.copyVespaLogs, log);
+ logs.append(id.application(), id.type(), Step.copyVespaLogs, log, false);
return run.with(log.get(log.size() - 1).at());
});
}
@@ -230,7 +230,7 @@ public class JobController {
if (entries.isEmpty())
return run;
- logs.append(id.application(), id.type(), step.get(), entries);
+ logs.append(id.application(), id.type(), step.get(), entries, false);
return run.with(entries.stream().mapToLong(LogEntry::id).max().getAsLong());
});
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java
index 09e0fec41d1..c8c5a1834c7 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java
@@ -14,14 +14,17 @@ public class ApplicationMetaDataGarbageCollector extends ControllerMaintainer {
private static final Logger log = Logger.getLogger(ApplicationMetaDataGarbageCollector.class.getName());
+ private final Duration timeToLive;
+
public ApplicationMetaDataGarbageCollector(Controller controller, Duration interval) {
super(controller, interval);
+ this.timeToLive = controller.system().isCd() ? Duration.ofDays(7) : Duration.ofDays(365);
}
@Override
protected double maintain() {
try {
- controller().applications().applicationStore().pruneMeta(controller().clock().instant().minus(Duration.ofDays(365)));
+ controller().applications().applicationStore().pruneMeta(controller().clock().instant().minus(timeToLive));
return 1.0;
}
catch (Exception e) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
index 4aeecdcd4ff..9793cded918 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
@@ -70,7 +70,7 @@ public class ControllerMaintenance extends AbstractComponent {
maintainers.add(new ArchiveAccessMaintainer(controller, metric, intervals.archiveAccessMaintainer));
maintainers.add(new TenantRoleMaintainer(controller, intervals.tenantRoleMaintainer));
maintainers.add(new ChangeRequestMaintainer(controller, intervals.changeRequestMaintainer));
- maintainers.add(new VcmrMaintainer(controller, intervals.vcmrMaintainer));
+ maintainers.add(new VcmrMaintainer(controller, intervals.vcmrMaintainer, metric));
maintainers.add(new CloudTrialExpirer(controller, intervals.defaultInterval));
maintainers.add(new RetriggerMaintainer(controller, intervals.retriggerMaintainer));
maintainers.add(new UserManagementMaintainer(controller, intervals.userManagementMaintainer, controller.serviceRegistry().roleMaintainer()));
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java
index 3bd1c7bb358..111931b638b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java
@@ -9,8 +9,10 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepo
import com.yahoo.vespa.hosted.controller.api.integration.deployment.OsRelease;
import com.yahoo.vespa.hosted.controller.versions.OsVersionTarget;
+import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
@@ -84,14 +86,11 @@ public class OsUpgradeScheduler extends ControllerMaintainer {
}
/** OS release based on a tag */
- private static class TaggedRelease implements Release {
+ private record TaggedRelease(SystemName system, ArtifactRepository artifactRepository) implements Release {
- private final SystemName system;
- private final ArtifactRepository artifactRepository;
-
- private TaggedRelease(SystemName system, ArtifactRepository artifactRepository) {
- this.system = Objects.requireNonNull(system);
- this.artifactRepository = Objects.requireNonNull(artifactRepository);
+ public TaggedRelease {
+ Objects.requireNonNull(system);
+ Objects.requireNonNull(artifactRepository);
}
@Override
@@ -119,41 +118,30 @@ public class OsUpgradeScheduler extends ControllerMaintainer {
}
/** OS release based on calendar-versioning */
- private static class CalendarVersionedRelease implements Release {
+ record CalendarVersionedRelease(SystemName system) implements Release {
- /** The time to wait before scheduling upgrade to next version */
- private static final Duration SCHEDULING_INTERVAL = Duration.ofDays(45);
+ /** A fixed point in time which the release schedule is calculated from */
+ private static final Instant START_OF_SCHEDULE = LocalDate.of(2022, 1, 1)
+ .atStartOfDay()
+ .toInstant(ZoneOffset.UTC);
- /**
- * The interval at which new versions become available. We use this to avoid scheduling upgrades to a version
- * that has not been released yet. Example: Version N is the latest one and target is set to N+1. If N+1 does
- * not exist the zone will not converge until N+1 has been released and we may end up triggering multiple
- * rounds of upgrades.
- */
- private static final Duration AVAILABILITY_INTERVAL = Duration.ofDays(7);
+ /** The time that should elapse between versions */
+ private static final Duration SCHEDULING_STEP = Duration.ofDays(60);
- private static final DateTimeFormatter CALENDAR_VERSION_PATTERN = DateTimeFormatter.ofPattern("yyyyMMdd");
+ /** The day of week new releases are published */
+ private static final DayOfWeek RELEASE_DAY = DayOfWeek.MONDAY;
- private final SystemName system;
+ private static final DateTimeFormatter CALENDAR_VERSION_PATTERN = DateTimeFormatter.ofPattern("yyyyMMdd");
- public CalendarVersionedRelease(SystemName system) {
- this.system = Objects.requireNonNull(system);
+ public CalendarVersionedRelease {
+ Objects.requireNonNull(system);
}
@Override
public Version version(OsVersionTarget currentTarget, Instant now) {
- Instant scheduledAt = currentTarget.scheduledAt();
Version currentVersion = currentTarget.osVersion().version();
- if (scheduledAt.isBefore(now.minus(SCHEDULING_INTERVAL))) {
- String calendarVersion = now.minus(AVAILABILITY_INTERVAL)
- .atZone(ZoneOffset.UTC)
- .format(CALENDAR_VERSION_PATTERN);
- return new Version(currentVersion.getMajor(),
- currentVersion.getMinor(),
- currentVersion.getMicro(),
- calendarVersion);
- }
- return currentVersion; // New version should not be scheduled yet
+ Version wantedVersion = asVersion(dateOfWantedVersion(now), currentVersion);
+ return wantedVersion.isAfter(currentVersion) ? wantedVersion : currentVersion;
}
@Override
@@ -161,6 +149,32 @@ public class OsUpgradeScheduler extends ControllerMaintainer {
return system.isCd() ? Duration.ZERO : Duration.ofDays(14);
}
+ /**
+ * Calculate the date of the wanted version relative to now. A given zone will choose the oldest release
+ * available which is not older than this date.
+ */
+ static LocalDate dateOfWantedVersion(Instant now) {
+ Instant candidate = START_OF_SCHEDULE;
+ while (!candidate.plus(SCHEDULING_STEP).isAfter(now)) {
+ candidate = candidate.plus(SCHEDULING_STEP);
+ }
+ LocalDate date = LocalDate.ofInstant(candidate, ZoneOffset.UTC);
+ return releaseDayOf(date);
+ }
+
+ private static LocalDate releaseDayOf(LocalDate date) {
+ int releaseDayDelta = RELEASE_DAY.getValue() - date.getDayOfWeek().getValue();
+ return date.plusDays(releaseDayDelta);
+ }
+
+ private static Version asVersion(LocalDate dateOfVersion, Version currentVersion) {
+ String calendarVersion = dateOfVersion.format(CALENDAR_VERSION_PATTERN);
+ return new Version(currentVersion.getMajor(),
+ currentVersion.getMinor(),
+ currentVersion.getMicro(),
+ calendarVersion);
+ }
+
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java
index fa64a2677f4..8155476f139 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java
@@ -62,7 +62,7 @@ public class OsUpgrader extends InfrastructureUpgrader<OsVersionTarget> {
protected boolean expectUpgradeOf(Node node, SystemApplication application, ZoneApi zone) {
return cloud.equals(zone.getCloudName()) && // Cloud is managed by this upgrader
application.shouldUpgradeOs() && // Application should upgrade in this cloud
- canUpgrade(node); // Node is in an upgradable state
+ canUpgrade(node);
}
@Override
@@ -98,14 +98,12 @@ public class OsUpgrader extends InfrastructureUpgrader<OsVersionTarget> {
/** Returns whether to spend upgrade budget on given zone */
private boolean spendBudgetOn(ZoneApi zone) {
- if (!zone.getEnvironment().isProduction()) return false;
- if (controller().zoneRegistry().systemZone().getVirtualId().equals(zone.getVirtualId())) return false; // Controller zone
- return true;
+ return !controller().zoneRegistry().systemZone().getVirtualId().equals(zone.getVirtualId()); // Do not spend budget on controller zone
}
- /** Returns whether node is in a state where it can be upgraded */
+ /** Returns whether node currently allows upgrades */
public static boolean canUpgrade(Node node) {
- return upgradableNodeStates.contains(node.state());
+ return !node.deferOsUpgrade() && upgradableNodeStates.contains(node.state());
}
private static String name(CloudName cloud) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java
index a2fb0df626f..1932dc65657 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java
@@ -23,7 +23,6 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.OptionalInt;
import java.util.Random;
import java.util.Set;
@@ -182,7 +181,7 @@ public class Upgrader extends ControllerMaintainer {
}
/** Sets the default target major version. Set to empty to determine target version normally (by confidence) */
- public void setTargetMajorVersion(Optional<Integer> targetMajorVersion) {
+ public void setTargetMajorVersion(OptionalInt targetMajorVersion) {
controller().applications().setTargetMajorVersion(targetMajorVersion);
}
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 551f803f368..daba7e74f34 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
@@ -5,6 +5,7 @@ import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.jdisc.Metric;
import com.yahoo.text.Text;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
@@ -46,26 +47,28 @@ public class VcmrMaintainer extends ControllerMaintainer {
private static final Logger LOG = Logger.getLogger(VcmrMaintainer.class.getName());
private static final int DAYS_TO_RETIRE = 2;
private static final Duration ALLOWED_POSTPONEMENT_TIME = Duration.ofDays(7);
+ protected static final String TRACKED_CMRS_METRIC = "cmr.tracked";
private final CuratorDb curator;
private final NodeRepository nodeRepository;
private final ChangeRequestClient changeRequestClient;
private final SystemName system;
+ private final Metric metric;
- public VcmrMaintainer(Controller controller, Duration interval) {
+ public VcmrMaintainer(Controller controller, Duration interval, Metric metric) {
super(controller, interval, null, SystemName.allOf(Predicate.not(SystemName::isPublic)));
this.curator = controller.curator();
this.nodeRepository = controller.serviceRegistry().configServer().nodeRepository();
this.changeRequestClient = controller.serviceRegistry().changeRequestClient();
this.system = controller.system();
+ this.metric = metric;
}
@Override
protected double maintain() {
var changeRequests = curator.readChangeRequests()
.stream()
- .filter(shouldUpdate())
- .collect(Collectors.toList());
+ .filter(shouldUpdate()).toList();
var nodesByZone = nodesByZone();
@@ -86,6 +89,7 @@ public class VcmrMaintainer extends ControllerMaintainer {
});
}
});
+ updateMetrics();
return 1.0;
}
@@ -357,4 +361,15 @@ public class VcmrMaintainer extends ControllerMaintainer {
return time;
}
+ private void updateMetrics() {
+ var cmrsByStatus = curator.readChangeRequests()
+ .stream()
+ .collect(Collectors.groupingBy(VespaChangeRequest::getStatus));
+
+ for (var status : Status.values()) {
+ var count = cmrsByStatus.getOrDefault(status, List.of()).size();
+ metric.set(TRACKED_CMRS_METRIC, count, metric.createContext(Map.of("status", status.name())));
+ }
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java
index 9721026c628..ecb9db8195f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java
@@ -49,7 +49,7 @@ public class BufferedLogStore {
}
/** Appends to the log of the given, active run, reassigning IDs as counted here, and converting to Vespa log levels. */
- public void append(ApplicationId id, JobType type, Step step, List<LogEntry> entries) {
+ public void append(ApplicationId id, JobType type, Step step, List<LogEntry> entries, boolean forceLog) {
if (entries.isEmpty())
return;
@@ -58,7 +58,7 @@ public class BufferedLogStore {
long lastEntryId = buffer.readLastLogEntryId(id, type).orElse(-1L);
long lastChunkId = buffer.getLogChunkIds(id, type).max().orElse(0);
long numberOfChunks = Math.max(1, buffer.getLogChunkIds(id, type).count());
- if (numberOfChunks > maxLogSize / chunkSize)
+ if (numberOfChunks > maxLogSize / chunkSize && ! forceLog)
return; // Max size exceeded — store no more.
byte[] emptyChunk = "[]".getBytes();
@@ -72,8 +72,12 @@ public class BufferedLogStore {
buffer.writeLastLogEntryId(id, type, lastEntryId);
buffer.writeLog(id, type, lastChunkId, logSerializer.toJson(log));
lastChunkId = lastEntryId + 1;
- if (++numberOfChunks > maxLogSize / chunkSize) {
- log = Map.of(step, List.of(new LogEntry(++lastEntryId, entry.at(), LogEntry.Type.warning, "Max log size of " + (maxLogSize >> 20) + "Mb exceeded; further entries are discarded.")));
+ if (++numberOfChunks > maxLogSize / chunkSize && ! forceLog) {
+ log = Map.of(step, List.of(new LogEntry(++lastEntryId,
+ entry.at(),
+ LogEntry.Type.warning,
+ "Max log size of " + (maxLogSize >> 20) +
+ "Mb exceeded; further user entries are discarded.")));
break;
}
log = new HashMap<>();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
index f02f49e7114..54e98877ba3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
@@ -1,9 +1,9 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.persistence;
-import com.yahoo.component.annotation.Inject;
import com.yahoo.collections.Pair;
import com.yahoo.component.Version;
+import com.yahoo.component.annotation.Inject;
import com.yahoo.concurrent.UncheckedTimeoutException;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
@@ -41,6 +41,7 @@ import com.yahoo.vespa.hosted.controller.versions.OsVersionStatus;
import com.yahoo.vespa.hosted.controller.versions.OsVersionTarget;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
+
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
@@ -53,6 +54,7 @@ import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Optional;
+import java.util.OptionalInt;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
@@ -271,9 +273,9 @@ public class CuratorDb {
return read(targetMajorVersionPath(), ByteBuffer::wrap).map(ByteBuffer::getInt);
}
- public void writeTargetMajorVersion(Optional<Integer> targetMajorVersion) {
+ public void writeTargetMajorVersion(OptionalInt targetMajorVersion) {
if (targetMajorVersion.isPresent())
- curator.set(targetMajorVersionPath(), ByteBuffer.allocate(Integer.BYTES).putInt(targetMajorVersion.get()).array());
+ curator.set(targetMajorVersionPath(), ByteBuffer.allocate(Integer.BYTES).putInt(targetMajorVersion.getAsInt()).array());
else
curator.delete(targetMajorVersionPath());
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index cfb00db7b63..56eaf2f3a2e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -75,6 +75,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.RestartFilter;
import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.RoleDefinition;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
@@ -1473,6 +1474,15 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
private HttpResponse trigger(ApplicationId id, JobType type, HttpRequest request) {
+ // JobType.fromJobName doesn't properly initiate test jobs. Triggering these without context isn't _really_
+ // necessary, but triggering a test in the default cloud is better than failing with a weird error.
+ ZoneRegistry zones = controller.zoneRegistry();
+ type = switch (type.environment()) {
+ case test -> JobType.systemTest(zones, zones.systemZone().getCloudName());
+ case staging -> JobType.stagingTest(zones, zones.systemZone().getCloudName());
+ default -> type;
+ };
+
Inspector requestObject = toSlime(request.getData()).get();
boolean requireTests = ! requestObject.field("skipTests").asBool();
boolean reTrigger = requestObject.field("reTrigger").asBool();
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 9c016eccd27..25953c16bf0 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
@@ -115,6 +115,7 @@ class JobControllerApiHandlerHelper {
Run run = jobController.run(runId);
detailsObject.setBool("active", ! run.hasEnded());
detailsObject.setString("status", nameOf(run.status()));
+ run.reason().ifPresent(reason -> detailsObject.setString("reason", reason));
try {
jobController.updateTestLog(runId);
jobController.updateVespaLog(runId);
@@ -421,6 +422,7 @@ class JobControllerApiHandlerHelper {
runObject.setLong("start", run.start().toEpochMilli());
run.end().ifPresent(end -> runObject.setLong("end", end.toEpochMilli()));
runObject.setString("status", run.status().name());
+ run.reason().ifPresent(reason -> runObject.setString("reason", reason));
toSlime(runObject.setObject("versions"), run.versions(), application);
Cursor runStepsArray = runObject.setArray("steps");
run.steps().forEach((step, info) -> {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
index 25ac90ac0ea..776fcbfd03b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
@@ -35,6 +35,7 @@ import java.security.Principal;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Optional;
+import java.util.OptionalInt;
import java.util.Scanner;
import java.util.function.Function;
import java.util.logging.Level;
@@ -60,13 +61,13 @@ public class ControllerApiHandler extends AuditLoggingRequestHandler {
@Override
public HttpResponse auditAndHandle(HttpRequest request) {
try {
- switch (request.getMethod()) {
- case GET: return get(request);
- case POST: return post(request);
- case DELETE: return delete(request);
- case PATCH: return patch(request);
- default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
- }
+ return switch (request.getMethod()) {
+ case GET -> get(request);
+ case POST -> post(request);
+ case DELETE -> delete(request);
+ case PATCH -> patch(request);
+ default -> ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
+ };
}
catch (IllegalArgumentException e) {
return ErrorResponse.badRequest(Exceptions.toMessageString(e));
@@ -165,8 +166,8 @@ public class ControllerApiHandler extends AuditLoggingRequestHandler {
if (inspect.field(upgradesPerMinuteField).valid()) {
upgrader.setUpgradesPerMinute(inspect.field(upgradesPerMinuteField).asDouble());
} else if (inspect.field(targetMajorVersionField).valid()) {
- int target = (int)inspect.field(targetMajorVersionField).asLong();
- upgrader.setTargetMajorVersion(Optional.ofNullable(target == 0 ? null : target)); // 0 is the default value
+ int target = (int) inspect.field(targetMajorVersionField).asLong();
+ upgrader.setTargetMajorVersion(target == 0 ? OptionalInt.empty() : OptionalInt.of(target)); // 0 is the default value
} else {
return ErrorResponse.badRequest("No such modifiable field(s)");
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java
index 7f33f612cd0..e078df0267f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java
@@ -15,7 +15,8 @@ import static com.yahoo.config.application.api.DeploymentSpec.UpgradePolicy;
/**
* Information about a particular Vespa version.
- * VespaVersions are identified by their version number and ordered by increasing version numbers.
+ *
+ * Vespa versions are identified by their version number and ordered by increasing version numbers.
*
* @author bratseth
*/
@@ -29,8 +30,11 @@ public record VespaVersion(Version version,
Confidence confidence) implements Comparable<VespaVersion> {
public static Confidence confidenceFrom(DeploymentStatistics statistics, Controller controller) {
+ int thisMajorVersion = statistics.version().getMajor();
+ int defaultMajorVersion = controller.applications().targetMajorVersion().orElse(thisMajorVersion);
InstanceList all = InstanceList.from(controller.jobController().deploymentStatuses(ApplicationList.from(controller.applications().asList())
- .withProductionDeployment()));
+ .withProductionDeployment()))
+ .allowingMajorVersion(thisMajorVersion, defaultMajorVersion);
// 'production on this': All production deployment jobs upgrading to this version have completed without failure
InstanceList productionOnThis = all.matching(instance -> statistics.productionSuccesses().stream().anyMatch(run -> run.id().application().equals(instance)))
.not().failingUpgrade()
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index 157f6b27487..a8c18957773 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -1153,7 +1153,7 @@ public class ControllerTest {
assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.of(8)));
// Default major version is set to 8.
- tester.applications().setTargetMajorVersion(Optional.of(8));
+ tester.applications().setTargetMajorVersion(OptionalInt.of(8));
assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7)));
assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty()));
@@ -1169,13 +1169,13 @@ public class ControllerTest {
// Application upgrades to major 8; only major version from deployment spec should cause a downgrade.
context.submit(new ApplicationPackageBuilder().region("us-west-1").compileVersion(version2).build()).deploy();
- tester.applications().setTargetMajorVersion(Optional.empty());
+ tester.applications().setTargetMajorVersion(OptionalInt.empty());
tester.applications().lockApplicationOrThrow(application, locked -> tester.applications().store(locked.withMajorVersion(null)));
assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7)));
assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty()));
// Default major version across all apps should not cause a downgrade.
- tester.applications().setTargetMajorVersion(Optional.of(7));
+ tester.applications().setTargetMajorVersion(OptionalInt.of(7));
assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7)));
assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty()));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
index 673cbf9708b..08fc6df37fb 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
@@ -2153,6 +2153,7 @@ public class DeploymentTriggerTest {
Version version2 = new Version("8");
tester.controllerTester().flagSource().withListFlag(PermanentFlags.INCOMPATIBLE_VERSIONS.id(), List.of("8"), String.class);
+ // App deploys on version1.
tester.controllerTester().upgradeSystem(version1);
DeploymentContext app = tester.newDeploymentContext()
.submit(new ApplicationPackageBuilder().region("us-east-3")
@@ -2160,10 +2161,12 @@ public class DeploymentTriggerTest {
.build())
.deploy();
+ // System upgrades to version2, but the app is not upgraded.
tester.controllerTester().upgradeSystem(version2);
tester.upgrader().run();
assertEquals(Change.empty(), app.instance().change());
+ // App compiles against version2, and upgrades.
app.submit(new ApplicationPackageBuilder().region("us-east-3")
.compileVersion(version2)
.build());
@@ -2171,6 +2174,18 @@ public class DeploymentTriggerTest {
assertEquals(version2, tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetPlatform());
assertEquals(version2, app.application().revisions().get(tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetRevision()).compileVersion().get());
+
+ // App specifies version1 in deployment spec, compiles against version1, pins to version1, and then downgrades.
+ app.submit(new ApplicationPackageBuilder().region("us-east-3")
+ .majorVersion(7)
+ .compileVersion(version1)
+ .build());
+ tester.deploymentTrigger().forceChange(app.instanceId(), app.instance().change().withPin());
+ app.deploy();
+ assertEquals(version1, tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetPlatform());
+ assertEquals(version1, app.application().revisions().get(tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetRevision()).compileVersion().get());
+
+ // A new app, compiled against version1, is deployed on version1.
DeploymentContext newApp = tester.newDeploymentContext("new", "app", "default")
.submit(new ApplicationPackageBuilder().region("us-east-3")
.compileVersion(version1)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java
index 300aa86b5ea..621e847c0d3 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java
@@ -13,7 +13,10 @@ import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
import java.util.List;
+import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -27,7 +30,7 @@ public class OsUpgradeSchedulerTest {
public void schedule_calendar_versioned_release() {
ControllerTester tester = new ControllerTester();
OsUpgradeScheduler scheduler = new OsUpgradeScheduler(tester.controller(), Duration.ofDays(1));
- Instant t0 = Instant.parse("2021-01-23T00:00:00.00Z"); // Outside trigger period
+ Instant t0 = Instant.parse("2022-01-16T00:00:00.00Z"); // Outside trigger period
tester.clock().setInstant(t0);
CloudName cloud = CloudName.from("cloud");
@@ -38,8 +41,8 @@ public class OsUpgradeSchedulerTest {
scheduler.maintain();
assertTrue("No target set", tester.controller().osVersionTarget(cloud).isEmpty());
- // Target is set
- Version version0 = Version.fromString("7.0.0.20210123190005");
+ // Target is set manually
+ Version version0 = Version.fromString("7.0.0.20220101");
tester.controller().upgradeOsIn(cloud, version0, Duration.ofDays(1), false);
// Target remains unchanged as it hasn't expired yet
@@ -49,9 +52,9 @@ public class OsUpgradeSchedulerTest {
assertEquals(version0, tester.controller().osVersionTarget(cloud).get().osVersion().version());
}
- // Just over 45 days pass, and a new target replaces the expired one
- Version version1 = Version.fromString("7.0.0.20210302");
- tester.clock().advance(Duration.ofDays(15).plus(Duration.ofSeconds(1)));
+ // Enough days pass that the next release is triggered
+ Version version1 = Version.fromString("7.0.0.20220228");
+ tester.clock().advance(Duration.ofDays(30));
scheduler.maintain();
assertEquals("Target is unchanged because we're outside trigger period", version0,
tester.controller().osVersionTarget(cloud).get().osVersion().version());
@@ -60,7 +63,7 @@ public class OsUpgradeSchedulerTest {
assertEquals("New target set", version1,
tester.controller().osVersionTarget(cloud).get().osVersion().version());
- // A few days pass and target remains unchanged
+ // A few more days pass and target remains unchanged
tester.clock().advance(Duration.ofDays(2));
scheduler.maintain();
assertEquals(version1, tester.controller().osVersionTarget(cloud).get().osVersion().version());
@@ -112,6 +115,24 @@ public class OsUpgradeSchedulerTest {
scheduleUpgradeAfter(Duration.ofDays(1), version1, tester);
}
+ @Test
+ public void schedule_of_calender_versioned_releases() {
+ Map<String, String> tests = Map.of("2022-01-01", "2021-12-27",
+ "2022-03-01", "2021-12-27",
+ "2022-03-02", "2022-02-28",
+ "2022-04-30", "2022-02-28",
+ "2022-05-01", "2022-04-25",
+ "2022-06-29", "2022-04-25",
+ "2022-07-01", "2022-06-27",
+ "2022-08-28", "2022-06-27",
+ "2022-08-29", "2022-08-29");
+ tests.forEach((now, expectedVersion) -> {
+ Instant instant = LocalDate.parse(now).atStartOfDay().toInstant(ZoneOffset.UTC);
+ LocalDate dateOfWantedVersion = OsUpgradeScheduler.CalendarVersionedRelease.dateOfWantedVersion(instant);
+ assertEquals("scheduled wanted version at " + now, LocalDate.parse(expectedVersion), dateOfWantedVersion);
+ });
+ }
+
private void scheduleUpgradeAfter(Duration duration, Version version, ControllerTester tester) {
tester.clock().advance(duration);
new OsUpgradeScheduler(tester.controller(), Duration.ofDays(1)).maintain();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java
index 3c3f0053e91..3a5b4a90baa 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java
@@ -19,7 +19,9 @@ import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
+import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -52,18 +54,19 @@ public class OsUpgraderTest {
OsUpgrader osUpgrader = osUpgrader(upgradePolicy, cloud1, false);
// Bootstrap system
- List<ZoneId> nonControllerZones = List.of(zone1, zone2, zone3, zone4, zone5).stream()
- .map(ZoneApi::getVirtualId)
- .collect(Collectors.toList());
+ List<ZoneId> nonControllerZones = Stream.of(zone1, zone2, zone3, zone4, zone5)
+ .map(ZoneApi::getVirtualId)
+ .collect(Collectors.toList());
tester.configServer().bootstrap(nonControllerZones, List.of(SystemApplication.tenantHost));
tester.configServer().addNodes(List.of(zone0.getVirtualId()), List.of(SystemApplication.controllerHost));
// Add system application that exists in a real system, but isn't eligible for OS upgrades
tester.configServer().addNodes(nonControllerZones, List.of(SystemApplication.configServer));
- // Fail a few nodes. Failed nodes should not affect versions
+ // Change state of a few nodes. These should not affect convergence
failNodeIn(zone1, SystemApplication.tenantHost);
failNodeIn(zone3, SystemApplication.tenantHost);
+ Node nodeDeferringOsUpgrade = deferOsUpgradeIn(zone2, SystemApplication.tenantHost);
// New OS version released
Version version1 = Version.fromString("7.1");
@@ -91,7 +94,7 @@ public class OsUpgraderTest {
completeUpgrade(version1, SystemApplication.tenantHost, zone1);
statusUpdater.maintain();
assertEquals(5, nodesOn(version1).size());
- assertEquals(11, nodesOn(Version.emptyVersion).size());
+ assertEquals(10, nodesOn(Version.emptyVersion).size());
// zone 2 and 3: begins upgrading
osUpgrader.maintain();
@@ -102,6 +105,10 @@ public class OsUpgraderTest {
// zone 2 and 3: completes upgrade
completeUpgrade(version1, SystemApplication.tenantHost, zone2, zone3);
+ assertEquals("Current version is unchanged for node deferring OS upgrade", Version.emptyVersion,
+ nodeRepository().list(zone2.getVirtualId(), NodeFilter.all().hostnames(nodeDeferringOsUpgrade.hostname()))
+ .get(0)
+ .currentOsVersion());
// zone 4: begins upgrading
osUpgrader.maintain();
@@ -158,15 +165,15 @@ public class OsUpgraderTest {
// First zone upgrades
osUpgrader.maintain();
for (var nodeType : nodeTypes) {
- assertEquals("Dev zone gets a zero budget", Duration.ZERO, upgradeBudget(zone1, nodeType, version));
+ assertEquals(Duration.ofHours(4), upgradeBudget(zone1, nodeType, version));
completeUpgrade(version, nodeType, zone1);
}
// Next set of zones upgrade
osUpgrader.maintain();
- for (var zone : List.of(zone2, zone3)) {
+ for (var zone : List.of(zone1, zone2, zone3)) {
for (var nodeType : nodeTypes) {
- assertEquals("Parallel prod zones share the budget of a single zone", Duration.ofHours(6),
+ assertEquals("Parallel prod zones share the budget of a single zone", Duration.ofHours(4),
upgradeBudget(zone, nodeType, version));
completeUpgrade(version, nodeType, zone);
}
@@ -175,7 +182,7 @@ public class OsUpgraderTest {
// Last zone upgrades
osUpgrader.maintain();
for (var nodeType : nodeTypes) {
- assertEquals(nodeType + " in last prod zone gets the budget of a single zone", Duration.ofHours(6),
+ assertEquals(nodeType + " in last prod zone gets the budget of a single zone", Duration.ofHours(4),
upgradeBudget(zone4, nodeType, version));
completeUpgrade(version, nodeType, zone4);
}
@@ -271,13 +278,23 @@ public class OsUpgraderTest {
.collect(Collectors.toList());
}
- private void failNodeIn(ZoneApi zone, SystemApplication application) {
+ private Node failNodeIn(ZoneApi zone, SystemApplication application) {
+ return patchOneNodeIn(zone, application, (node) -> Node.builder(node).state(Node.State.failed).build());
+ }
+
+ private Node deferOsUpgradeIn(ZoneApi zone, SystemApplication application) {
+ return patchOneNodeIn(zone, application, (node) -> Node.builder(node).deferOsUpgrade(true).build());
+ }
+
+ private Node patchOneNodeIn(ZoneApi zone, SystemApplication application, UnaryOperator<Node> patcher) {
List<Node> nodes = nodeRepository().list(zone.getVirtualId(), NodeFilter.all().applications(application.id()));
if (nodes.isEmpty()) {
throw new IllegalArgumentException("No nodes allocated to " + application.id());
}
Node node = nodes.get(0);
- nodeRepository().putNodes(zone.getVirtualId(), Node.builder(node).state(Node.State.failed).build());
+ Node newNode = patcher.apply(node);
+ nodeRepository().putNodes(zone.getVirtualId(), newNode);
+ return newNode;
}
/** Simulate OS upgrade of nodes allocated to application. In a real system this is done by the node itself */
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java
index 5968b490c09..f053ab7a4a6 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java
@@ -36,7 +36,6 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.pro
import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.stagingTest;
import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.systemTest;
import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.ALL;
-import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.APPLICATION;
import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.PIN;
import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.PLATFORM;
import static org.junit.Assert.assertEquals;
@@ -746,7 +745,7 @@ public class UpgraderTest {
var default1 = tester.newDeploymentContext("tenant1", "default1", "default").submit(DeploymentContext.applicationPackage()).deploy();
// New major version is released, but we don't want to upgrade to it yet
- tester.upgrader().setTargetMajorVersion(Optional.of(6));
+ tester.upgrader().setTargetMajorVersion(OptionalInt.of(6));
version = Version.fromString("7.0");
tester.controllerTester().upgradeSystem(version);
assertEquals(version, tester.controller().readVersionStatus().systemVersion().get().versionNumber());
@@ -771,7 +770,7 @@ public class UpgraderTest {
assertEquals(0, tester.jobs().active().size());
// Now we want to upgrade the latest application
- tester.upgrader().setTargetMajorVersion(Optional.empty());
+ tester.upgrader().setTargetMajorVersion(OptionalInt.empty());
tester.upgrader().maintain();
tester.triggerJobs();
assertEquals(2, tester.jobs().active().size());
@@ -959,7 +958,7 @@ public class UpgraderTest {
tester.controllerTester().upgradeSystem(version0);
// Apps target 6 by default
- tester.upgrader().setTargetMajorVersion(Optional.of(6));
+ tester.upgrader().setTargetMajorVersion(OptionalInt.of(6));
// All applications deploy on current version
var app1 = createAndDeploy("app1", "default");
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 bfbd3836ce7..321ec3ad8ea 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
@@ -14,6 +14,7 @@ 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.MetricsMock;
import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock;
import org.junit.Before;
import org.junit.Test;
@@ -25,6 +26,7 @@ import java.time.ZonedDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
+import static com.yahoo.vespa.hosted.controller.maintenance.VcmrMaintainer.TRACKED_CMRS_METRIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -37,6 +39,7 @@ public class VcmrMaintainerTest {
private ControllerTester tester;
private VcmrMaintainer maintainer;
private NodeRepositoryMock nodeRepo;
+ private MetricsMock metrics;
private final ZoneId zoneId = ZoneId.from("prod.us-east-3");
private final ZoneId zone2 = ZoneId.from("prod.us-west-1");
private final HostName host1 = HostName.of("host1");
@@ -47,7 +50,8 @@ public class VcmrMaintainerTest {
@Before
public void setup() {
tester = new ControllerTester();
- maintainer = new VcmrMaintainer(tester.controller(), Duration.ofMinutes(1));
+ metrics = new MetricsMock();
+ maintainer = new VcmrMaintainer(tester.controller(), Duration.ofMinutes(1), metrics);
nodeRepo = tester.serviceRegistry().configServer().nodeRepository().allowPatching(true);
}
@@ -244,6 +248,8 @@ public class VcmrMaintainerTest {
assertEquals(State.OUT_OF_SYNC, action.getState());
assertEquals(Status.OUT_OF_SYNC, writtenChangeRequest.getStatus());
+ assertEquals(1, metrics.getMetric(context -> "OUT_OF_SYNC".equals(context.get("status")), TRACKED_CMRS_METRIC).get());
+ assertEquals(0, metrics.getMetric(context -> "REQUIRES_OPERATOR_ACTION".equals(context.get("status")), TRACKED_CMRS_METRIC).get());
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java
index 0f7f97d333a..87280c0c1a3 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java
@@ -50,19 +50,19 @@ public class BufferedLogStoreTest {
assertEquals(Optional.empty(), logs.readFinished(id, -1));
assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), -1));
- logs.append(id.application(), id.type(), Step.deployReal, List.of(entry));
+ logs.append(id.application(), id.type(), Step.deployReal, List.of(entry), false);
assertEquals(List.of(entry0),
logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 0));
- logs.append(id.application(), id.type(), Step.deployReal, List.of(entry));
+ logs.append(id.application(), id.type(), Step.deployReal, List.of(entry), false);
assertEquals(List.of(entry0, entry1),
logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
assertEquals(List.of(entry1),
logs.readActive(id.application(), id.type(), 0).get(Step.deployReal));
assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 1));
- logs.append(id.application(), id.type(), Step.deployReal, List.of(entry, entry, entry));
+ logs.append(id.application(), id.type(), Step.deployReal, List.of(entry, entry, entry), false);
assertEquals(List.of(entry0, entry1, entry2, entry3, entry4),
logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
assertEquals(List.of(entry1, entry2, entry3, entry4),
@@ -105,17 +105,28 @@ public class BufferedLogStoreTest {
logged.remove(logged.size() - 1);
logged.remove(logged.size() - 1);
logged.remove(logged.size() - 1);
- logged.add(new LogEntry(2 * maxChunks, entry.at(), LogEntry.Type.warning, "Max log size of " + ((chunkSize * maxChunks) >> 20) + "Mb exceeded; further entries are discarded."));
+ logged.add(new LogEntry(2 * maxChunks, entry.at(), LogEntry.Type.warning, "Max log size of " + ((chunkSize * maxChunks) >> 20) + "Mb exceeded; further user entries are discarded."));
- logs.append(id.application(), id.type(), Step.deployReal, monsterLog);
+ logs.append(id.application(), id.type(), Step.deployReal, monsterLog, false);
assertEquals(logged.size(),
logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size());
assertEquals(logged,
logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
+ // An additional, forced entry is appended.
+ LogEntry forced = new LogEntry(logged.size(), entry.at(), entry.type(), entry.message());
+ logs.append(id.application(), id.type(), Step.deployReal, List.of(forced), true);
+ logged.add(forced);
+ assertEquals(logged.size(),
+ logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size());
+ assertEquals(logged,
+ logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
+ logged.remove(logged.size() - 1);
+
+ // Flushing the buffer clears it again, and makes it ready for reuse.
logs.flush(id);
for (int i = 0; i < 2 * maxChunks + 3; i++)
- logs.append(id.application(), id.type(), Step.deployReal, List.of(entry));
+ logs.append(id.application(), id.type(), Step.deployReal, List.of(entry), false);
assertEquals(logged.size(),
logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size());
assertEquals(logged,
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json
index 696f1ef0ba3..38e9d8c823e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json
@@ -428,6 +428,7 @@
"url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-west-1/run/1",
"start": 1600000000000,
"status": "running",
+ "reason": "triggered by user.myuser",
"versions": {
"targetPlatform": "6.1.0",
"targetApplication": {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json
index 12430b67539..2477e8df56e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json
@@ -9,6 +9,7 @@
"start": 1600000000000,
"end": 1600000000000,
"status": "success",
+ "reason": "triggered by user.myuser",
"versions": {
"targetPlatform": "6.1.0",
"targetApplication": {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java
index 7a137d4e410..7b045e508a9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java
@@ -13,6 +13,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeFilter;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
@@ -27,6 +28,7 @@ import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
+import java.util.OptionalInt;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -218,7 +220,7 @@ public class VersionStatusTest {
tester.upgrader().maintain();
// Setup applications - all running on version0
- ApplicationPackage canaryPolicy = applicationPackage("canary");
+ ApplicationPackage canaryPolicy = applicationPackage("canary", 7);
var canary0 = tester.newDeploymentContext("tenant1", "canary0", "default")
.submit(canaryPolicy)
.deploy();
@@ -231,7 +233,7 @@ public class VersionStatusTest {
ApplicationPackage defaultPolicy = applicationPackage("default");
var default0 = tester.newDeploymentContext("tenant1", "default0", "default")
- .submit(defaultPolicy)
+ .submit(applicationPackage("default", 7))
.deploy();
var default1 = tester.newDeploymentContext("tenant1", "default1", "default")
.submit(defaultPolicy)
@@ -379,6 +381,28 @@ public class VersionStatusTest {
assertTrue(versions.get(0).isReleased());
assertFalse(versions.get(1).isReleased()); // tesst quirk: maven repo lost during controller recreation; useful to test status though
assertTrue(versions.get(2).isReleased());
+
+ // A new major version is released and all canaries upgrade
+ Version version4 = new Version("7.1");
+ tester.controller().applications().setTargetMajorVersion(OptionalInt.of(version3.getMajor())); // Previous remains the default
+ tester.controllerTester().upgradeSystem(version4);
+ tester.upgrader().maintain();
+ tester.triggerJobs();
+ canary0.deployPlatform(version4);
+ canary1.deployPlatform(version4);
+ canary2.deployPlatform(version4);
+ tester.controllerTester().computeVersionStatus();
+ assertEquals(Confidence.normal, confidence(tester.controller(), version4));
+
+ // The single application allowing this major upgrades and confidence becomes 'high'
+ tester.upgrader().maintain();
+ tester.triggerJobs();
+ assertEquals(Change.of(version4), default0.instance().change());
+ default0.jobAborted(systemTest)
+ .jobAborted(stagingTest)
+ .deployPlatform(version4);
+ tester.controllerTester().computeVersionStatus();
+ assertEquals(Confidence.high, confidence(tester.controller(), version4));
}
@Test
@@ -687,6 +711,14 @@ public class VersionStatusTest {
.orElseThrow(() -> new IllegalArgumentException("Expected to find version: " + version));
}
+ private static ApplicationPackage applicationPackage(String upgradePolicy, int majorVersion) {
+ return new ApplicationPackageBuilder().upgradePolicy(upgradePolicy)
+ .region("us-west-1")
+ .region("us-east-3")
+ .majorVersion(majorVersion)
+ .build();
+ }
+
private static final ApplicationPackage canaryApplicationPackage =
new ApplicationPackageBuilder().upgradePolicy("canary")
.region("us-west-1")
@@ -707,12 +739,12 @@ public class VersionStatusTest {
/** Returns empty prebuilt applications for efficiency */
private ApplicationPackage applicationPackage(String upgradePolicy) {
- switch (upgradePolicy) {
- case "canary" : return canaryApplicationPackage;
- case "default" : return defaultApplicationPackage;
- case "conservative" : return conservativeApplicationPackage;
- default : throw new IllegalArgumentException("No upgrade policy '" + upgradePolicy + "'");
- }
+ return switch (upgradePolicy) {
+ case "canary" -> canaryApplicationPackage;
+ case "default" -> defaultApplicationPackage;
+ case "conservative" -> conservativeApplicationPackage;
+ default -> throw new IllegalArgumentException("No upgrade policy '" + upgradePolicy + "'");
+ };
}
}
diff --git a/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java b/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java
index 76f4578ac87..c6ba63b7924 100644
--- a/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java
+++ b/docprocs/src/test/java/com/yahoo/docprocs/indexing/IndexingProcessorTestCase.java
@@ -125,6 +125,7 @@ public class IndexingProcessorTestCase {
return lst.get(0);
}
+ @SuppressWarnings("deprecation")
private static IndexingProcessor newProcessor(String configId) {
return new IndexingProcessor(new DocumentTypeManager(ConfigGetter.getConfig(DocumentmanagerConfig.class, configId)),
ConfigGetter.getConfig(IlscriptsConfig.class, configId),
diff --git a/document/src/vespa/document/config/documentmanager.def b/document/src/vespa/document/config/documentmanager.def
index b1929e42d34..0d0f3876f15 100644
--- a/document/src/vespa/document/config/documentmanager.def
+++ b/document/src/vespa/document/config/documentmanager.def
@@ -5,9 +5,6 @@ namespace=document.config
## Whether attempts to set an undefined field should be ignored rather than causing an error
ignoreundefinedfields bool default=false
-## Whether to enable compression in this process.
-enablecompression bool default=false
-
## Prefer "Vespa 8" format for the "position" type
usev8geopositions bool default=false
diff --git a/document/src/vespa/document/config/documenttypes.def b/document/src/vespa/document/config/documenttypes.def
index 3138e71e025..0c135db7b0d 100644
--- a/document/src/vespa/document/config/documenttypes.def
+++ b/document/src/vespa/document/config/documenttypes.def
@@ -5,9 +5,6 @@ namespace=document.config
## Whether attempts to set an undefined field should be ignored rather than causing an error
ignoreundefinedfields bool default=false
-## Whether to enable compression in this process.
-enablecompression bool default=false
-
## Prefer "Vespa 8" format for the "position" type
usev8geopositions bool default=false
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusVisitorSession.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusVisitorSession.java
index 3607f652a51..1c729008e2c 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusVisitorSession.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusVisitorSession.java
@@ -230,7 +230,6 @@ public class MessageBusVisitorSession implements VisitorSession {
@Override
public Sender createSender(ReplyHandler replyHandler, VisitorParameters visitorParameters) {
- messageBus.setMaxPendingCount(0);
SourceSessionParams sessionParams = createSourceSessionParams(visitorParameters);
return new MessageBusSender(messageBus.createSourceSession(replyHandler, sessionParams));
}
@@ -308,7 +307,7 @@ public class MessageBusVisitorSession implements VisitorSession {
private static final Logger log = Logger.getLogger(MessageBusVisitorSession.class.getName());
- private static AtomicLong sessionCounter = new AtomicLong(0);
+ private static final AtomicLong sessionCounter = new AtomicLong(0);
private static long getNextSessionId() {
return sessionCounter.incrementAndGet();
}
@@ -336,7 +335,7 @@ public class MessageBusVisitorSession implements VisitorSession {
private boolean done = false;
private boolean destroying = false; // For testing and sanity checking
private final Object completionMonitor = new Object();
- private Trace trace;
+ private final Trace trace;
/**
* We keep our own track of pending messages since the sender's pending
* count cannot be relied on in an async task execution context. This
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/EmptyFileReferenceData.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/EmptyFileReferenceData.java
index 8b3bc32ff71..ea8461b42f3 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/EmptyFileReferenceData.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/EmptyFileReferenceData.java
@@ -12,7 +12,7 @@ public class EmptyFileReferenceData extends FileReferenceData {
private int contentRead = 0;
private EmptyFileReferenceData(FileReference fileReference, String filename, Type type, byte[] content, long xxhash) {
- super(fileReference, filename, type);
+ super(fileReference, filename, type, CompressionType.gzip);
this.content = content;
this.xxhash = xxhash;
}
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java
index 5941ed536a8..63ae8faacfe 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileDownloader.java
@@ -6,11 +6,11 @@ import com.yahoo.jrt.Supervisor;
import com.yahoo.vespa.config.Connection;
import com.yahoo.vespa.config.ConnectionPool;
import com.yahoo.vespa.defaults.Defaults;
-
import java.io.File;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -19,6 +19,8 @@ import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
+
/**
* Handles downloads of files (file references only for now)
*
@@ -29,6 +31,7 @@ public class FileDownloader implements AutoCloseable {
private static final Logger log = Logger.getLogger(FileDownloader.class.getName());
private static final Duration defaultSleepBetweenRetries = Duration.ofSeconds(5);
public static final File defaultDownloadDirectory = new File(Defaults.getDefaults().underVespaHome("var/db/vespa/filedistribution"));
+ private static final boolean forceDownload = Boolean.parseBoolean(System.getenv("VESPA_CONFIG_PROXY_FORCE_DOWNLOAD_OF_FILE_REFERENCES"));
private final ConnectionPool connectionPool;
private final Supervisor supervisor;
@@ -37,19 +40,20 @@ public class FileDownloader implements AutoCloseable {
private final FileReferenceDownloader fileReferenceDownloader;
private final Downloads downloads = new Downloads();
- public FileDownloader(ConnectionPool connectionPool, Supervisor supervisor, Duration timeout) {
- this(connectionPool, supervisor, defaultDownloadDirectory, timeout, defaultSleepBetweenRetries);
+ public FileDownloader(ConnectionPool connectionPool, Supervisor supervisor, Duration timeout, Set<CompressionType> acceptedCompressionTypes) {
+ this(connectionPool, supervisor, defaultDownloadDirectory, timeout, defaultSleepBetweenRetries, acceptedCompressionTypes);
}
- public FileDownloader(ConnectionPool connectionPool, Supervisor supervisor, File downloadDirectory, Duration timeout) {
- this(connectionPool, supervisor, downloadDirectory, timeout, defaultSleepBetweenRetries);
+ public FileDownloader(ConnectionPool connectionPool, Supervisor supervisor, File downloadDirectory, Duration timeout, Set<CompressionType> acceptedCompressionTypes) {
+ this(connectionPool, supervisor, downloadDirectory, timeout, defaultSleepBetweenRetries, acceptedCompressionTypes);
}
public FileDownloader(ConnectionPool connectionPool,
Supervisor supervisor,
File downloadDirectory,
Duration timeout,
- Duration sleepBetweenRetries) {
+ Duration sleepBetweenRetries,
+ Set<CompressionType> acceptedCompressionTypes) {
this.connectionPool = connectionPool;
this.supervisor = supervisor;
this.downloadDirectory = downloadDirectory;
@@ -60,7 +64,10 @@ public class FileDownloader implements AutoCloseable {
downloads,
timeout,
sleepBetweenRetries,
- downloadDirectory);
+ downloadDirectory,
+ acceptedCompressionTypes);
+ if (forceDownload)
+ log.log(Level.INFO, "Force download of file references (download even if file reference exists on disk)");
}
public Optional<File> getFile(FileReferenceDownload fileReferenceDownload) {
@@ -99,6 +106,8 @@ public class FileDownloader implements AutoCloseable {
}
private static Optional<File> getFileFromFileSystem(FileReference fileReference, File downloadDirectory) {
+ if (forceDownload) return Optional.empty();
+
File[] files = new File(downloadDirectory, fileReference.value()).listFiles();
if (files == null) return Optional.empty();
if (files.length == 0) return Optional.empty();
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java
index 65c6dd5931d..a285fbaafe2 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReceiver.java
@@ -129,6 +129,7 @@ public class FileReceiver {
moveFileToDestination(inprogressFile, file);
} else {
decompressedDir = Files.createTempDirectory(tmpDir.toPath(), "archive").toFile();
+ log.log(Level.FINE, () -> "compression type to use=" + compressionType);
new FileReferenceCompressor(fileType, compressionType).decompress(inprogressFile, decompressedDir);
moveFileToDestination(decompressedDir, fileReferenceDir);
}
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceCompressor.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceCompressor.java
index b485e6ded86..efb845bafe7 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceCompressor.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceCompressor.java
@@ -118,9 +118,9 @@ public class FileReferenceCompressor {
}
private OutputStream compressedOutputStream(File outputFile) throws IOException {
- log.log(Level.FINE, () -> "Compressing with type " + type + " and compression type " + compressionType);
switch (type) {
case compressed:
+ log.log(Level.FINE, () -> "Compressing with compression type " + compressionType);
switch (compressionType) {
case gzip:
return new GZIPOutputStream(new FileOutputStream(outputFile));
@@ -137,9 +137,9 @@ public class FileReferenceCompressor {
}
private InputStream decompressedInputStream(File inputFile) throws IOException {
- log.log(Level.FINE, () -> "Decompressing with type " + type + " and compression type " + compressionType);
switch (type) {
case compressed:
+ log.log(Level.FINE, () -> "Decompressing with compression type " + compressionType);
switch (compressionType) {
case gzip:
return new GZIPInputStream(new FileInputStream(inputFile));
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceData.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceData.java
index d14f690b2d3..3f83cbea506 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceData.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceData.java
@@ -18,11 +18,13 @@ public abstract class FileReferenceData {
private final FileReference fileReference;
private final String filename;
private final Type type;
+ private final CompressionType compressionType;
- public FileReferenceData(FileReference fileReference, String filename, Type type) {
+ public FileReferenceData(FileReference fileReference, String filename, Type type, CompressionType compressionType) {
this.fileReference = fileReference;
this.filename = filename;
this.type = type;
+ this.compressionType = compressionType;
}
public FileReference fileReference() {return fileReference;}
@@ -31,6 +33,8 @@ public abstract class FileReferenceData {
public Type type() {return type;}
+ public CompressionType compressionType() { return compressionType;}
+
public ByteBuffer content() {
ByteBuffer bb = ByteBuffer.allocate((int)size());
while (bb.remaining() > 0) {
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownload.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownload.java
index 8d6f428eaef..f3a8cf9299d 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownload.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownload.java
@@ -18,7 +18,7 @@ public class FileReferenceDownload {
private final boolean downloadFromOtherSourceIfNotFound;
private final String client;
- public FileReferenceDownload(FileReference fileReference, String client) {
+ public FileReferenceDownload(FileReference fileReference, String client) {
this(fileReference, client, true);
}
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 0267feb9ffc..7078c5aae6c 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/FileReferenceDownloader.java
@@ -5,14 +5,16 @@ import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.config.FileReference;
import com.yahoo.jrt.Int32Value;
import com.yahoo.jrt.Request;
+import com.yahoo.jrt.StringArray;
import com.yahoo.jrt.StringValue;
import com.yahoo.vespa.config.Connection;
import com.yahoo.vespa.config.ConnectionPool;
-
import java.io.File;
import java.time.Duration;
import java.time.Instant;
+import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -20,8 +22,10 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
+
/**
- * Downloads file reference using rpc requests to config server and keeps track of files being downloaded
+ * Downloads file reference from config server and keeps track of files being downloaded
*
* @author hmusum
*/
@@ -38,12 +42,14 @@ public class FileReferenceDownloader {
private final Duration sleepBetweenRetries;
private final Duration rpcTimeout;
private final File downloadDirectory;
+ private final Set<CompressionType> acceptedCompressionTypes;
FileReferenceDownloader(ConnectionPool connectionPool,
Downloads downloads,
Duration timeout,
Duration sleepBetweenRetries,
- File downloadDirectory) {
+ File downloadDirectory,
+ Set<CompressionType> acceptedCompressionTypes) {
this.connectionPool = connectionPool;
this.downloads = downloads;
this.downloadTimeout = timeout;
@@ -51,6 +57,7 @@ public class FileReferenceDownloader {
this.downloadDirectory = downloadDirectory;
String timeoutString = System.getenv("VESPA_CONFIGPROXY_FILEDOWNLOAD_RPC_TIMEOUT");
this.rpcTimeout = Duration.ofSeconds(timeoutString == null ? 30 : Integer.parseInt(timeoutString));
+ this.acceptedCompressionTypes = requireNonEmpty(acceptedCompressionTypes);
}
private void waitUntilDownloadStarted(FileReferenceDownload fileReferenceDownload) {
@@ -132,6 +139,9 @@ public class FileReferenceDownloader {
Request request = new Request("filedistribution.serveFile");
request.parameters().add(new StringValue(fileReferenceDownload.fileReference().value()));
request.parameters().add(new Int32Value(fileReferenceDownload.downloadFromOtherSourceIfNotFound() ? 0 : 1));
+ String[] temp = new String[acceptedCompressionTypes.size()];
+ acceptedCompressionTypes.stream().map(Enum::name).toList().toArray(temp);
+ request.parameters().add(new StringArray(temp));
return request;
}
@@ -160,4 +170,9 @@ public class FileReferenceDownloader {
}
}
+ private static Set<CompressionType> requireNonEmpty(Set<CompressionType> s) {
+ if (Objects.requireNonNull(s).isEmpty()) throw new IllegalArgumentException("set must be non-empty");
+ return s;
+ }
+
}
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyFileReferenceData.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyFileReferenceData.java
index 10de9c072b9..c8fbb639b35 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyFileReferenceData.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyFileReferenceData.java
@@ -17,8 +17,8 @@ public class LazyFileReferenceData extends FileReferenceData {
private final ReadableByteChannel channel;
private final StreamingXXHash64 hasher;
- public LazyFileReferenceData(FileReference fileReference, String filename, Type type, File file) throws IOException {
- super(fileReference, filename, type);
+ public LazyFileReferenceData(FileReference fileReference, String filename, Type type, File file, CompressionType compressionType) throws IOException {
+ super(fileReference, filename, type, compressionType);
this.file = file;
channel = Files.newByteChannel(file.toPath());
this.hasher = XXHashFactory.fastestInstance().newStreamingHash64(0);
diff --git a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyTemporaryStorageFileReferenceData.java b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyTemporaryStorageFileReferenceData.java
index 974d5ff1489..a69370b28ff 100644
--- a/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyTemporaryStorageFileReferenceData.java
+++ b/filedistribution/src/main/java/com/yahoo/vespa/filedistribution/LazyTemporaryStorageFileReferenceData.java
@@ -12,8 +12,8 @@ import java.nio.file.Files;
*/
public class LazyTemporaryStorageFileReferenceData extends LazyFileReferenceData {
- public LazyTemporaryStorageFileReferenceData(FileReference fileReference, String filename, Type type, File file) throws IOException {
- super(fileReference, filename, type, file);
+ public LazyTemporaryStorageFileReferenceData(FileReference fileReference, String filename, Type type, File file, CompressionType compressionType) throws IOException {
+ super(fileReference, filename, type, file, compressionType);
}
public void close() {
diff --git a/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileDownloaderTest.java b/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileDownloaderTest.java
index f5cd1760e89..629ea5915df 100644
--- a/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileDownloaderTest.java
+++ b/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileDownloaderTest.java
@@ -17,7 +17,6 @@ import net.jpountz.xxhash.XXHashFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -26,6 +25,7 @@ import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -41,6 +41,7 @@ import static org.junit.Assert.fail;
public class FileDownloaderTest {
private static final Duration sleepBetweenRetries = Duration.ofMillis(10);
+ private static final Set<FileReferenceData.CompressionType> acceptedCompressionTypes = Set.of(gzip);
private MockConnection connection;
private FileDownloader fileDownloader;
@@ -53,7 +54,7 @@ public class FileDownloaderTest {
downloadDir = Files.createTempDirectory("filedistribution").toFile();
connection = new MockConnection();
supervisor = new Supervisor(new Transport()).setDropEmptyBuffers(true);
- fileDownloader = new FileDownloader(connection, supervisor, downloadDir, Duration.ofSeconds(1), sleepBetweenRetries);
+ fileDownloader = createDownloader(connection, Duration.ofSeconds(1));
} catch (IOException e) {
e.printStackTrace();
fail(e.getMessage());
@@ -167,7 +168,7 @@ public class FileDownloaderTest {
@Test
public void getFileWhenConnectionError() throws IOException {
- fileDownloader = new FileDownloader(connection, supervisor, downloadDir, Duration.ofSeconds(2), sleepBetweenRetries);
+ fileDownloader = createDownloader(connection, Duration.ofSeconds(2));
File downloadDir = fileDownloader.downloadDirectory();
int timesToFail = 2;
@@ -201,7 +202,7 @@ public class FileDownloaderTest {
public void getFileWhenDownloadInProgress() throws IOException, ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);
String filename = "abc.jar";
- fileDownloader = new FileDownloader(connection, supervisor, downloadDir, Duration.ofSeconds(3), sleepBetweenRetries);
+ fileDownloader = createDownloader(connection, Duration.ofSeconds(3));
File downloadDir = fileDownloader.downloadDirectory();
// Delay response so that we can make a second request while downloading the file from the first request
@@ -241,7 +242,7 @@ public class FileDownloaderTest {
Duration timeout = Duration.ofMillis(200);
MockConnection connectionPool = new MockConnection();
connectionPool.setResponseHandler(new MockConnection.WaitResponseHandler(timeout.plus(Duration.ofMillis(1000))));
- FileDownloader fileDownloader = new FileDownloader(connectionPool, supervisor, downloadDir, timeout, sleepBetweenRetries);
+ FileDownloader fileDownloader = createDownloader(connectionPool, timeout);
FileReference xyzzy = new FileReference("xyzzy");
// Should download since we do not have the file on disk
fileDownloader.downloadIfNeeded(new FileReferenceDownload(xyzzy, "test"));
@@ -264,6 +265,16 @@ public class FileDownloaderTest {
assertEquals("content", IOUtils.readFile(downloadedFile));
}
+ @Test
+ public void testCompressionTypes() {
+ try {
+ createDownloader(connection, Duration.ofSeconds(1), Set.of());
+ fail("expected to fail when set is empty");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ }
+
private void writeFileReference(File dir, String fileReferenceString, String fileName) throws IOException {
File fileReferenceDir = new File(dir, fileReferenceString);
fileReferenceDir.mkdir();
@@ -302,6 +313,14 @@ public class FileDownloaderTest {
return fileDownloader.getFile(new FileReferenceDownload(fileReference, "test"));
}
+ private FileDownloader createDownloader(MockConnection connection, Duration timeout) {
+ return createDownloader(connection, timeout, acceptedCompressionTypes);
+ }
+
+ private FileDownloader createDownloader(MockConnection connection, Duration timeout, Set<FileReferenceData.CompressionType> acceptedCompressionTypes) {
+ return new FileDownloader(connection, supervisor, downloadDir, timeout, sleepBetweenRetries, acceptedCompressionTypes);
+ }
+
private static class MockConnection implements ConnectionPool, com.yahoo.vespa.config.Connection {
private ResponseHandler responseHandler;
diff --git a/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileReferenceDataTest.java b/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileReferenceDataTest.java
index 66b731f204b..eda93331c73 100644
--- a/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileReferenceDataTest.java
+++ b/filedistribution/src/test/java/com/yahoo/vespa/filedistribution/FileReferenceDataTest.java
@@ -7,12 +7,15 @@ import com.yahoo.text.Utf8;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.CompressionType.gzip;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.Type;
+import static com.yahoo.vespa.filedistribution.FileReferenceData.Type.compressed;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -27,7 +30,7 @@ public class FileReferenceDataTest {
String content = "blob";
File tempFile = writeTempFile(content);
FileReferenceData fileReferenceData =
- new LazyTemporaryStorageFileReferenceData(new FileReference("ref"), "foo", FileReferenceData.Type.compressed, tempFile);
+ new LazyTemporaryStorageFileReferenceData(new FileReference("ref"), "foo", compressed, tempFile, gzip);
ByteBuffer byteBuffer = ByteBuffer.allocate(100);
assertEquals(4, fileReferenceData.nextContent(byteBuffer));
assertEquals(content, Utf8.toString(Arrays.copyOfRange(byteBuffer.array(), 0, 4)));
@@ -44,7 +47,7 @@ public class FileReferenceDataTest {
String content = "blobbblubbblabb";
File file = writeTempFile(content);
FileReferenceData fileReferenceData =
- new LazyFileReferenceData(new FileReference("ref"), "foo", FileReferenceData.Type.compressed, file);
+ new LazyFileReferenceData(new FileReference("ref"), "foo", Type.compressed, file, CompressionType.gzip);
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
assertEquals(10, fileReferenceData.nextContent(byteBuffer));
assertEquals(content.substring(0,10), Utf8.toString(Arrays.copyOfRange(byteBuffer.array(), 0, 10)));
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 78b7f5fff76..bbe11e18d93 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -68,6 +68,12 @@ public class Flags {
"Takes effect at redeployment (requires restart)",
ZONE_ID, APPLICATION_ID);
+ public static final UnboundBooleanFlag KEEP_STORAGE_NODE_UP = defineFeatureFlag(
+ "keep-storage-node-up", true,
+ List.of("hakonhall"), "2022-07-07", "2022-08-07",
+ "Whether to leave the storage node (with wanted state) UP while the node is permanently down.",
+ "Takes effect immediately for nodes transitioning to permanently down.",
+ ZONE_ID, APPLICATION_ID);
public static final UnboundIntFlag MAX_UNCOMMITTED_MEMORY = defineIntFlag(
"max-uncommitted-memory", 130000,
@@ -153,6 +159,43 @@ public class Flags {
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
+ public static final UnboundIntFlag MBUS_JAVA_NUM_TARGETS = defineIntFlag(
+ "mbus-java-num-targets", 1,
+ List.of("baldersheim"), "2022-07-05", "2023-01-01",
+ "Number of rpc targets per service",
+ "Takes effect at redeployment",
+ ZONE_ID, APPLICATION_ID);
+ public static final UnboundIntFlag MBUS_CPP_NUM_TARGETS = defineIntFlag(
+ "mbus-cpp-num-targets", 1,
+ List.of("baldersheim"), "2022-07-05", "2023-01-01",
+ "Number of rpc targets per service",
+ "Takes effect at redeployment",
+ ZONE_ID, APPLICATION_ID);
+ public static final UnboundIntFlag RPC_NUM_TARGETS = defineIntFlag(
+ "rpc-num-targets", 1,
+ List.of("baldersheim"), "2022-07-05", "2023-01-01",
+ "Number of rpc targets per content node",
+ "Takes effect at redeployment",
+ ZONE_ID, APPLICATION_ID);
+ public static final UnboundIntFlag MBUS_JAVA_EVENTS_BEFORE_WAKEUP = defineIntFlag(
+ "mbus-java-events-before-wakeup", 1,
+ List.of("baldersheim"), "2022-07-05", "2023-01-01",
+ "Number write events before waking up transport thread",
+ "Takes effect at redeployment",
+ ZONE_ID, APPLICATION_ID);
+ public static final UnboundIntFlag MBUS_CPP_EVENTS_BEFORE_WAKEUP = defineIntFlag(
+ "mbus-cpp-events-before-wakeup", 1,
+ List.of("baldersheim"), "2022-07-05", "2023-01-01",
+ "Number write events before waking up transport thread",
+ "Takes effect at redeployment",
+ ZONE_ID, APPLICATION_ID);
+ public static final UnboundIntFlag RPC_EVENTS_BEFORE_WAKEUP = defineIntFlag(
+ "rpc-events-before-wakeup", 1,
+ List.of("baldersheim"), "2022-07-05", "2023-01-01",
+ "Number write events before waking up transport thread",
+ "Takes effect at redeployment",
+ ZONE_ID, APPLICATION_ID);
+
public static final UnboundIntFlag MBUS_NUM_THREADS = defineIntFlag(
"mbus-num-threads", 4,
List.of("baldersheim"), "2022-07-01", "2023-01-01",
@@ -366,9 +409,9 @@ public class Flags {
ZONE_ID, APPLICATION_ID);
public static final UnboundStringFlag APPLICATION_FILES_WITH_UNKNOWN_EXTENSION = defineStringFlag(
- "fail-deployment-for-files-with-unknown-extension", "NOOP",
+ "fail-deployment-for-files-with-unknown-extension", "LOG",
List.of("hmusum"), "2022-04-27", "2022-07-27",
- "Whether to log, fail or do nothing for deployments when app has a file with unknown extension (valid values LOG, FAIL, NOOP)",
+ "Whether to log, fail or do nothing for deployments when app has a file with unknown extension (valid values LOG, FAIL, NOOP)",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
@@ -393,13 +436,6 @@ public class Flags {
"Takes effect on redeployment",
APPLICATION_ID);
- public static final UnboundStringFlag FILE_DISTRIBUTION_COMPRESSION_ALGORITHM = defineStringFlag(
- "file-distribution-compression-algorithm", "gzip",
- List.of("hmusum"), "2022-05-24", "2022-07-24",
- "Which algorithm to use for compressing file references when distributing files. Valid values: none, gzip",
- "Takes effect immediately",
- APPLICATION_ID);
-
public static final UnboundListFlag<String> FILE_DISTRIBUTION_ACCEPTED_COMPRESSION_TYPES = defineListFlag(
"file-distribution-accepted-compression-types", List.of("gzip"), String.class,
List.of("hmusum"), "2022-07-05", "2022-09-05",
@@ -429,12 +465,19 @@ public class Flags {
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag FIX_IPV6_GATEWAY = defineFeatureFlag(
- "fix-ipv6-gateway", false,
+ "fix-ipv6-gateway", true,
List.of("mpolden"), "2022-07-04", "2022-09-01",
"Fix a misconfigured IPv6 gateway automatically",
"Takes effect on first host admin resume",
HOSTNAME);
+ public static final UnboundBooleanFlag SEPARATE_METRIC_CHECK_CONFIG = defineFeatureFlag(
+ "separate-metric-check-config", false,
+ List.of("olaa"), "2022-07-04", "2022-09-01",
+ "Determines whether one metrics config check should be written per Vespa node",
+ "Takes effect on next tick",
+ HOSTNAME);
+
/** 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/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java b/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java
index 3bd5d5d4890..19d3b5b3e43 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/MessageBus.java
@@ -470,6 +470,7 @@ public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler,
*
* @return The resender.
*/
+ @Deprecated // Remove on 9
public Resender getResender() {
return resender;
}
@@ -480,6 +481,7 @@ public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler,
*
* @return The pending count.
*/
+ @Deprecated // Package private on 9
public synchronized int getPendingCount() {
return pendingCount;
}
@@ -490,6 +492,7 @@ public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler,
*
* @return The pending size.
*/
+ @Deprecated // Package private on 9
public synchronized int getPendingSize() {
return pendingSize;
}
@@ -500,6 +503,7 @@ public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler,
*
* @param maxCount The max count.
*/
+ @Deprecated // Remove on 9
public void setMaxPendingCount(int maxCount) {
maxPendingCount = maxCount;
}
@@ -508,6 +512,7 @@ public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler,
* Gets maximum number of messages that can be received without being
* replied to yet.
*/
+ @Deprecated // Remove on 9
public int getMaxPendingCount() {
return maxPendingCount;
}
@@ -518,6 +523,7 @@ public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler,
*
* @param maxSize The max size.
*/
+ @Deprecated // Remove on 9
public void setMaxPendingSize(int maxSize) {
maxPendingSize = maxSize;
}
@@ -526,6 +532,7 @@ public class MessageBus implements ConfigHandler, NetworkOwner, MessageHandler,
* Gets maximum combined size of messages that can be received without
* being replied to yet.
*/
+ @Deprecated // Remove on 9
public int getMaxPendingSize() {
return maxPendingSize;
}
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java
index 60ec6c400a9..6d50fc769be 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/ChokeTestCase.java
@@ -14,7 +14,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
@@ -55,6 +54,7 @@ public class ChokeTestCase {
}
@Test
+ @SuppressWarnings("deprecation")
public void testMaxCount() {
int max = 10;
dstServer.mb.setMaxPendingCount(max);
@@ -103,6 +103,7 @@ public class ChokeTestCase {
}
@Test
+ @SuppressWarnings("deprecation")
public void testMaxSize() {
int size = createMessage("msg").getApproxSize();
int max = size * 10;
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp b/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp
index 8f9037f70dd..412dc29d4f2 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcnetwork.cpp
@@ -17,7 +17,7 @@
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/size_literals.h>
#include <vespa/vespalib/util/stringfmt.h>
-#include <vespa/vespalib/util/threadstackexecutor.h>
+#include <vespa/vespalib/util/gate.h>
#include <vespa/fastos/thread.h>
#include <thread>
@@ -136,12 +136,9 @@ RPCNetwork::RPCNetwork(const RPCNetworkParams &params) :
_targetPool(std::make_unique<RPCTargetPool>(params.getConnectionExpireSecs(), params.getNumRpcTargets())),
_targetPoolTask(std::make_unique<TargetPoolTask>(_scheduler, *_targetPool)),
_servicePool(std::make_unique<RPCServicePool>(*_mirror, 4_Ki)),
- _executor(std::make_unique<vespalib::ThreadStackExecutor>(params.getNumThreads(), 64_Ki)),
_sendV2(std::make_unique<RPCSendV2>()),
_sendAdapters(),
- _compressionConfig(params.getCompressionConfig()),
- _allowDispatchForEncode(params.getDispatchOnEncode()),
- _allowDispatchForDecode(params.getDispatchOnDecode())
+ _compressionConfig(params.getCompressionConfig())
{
}
@@ -413,7 +410,6 @@ void
RPCNetwork::sync()
{
SyncTask task(_scheduler);
- _executor->sync();
task.await();
}
@@ -424,7 +420,6 @@ RPCNetwork::shutdown()
_scheduler.Kill(_targetPoolTask.get());
_transport->ShutDown(true);
_threadPool->Close();
- _executor->shutdown().sync();
}
void
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetwork.h b/messagebus/src/vespa/messagebus/network/rpcnetwork.h
index e706431f90d..b95c0c77b3c 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetwork.h
+++ b/messagebus/src/vespa/messagebus/network/rpcnetwork.h
@@ -65,12 +65,9 @@ private:
std::unique_ptr<RPCTargetPool> _targetPool;
std::unique_ptr<FNET_Task> _targetPoolTask;
std::unique_ptr<RPCServicePool> _servicePool;
- std::unique_ptr<vespalib::SyncableThreadExecutor> _executor;
std::unique_ptr<RPCSendAdapter> _sendV2;
SendAdapterMap _sendAdapters;
CompressionConfig _compressionConfig;
- bool _allowDispatchForEncode;
- bool _allowDispatchForDecode;
/**
* Resolves and assigns a service address for the given recipient using the
@@ -222,10 +219,6 @@ public:
const slobrok::api::IMirrorAPI &getMirror() const override;
CompressionConfig getCompressionConfig() { return _compressionConfig; }
void invoke(FRT_RPCRequest *req);
- vespalib::Executor & getExecutor() const { return *_executor; }
- bool allowDispatchForEncode() const { return _allowDispatchForEncode; }
- bool allowDispatchForDecode() const { return _allowDispatchForDecode; }
-
};
} // namespace mbus
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp
index 8fcadaa64c6..fc060b48fc2 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.cpp
@@ -15,13 +15,10 @@ RPCNetworkParams::RPCNetworkParams(config::ConfigUri configUri) :
_listenPort(0),
_maxInputBufferSize(256_Ki),
_maxOutputBufferSize(256_Ki),
- _numThreads(4),
_numNetworkThreads(1),
_numRpcTargets(1),
_events_before_wakeup(1),
_tcpNoDelay(true),
- _dispatchOnEncode(true),
- _dispatchOnDecode(false),
_connectionExpireSecs(600),
_compressionConfig(CompressionConfig::LZ4, 6, 90, 1024)
{ }
diff --git a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h
index a8d611df653..4a4d92ba797 100644
--- a/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h
+++ b/messagebus/src/vespa/messagebus/network/rpcnetworkparams.h
@@ -19,13 +19,10 @@ private:
int _listenPort;
uint32_t _maxInputBufferSize;
uint32_t _maxOutputBufferSize;
- uint32_t _numThreads;
uint32_t _numNetworkThreads;
uint32_t _numRpcTargets;
uint32_t _events_before_wakeup;
bool _tcpNoDelay;
- bool _dispatchOnEncode;
- bool _dispatchOnDecode;
double _connectionExpireSecs;
CompressionConfig _compressionConfig;
@@ -113,19 +110,6 @@ public:
return *this;
}
- /**
- * Sets number of threads for the thread pool.
- *
- * @param numThreads number of threads for thread pool
- * @return This, to allow chaining.
- */
- RPCNetworkParams &setNumThreads(uint32_t numThreads) {
- _numThreads = numThreads;
- return *this;
- }
-
- uint32_t getNumThreads() const { return _numThreads; }
-
RPCNetworkParams &setTcpNoDelay(bool tcpNoDelay) {
_tcpNoDelay = tcpNoDelay;
return *this;
@@ -177,21 +161,6 @@ public:
}
CompressionConfig getCompressionConfig() const { return _compressionConfig; }
-
- RPCNetworkParams &setDispatchOnDecode(bool dispatchOnDecode) {
- _dispatchOnDecode = dispatchOnDecode;
- return *this;
- }
-
- bool getDispatchOnDecode() const { return _dispatchOnDecode; }
-
- RPCNetworkParams &setDispatchOnEncode(bool dispatchOnEncode) {
- _dispatchOnEncode = dispatchOnEncode;
- return *this;
- }
-
- bool getDispatchOnEncode() const { return _dispatchOnEncode; }
-
RPCNetworkParams &events_before_wakeup(uint32_t value) {
_events_before_wakeup = value;
return *this;
diff --git a/messagebus/src/vespa/messagebus/network/rpcsend.cpp b/messagebus/src/vespa/messagebus/network/rpcsend.cpp
index 7627aa876b3..ff77a1bb639 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsend.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcsend.cpp
@@ -220,14 +220,7 @@ RPCSend::decode(vespalib::stringref protocolName, const vespalib::Version & vers
void
RPCSend::handleReply(Reply::UP reply)
{
- if (!_net->allowDispatchForEncode()) {
- doHandleReply(std::move(reply));
- } else {
- auto rejected = _net->getExecutor().execute(makeLambdaTask([this, reply = std::move(reply)]() mutable {
- doHandleReply(std::move(reply));
- }));
- assert (!rejected);
- }
+ doHandleReply(std::move(reply));
}
void
@@ -256,15 +249,7 @@ void
RPCSend::invoke(FRT_RPCRequest *req)
{
req->Detach();
-
- if (!_net->allowDispatchForDecode()) {
- doRequest(req);
- } else {
- auto rejected = _net->getExecutor().execute(makeLambdaTask([this, req]() {
- doRequest(req);
- }));
- assert (!rejected);
- }
+ doRequest(req);
}
void
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 a36215e005f..ab2f53db863 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
@@ -2,7 +2,6 @@
package ai.vespa.models.evaluation;
import com.yahoo.config.subscription.ConfigGetter;
-import com.yahoo.config.subscription.FileSource;
import com.yahoo.filedistribution.fileacquirer.MockFileAcquirer;
import com.yahoo.path.Path;
import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
@@ -10,7 +9,6 @@ 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 static org.junit.Assert.assertEquals;
@@ -31,8 +29,8 @@ public class ModelTester {
public Map<String, Model> models() { return models; }
+ @SuppressWarnings("deprecation")
private static Map<String, Model> createModels(String path) {
-
RankProfilesConfig config = ConfigGetter.getConfig(RankProfilesConfig.class, fileConfigId(path, "rank-profiles.cfg"));
RankingConstantsConfig constantsConfig = ConfigGetter.getConfig(RankingConstantsConfig.class, fileConfigId(path, "ranking-constants.cfg"));
RankingExpressionsConfig expressionsConfig = ConfigGetter.getConfig(RankingExpressionsConfig.class, fileConfigId(path, "ranking-expressions.cfg"));
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 540c534925e..c4e859bec9f 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
@@ -2,7 +2,6 @@
package ai.vespa.models.evaluation;
import com.yahoo.config.subscription.ConfigGetter;
-import com.yahoo.config.subscription.FileSource;
import com.yahoo.filedistribution.fileacquirer.MockFileAcquirer;
import com.yahoo.path.Path;
import com.yahoo.searchlib.rankingexpression.ExpressionFunction;
@@ -15,7 +14,6 @@ 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;
-
import java.util.ArrayList;
import java.util.List;
@@ -128,6 +126,7 @@ public class ModelsEvaluatorTest {
// TODO: Test argument-less function
// TODO: Test with nested functions
+ @SuppressWarnings("deprecation")
private ModelsEvaluator createModels() {
RankProfilesConfig config = ConfigGetter.getConfig(RankProfilesConfig.class, fileConfigId("rank-profiles.cfg"));
RankingConstantsConfig constantsConfig = ConfigGetter.getConfig(RankingConstantsConfig.class, fileConfigId("ranking-constants.cfg"));
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 27d1c08ea39..992dae22aaf 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
@@ -51,6 +51,7 @@ public class OnnxEvaluatorTest {
assertEquals(function.evaluate(), Tensor.from("tensor<float>(d0[2],d1[1]):[0.63931,0.67574]"));
}
+ @SuppressWarnings("deprecation")
private ModelsEvaluator createModels() {
RankProfilesConfig config = ConfigGetter.getConfig(RankProfilesConfig.class, fileConfigId("rank-profiles.cfg"));
RankingConstantsConfig constantsConfig = ConfigGetter.getConfig(RankingConstantsConfig.class, fileConfigId("ranking-constants.cfg"));
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 7790f8a60d0..0de8ce5f061 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
@@ -257,6 +257,7 @@ public class ModelsEvaluationHandlerTest {
handler.assertResponse(url, properties, 200, expected);
}
+ @SuppressWarnings("deprecation")
static private ModelsEvaluator createModels() {
RankProfilesConfig config = ConfigGetter.getConfig(RankProfilesConfig.class, fileConfigId("rank-profiles.cfg"));
RankingConstantsConfig constantsConfig = ConfigGetter.getConfig(RankingConstantsConfig.class, fileConfigId("ranking-constants.cfg"));
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 f065435ec15..8ab282668da 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
@@ -117,6 +117,7 @@ public class OnnxEvaluationHandlerTest {
handler.assertResponse(url, properties, 200, expected);
}
+ @SuppressWarnings("deprecation")
static private ModelsEvaluator createModels() {
RankProfilesConfig config = ConfigGetter.getConfig(RankProfilesConfig.class, fileConfigId("rank-profiles.cfg"));
RankingConstantsConfig constantsConfig = ConfigGetter.getConfig(RankingConstantsConfig.class, fileConfigId("ranking-constants.cfg"));
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java
index 93195a3c340..b526c573c05 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java
@@ -194,13 +194,28 @@ public class VespaServiceDumperImpl implements VespaServiceDumper {
@Override
public int servicePid() {
if (pid == -1) {
- ContainerPath findPidBinary = nodeAgentCtx.paths().underVespaHome("libexec/vespa/find-pid");
- CommandResult findPidResult = executeCommandInNode(List.of(findPidBinary.pathInContainer(), serviceId()), true);
- this.pid = Integer.parseInt(findPidResult.getOutput());
+ try {
+ pid = findServicePid(serviceId());
+ } catch (RuntimeException e1) {
+ try {
+ // Workaround for Vespa 7 container clusters having service name 'qrserver'
+ if (serviceId().equals("container")) pid = findServicePid("qrserver");
+ else throw e1;
+ } catch (RuntimeException e2) {
+ e1.addSuppressed(e2);
+ throw e1;
+ }
+ }
}
return pid;
}
+ private int findServicePid(String serviceId) {
+ ContainerPath findPidBinary = nodeAgentCtx.paths().underVespaHome("libexec/vespa/find-pid");
+ CommandResult findPidResult = executeCommandInNode(List.of(findPidBinary.pathInContainer(), serviceId), true);
+ return Integer.parseInt(findPidResult.getOutput());
+ }
+
@Override
public CommandResult executeCommandInNode(List<String> command, boolean logOutput) {
CommandResult result = container.executeCommandInContainer(nodeAgentCtx, nodeAgentCtx.users().vespa(), command.toArray(new String[0]));
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java
index 9d9a1304418..368a8da0f90 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java
@@ -12,7 +12,6 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.jdisc.Metric;
import com.yahoo.lang.MutableInteger;
-import com.yahoo.transaction.Mutex;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.JacksonFlag;
import com.yahoo.vespa.flags.ListFlag;
@@ -77,16 +76,14 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
@Override
protected double maintain() {
- try (Mutex lock = nodeRepository().nodes().lockUnallocated()) {
- NodeList nodes = nodeRepository().nodes().list();
- resumeProvisioning(nodes, lock);
- convergeToCapacity(nodes);
- }
+ NodeList nodes = nodeRepository().nodes().list();
+ resumeProvisioning(nodes);
+ convergeToCapacity(nodes);
return 1.0;
}
/** Resume provisioning of already provisioned hosts and their children */
- private void resumeProvisioning(NodeList nodes, Mutex lock) {
+ private void resumeProvisioning(NodeList nodes) {
Map<String, Set<Node>> nodesByProvisionedParentHostname =
nodes.nodeType(NodeType.tenant, NodeType.config, NodeType.controller)
.asList()
@@ -97,9 +94,11 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
nodes.state(Node.State.provisioned).nodeType(NodeType.host, NodeType.confighost, NodeType.controllerhost).forEach(host -> {
Set<Node> children = nodesByProvisionedParentHostname.getOrDefault(host.hostname(), Set.of());
try {
- List<Node> updatedNodes = hostProvisioner.provision(host, children);
- verifyDns(updatedNodes);
- nodeRepository().nodes().write(updatedNodes, lock);
+ try (var lock = nodeRepository().nodes().lockUnallocated()) {
+ List<Node> updatedNodes = hostProvisioner.provision(host, children);
+ verifyDns(updatedNodes);
+ nodeRepository().nodes().write(updatedNodes, lock);
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
log.log(Level.INFO, "Could not provision " + host.hostname() + " with " + children.size() + " children, will retry in " +
interval() + ": " + Exceptions.toMessageString(e));
@@ -189,17 +188,12 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
private List<Node> candidatesForRemoval(List<Node> nodes) {
Map<String, Node> hostsByHostname = new HashMap<>(nodes.stream()
- .filter(node -> {
- switch (node.type()) {
- case host:
- // TODO: Mark empty tenant hosts as wanttoretire & wanttodeprovision elsewhere, then handle as confighost here
- return node.state() != Node.State.parked || node.status().wantToDeprovision();
- case confighost:
- case controllerhost:
- return node.state() == Node.State.parked && node.status().wantToDeprovision();
- default:
- return false;
- }
+ .filter(node -> switch (node.type()) {
+ case host ->
+ // TODO: Mark empty tenant hosts as wanttoretire & wanttodeprovision elsewhere, then handle as confighost here
+ node.state() != Node.State.parked || node.status().wantToDeprovision();
+ case confighost, controllerhost -> node.state() == Node.State.parked && node.status().wantToDeprovision();
+ default -> false;
})
.collect(Collectors.toMap(Node::hostname, Function.identity())));
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/History.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/History.java
index ac804f99cd3..c2d4506a28c 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/History.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/History.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.node;
import com.google.common.collect.ImmutableMap;
import com.yahoo.vespa.hosted.provision.Node;
+import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
@@ -50,6 +51,12 @@ public class History {
return builder.build();
}
+ /** Returns the age of this node as best as we can determine: The time since the first event registered for it */
+ public Duration age(Instant now) {
+ Instant oldestEventTime = events.values().stream().map(event -> event.at()).sorted().findFirst().orElse(now);
+ return Duration.between(oldestEventTime, now);
+ }
+
/** Returns the last event of given type, if it is present in this history */
public Optional<Event> event(Event.Type type) { return Optional.ofNullable(events.get(type)); }
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/NodeListFilter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/NodeListFilter.java
index 8578e3eb5ec..2b790ff7392 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/NodeListFilter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/NodeListFilter.java
@@ -30,4 +30,5 @@ public class NodeListFilter {
public static Predicate<Node> from(List<Node> nodes) {
return makePredicate(nodes);
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/DelegatingOsUpgrader.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/DelegatingOsUpgrader.java
index 30fd2713017..4178d4a6328 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/DelegatingOsUpgrader.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/DelegatingOsUpgrader.java
@@ -7,6 +7,7 @@ import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.filter.NodeListFilter;
+import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;
@@ -39,8 +40,10 @@ public class DelegatingOsUpgrader implements OsUpgrader {
public void upgradeTo(OsVersionTarget target) {
NodeList activeNodes = nodeRepository.nodes().list(Node.State.active).nodeType(target.nodeType());
int numberToUpgrade = Math.max(0, maxActiveUpgrades - activeNodes.changingOsVersionTo(target.version()).size());
+ Instant now = nodeRepository.clock().instant();
NodeList nodesToUpgrade = activeNodes.not().changingOsVersionTo(target.version())
.osVersionIsBefore(target.version())
+ .matching(node -> canUpgradeAt(now, node))
.byIncreasingOsVersion()
.first(numberToUpgrade);
if (nodesToUpgrade.size() == 0) return;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsUpgrader.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsUpgrader.java
index 5310ef339ed..4140de76368 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsUpgrader.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsUpgrader.java
@@ -2,6 +2,9 @@
package com.yahoo.vespa.hosted.provision.os;
import com.yahoo.config.provision.NodeType;
+import com.yahoo.vespa.hosted.provision.Node;
+
+import java.time.Instant;
/**
* Interface for an OS upgrader.
@@ -16,4 +19,9 @@ public interface OsUpgrader {
/** Disable OS upgrade for all nodes of given type */
void disableUpgrade(NodeType type);
+ /** Returns whether node can upgrade at given instant */
+ default boolean canUpgradeAt(Instant instant, Node node) {
+ return true;
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsVersions.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsVersions.java
index 7c6d1cb69db..440046ab818 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsVersions.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/OsVersions.java
@@ -84,7 +84,7 @@ public class OsVersions {
Version target = Optional.ofNullable(change.targets().get(nodeType))
.map(OsVersionTarget::version)
.orElse(Version.emptyVersion);
- chooseUpgrader(nodeType, target).disableUpgrade(nodeType);
+ chooseUpgrader(nodeType, Optional.of(target)).disableUpgrade(nodeType);
return change.withoutTarget(nodeType);
});
}
@@ -120,7 +120,7 @@ public class OsVersions {
try (Lock lock = db.lockOsVersionChange()) {
OsVersionTarget target = readChange().targets().get(nodeType);
if (target == null) return; // No target set for this type
- OsUpgrader upgrader = chooseUpgrader(nodeType, target.version());
+ OsUpgrader upgrader = chooseUpgrader(nodeType, Optional.of(target.version()));
if (resume) {
upgrader.upgradeTo(target);
} else {
@@ -129,17 +129,23 @@ public class OsVersions {
}
}
+ /** Returns whether node can be upgraded now */
+ public boolean canUpgrade(Node node) {
+ return chooseUpgrader(node.type(), Optional.empty()).canUpgradeAt(nodeRepository.clock().instant(), node);
+ }
+
/** Returns the upgrader to use when upgrading given node type to target */
- private OsUpgrader chooseUpgrader(NodeType nodeType, Version target) {
+ private OsUpgrader chooseUpgrader(NodeType nodeType, Optional<Version> target) {
if (reprovisionToUpgradeOs) {
return new RetiringOsUpgrader(nodeRepository);
}
// Require rebuild if we have any nodes of this type on a major version lower than target
- boolean rebuildRequired = nodeRepository.nodes().list(Node.State.active).nodeType(nodeType).stream()
+ boolean rebuildRequired = target.isPresent() &&
+ nodeRepository.nodes().list(Node.State.active).nodeType(nodeType).stream()
.map(Node::status)
.map(Status::osVersion)
.anyMatch(osVersion -> osVersion.current().isPresent() &&
- osVersion.current().get().getMajor() < target.getMajor());
+ osVersion.current().get().getMajor() < target.get().getMajor());
if (rebuildRequired) {
return new RebuildingOsUpgrader(nodeRepository);
}
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 efc377e6cc3..f96effe9e10 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
@@ -47,7 +47,7 @@ public class RebuildingOsUpgrader implements OsUpgrader {
public void upgradeTo(OsVersionTarget target) {
NodeList allNodes = nodeRepository.nodes().list();
Instant now = nodeRepository.clock().instant();
- rebuildableHosts(target, allNodes).forEach(host -> rebuild(host, target.version(), now));
+ rebuildableHosts(target, allNodes, now).forEach(host -> rebuild(host, target.version(), now));
}
@Override
@@ -62,7 +62,7 @@ public class RebuildingOsUpgrader implements OsUpgrader {
return Math.max(0, limit - hostsOfType.rebuilding().size());
}
- private List<Node> rebuildableHosts(OsVersionTarget target, NodeList allNodes) {
+ private List<Node> rebuildableHosts(OsVersionTarget target, NodeList allNodes, Instant now) {
NodeList hostsOfTargetType = allNodes.nodeType(target.nodeType());
int rebuildLimit = rebuildLimit(target.nodeType(), hostsOfTargetType);
@@ -76,6 +76,7 @@ public class RebuildingOsUpgrader implements OsUpgrader {
NodeList candidates = hostsOfTargetType.state(Node.State.active)
.not().rebuilding()
.osVersionIsBefore(target.version())
+ .matching(node -> canUpgradeAt(now, node))
.byIncreasingOsVersion();
for (Node host : candidates) {
if (hostsToRebuild.size() == rebuildLimit) break;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java
index d923c78a929..79b7441cc34 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java
@@ -26,6 +26,9 @@ public class RetiringOsUpgrader implements OsUpgrader {
private static final Logger LOG = Logger.getLogger(RetiringOsUpgrader.class.getName());
+ /** The duration this leaves new nodes alone before scheduling any upgrade */
+ static final Duration GRACE_PERIOD = Duration.ofDays(30);
+
protected final NodeRepository nodeRepository;
public RetiringOsUpgrader(NodeRepository nodeRepository) {
@@ -33,21 +36,27 @@ public class RetiringOsUpgrader implements OsUpgrader {
}
@Override
- public final void upgradeTo(OsVersionTarget target) {
+ public void upgradeTo(OsVersionTarget target) {
NodeList allNodes = nodeRepository.nodes().list();
Instant now = nodeRepository.clock().instant();
NodeList candidates = candidates(now, target, allNodes);
candidates.not().deprovisioning()
+ .matching(node -> canUpgradeAt(now, node))
.byIncreasingOsVersion()
.first(1)
.forEach(node -> deprovision(node, target.version(), now));
}
@Override
- public final void disableUpgrade(NodeType type) {
+ public void disableUpgrade(NodeType type) {
// No action needed in this implementation.
}
+ @Override
+ public boolean canUpgradeAt(Instant instant, Node node) {
+ return node.history().age(instant).compareTo(GRACE_PERIOD) > 0;
+ }
+
/** Returns nodes that are candidates for upgrade */
private NodeList candidates(Instant instant, OsVersionTarget target, NodeList allNodes) {
NodeList activeNodes = allNodes.state(Node.State.active).nodeType(target.nodeType());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
index 3659166c9da..efd76187bc6 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
@@ -161,7 +161,10 @@ class NodesResponse extends SlimeJsonResponse {
object.setLong("rebootGeneration", node.status().reboot().wanted());
object.setLong("currentRebootGeneration", node.status().reboot().current());
node.status().osVersion().current().ifPresent(version -> object.setString("currentOsVersion", version.toFullString()));
- node.status().osVersion().wanted().ifPresent(version -> object.setString("wantedOsVersion", version.toFullString()));
+ node.status().osVersion().wanted().ifPresent(version -> {
+ object.setString("wantedOsVersion", version.toFullString());
+ object.setBool("deferOsUpgrade", !nodeRepository.osVersions().canUpgrade(node));
+ });
node.status().firmwareVerifiedAt().ifPresent(instant -> object.setLong("currentFirmwareCheck", instant.toEpochMilli()));
if (node.type().isHost())
nodeRepository.firmwareChecks().requiredAfter().ifPresent(after -> object.setLong("wantedFirmwareCheck", after.toEpochMilli()));
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 97a8ac0d655..af2a215dae0 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
@@ -172,6 +172,7 @@ public class OsVersionsTest {
Supplier<NodeList> hostNodes = () -> tester.nodeRepository().nodes().list()
.hosts()
.not().state(Node.State.deprovisioned);
+ tester.clock().advance(RetiringOsUpgrader.GRACE_PERIOD.plusDays(1));
// Target is set and upgrade started
var version1 = Version.fromString("7.1");
@@ -233,6 +234,7 @@ public class OsVersionsTest {
Supplier<NodeList> hostNodes = () -> tester.nodeRepository().nodes().list()
.nodeType(NodeType.confighost)
.not().state(Node.State.deprovisioned);
+ tester.clock().advance(RetiringOsUpgrader.GRACE_PERIOD.plusDays(1));
// Target is set with zero budget and upgrade started
var version1 = Version.fromString("7.1");
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 6b1853b3893..19af4d00e54 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
@@ -770,8 +770,9 @@ public class NodesV2ApiTest {
Request.Method.PATCH),
"{\"message\":\"Set osVersion to 7.5.2, upgradeBudget to PT0S for nodes of type host\"}");
+ var nodeRepository = (NodeRepository) tester.container().components().getComponent(MockNodeRepository.class.getName());
+
// Activate target
- var nodeRepository = (NodeRepository)tester.container().components().getComponent(MockNodeRepository.class.getName());
var osUpgradeActivator = new OsUpgradeActivator(nodeRepository, Duration.ofDays(1), new TestMetric());
osUpgradeActivator.run();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json
index b885f7bd7fc..287db73faf6 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node1-os-upgrade-complete.json
@@ -30,6 +30,7 @@
"currentRebootGeneration": 0,
"currentOsVersion": "7.5.2",
"wantedOsVersion": "7.5.2",
+ "deferOsUpgrade": false,
"failCount": 0,
"wantToRetire": false,
"preferToRetire": false,
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java
index aa218755792..f798cea3572 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorContext.java
@@ -47,7 +47,7 @@ public class OrchestratorContext implements AutoCloseable {
/** Create an OrchestratorContext for an operation on a single application. */
public static OrchestratorContext createContextForSingleAppOp(Clock clock) {
return new OrchestratorContext(null, clock, TimeBudget.fromNow(clock, DEFAULT_TIMEOUT_FOR_SINGLE_OP),
- false, false);
+ false, false);
}
public static OrchestratorContext createContextForAdminOp(Clock clock) {
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java
index b3244c1ac74..587875363d5 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java
@@ -1,8 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.orchestrator;
-import com.yahoo.component.annotation.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.component.annotation.Inject;
import com.yahoo.concurrent.UncheckedTimeoutException;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Zone;
@@ -19,6 +19,7 @@ import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerNodeState;
import com.yahoo.vespa.orchestrator.model.ApplicationApi;
import com.yahoo.vespa.orchestrator.model.ApplicationApiFactory;
+import com.yahoo.vespa.orchestrator.model.ContentService;
import com.yahoo.vespa.orchestrator.model.NodeGroup;
import com.yahoo.vespa.orchestrator.model.VespaModelUtil;
import com.yahoo.vespa.orchestrator.policy.BatchHostStateChangeDeniedException;
@@ -99,7 +100,8 @@ public class OrchestratorImpl implements Orchestrator {
{
this(new HostedVespaPolicy(new HostedVespaClusterPolicy(flagSource, zone),
clusterControllerClientFactory,
- applicationApiFactory),
+ applicationApiFactory,
+ flagSource),
clusterControllerClientFactory,
statusService,
serviceMonitor,
@@ -425,7 +427,7 @@ public class OrchestratorImpl implements Orchestrator {
ClusterControllerClient client = clusterControllerClientFactory.createClient(clusterControllers, cluster.clusterId().s());
for (ServiceInstance service : cluster.serviceInstances()) {
try {
- if ( ! client.trySetNodeState(context, service.hostName(), VespaModelUtil.getStorageNodeIndex(service.configId()), MAINTENANCE))
+ if ( ! client.trySetNodeState(context, service.hostName(), VespaModelUtil.getStorageNodeIndex(service.configId()), MAINTENANCE, ContentService.STORAGE_NODE, false))
return false;
}
catch (Exception e) {
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java
index 98ca9f805b4..e563f36a488 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClient.java
@@ -5,6 +5,7 @@ import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.orchestrator.ApplicationStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
+import com.yahoo.vespa.orchestrator.model.ContentService;
import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
/**
@@ -19,7 +20,8 @@ public interface ClusterControllerClient {
* @throws HostStateChangeDeniedException if operation fails, or is otherwise disallowed.
*/
boolean trySetNodeState(OrchestratorContext context, HostName host, int storageNodeIndex,
- ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException;
+ ClusterControllerNodeState wantedState, ContentService contentService, boolean force)
+ throws HostStateChangeDeniedException;
/**
* Requests that a cluster controller sets the requested node to the requested state.
@@ -27,7 +29,8 @@ public interface ClusterControllerClient {
* @throws HostStateChangeDeniedException if operation fails, or is disallowed.
*/
void setNodeState(OrchestratorContext context, HostName host, int storageNodeIndex,
- ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException;
+ ClusterControllerNodeState wantedState, ContentService contentService, boolean force)
+ throws HostStateChangeDeniedException;
/**
* Requests that a cluster controller sets all nodes in the cluster to the requested state.
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java
index 28ba259d2ae..25567cb00a1 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImpl.java
@@ -18,6 +18,7 @@ import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.orchestrator.ApplicationStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
+import com.yahoo.vespa.orchestrator.model.ContentService;
import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.policy.HostedVespaPolicy;
import com.yahoo.yolean.Exceptions;
@@ -53,14 +54,16 @@ public class ClusterControllerClientImpl implements ClusterControllerClient {
}
private boolean setNodeState(OrchestratorContext context, HostName host, int storageNodeIndex,
- ClusterControllerNodeState wantedState, boolean throwOnFailure) {
+ ClusterControllerNodeState wantedState, ContentService contentService,
+ Condition condition, boolean throwOnFailure) {
try {
ClusterControllerClientTimeouts timeouts = context.getClusterControllerTimeouts();
Inspector response = client.send(strategy(hosts), Method.POST)
- .at("cluster", "v2", clusterName, "storage", Integer.toString(storageNodeIndex))
+ .at("cluster", "v2", clusterName, contentService.nameInClusterController(),
+ Integer.toString(storageNodeIndex))
.deadline(timeouts.readBudget())
.parameters(() -> deadline(timeouts))
- .body(stateChangeRequestBytes(wantedState, Condition.SAFE, context.isProbe()))
+ .body(stateChangeRequestBytes(wantedState, condition, context.isProbe()))
.throwing(retryOnRedirect)
.read(SlimeUtils::jsonToSlime).get();
if ( ! response.field("wasModified").asBool()) {
@@ -99,13 +102,17 @@ public class ClusterControllerClientImpl implements ClusterControllerClient {
}
@Override
- public boolean trySetNodeState(OrchestratorContext context, HostName host, int storageNodeIndex, ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException {
- return setNodeState(context, host, storageNodeIndex, wantedState, false);
+ public boolean trySetNodeState(OrchestratorContext context, HostName host, int storageNodeIndex,
+ ClusterControllerNodeState wantedState, ContentService contentService, boolean force)
+ throws HostStateChangeDeniedException {
+ return setNodeState(context, host, storageNodeIndex, wantedState, contentService, force ? Condition.FORCE : Condition.SAFE, false);
}
@Override
- public void setNodeState(OrchestratorContext context, HostName host, int storageNodeIndex, ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException {
- setNodeState(context, host, storageNodeIndex, wantedState, true);
+ public void setNodeState(OrchestratorContext context, HostName host, int storageNodeIndex,
+ ClusterControllerNodeState wantedState, ContentService contentService, boolean force)
+ throws HostStateChangeDeniedException {
+ setNodeState(context, host, storageNodeIndex, wantedState, contentService, force ? Condition.FORCE : Condition.SAFE, true);
}
@Override
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ContentService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ContentService.java
new file mode 100644
index 00000000000..f611bada264
--- /dev/null
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ContentService.java
@@ -0,0 +1,14 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.orchestrator.model;
+
+public enum ContentService {
+ DISTRIBUTOR("distributor"), STORAGE_NODE("storage");
+
+ private final String nameInClusterController;
+
+ ContentService(String nameInClusterController) {
+ this.nameInClusterController = nameInClusterController;
+ }
+
+ public String nameInClusterController() { return nameInClusterController; }
+}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java
index 863c817fe6f..671bd351b3b 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNode.java
@@ -8,5 +8,6 @@ import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
public interface StorageNode extends Comparable<StorageNode> {
HostName hostName();
- void setNodeState(OrchestratorContext context, ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException;
+ void setStorageNodeState(OrchestratorContext context, ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException;
+ void forceDistributorState(OrchestratorContext context, ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException;
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java
index f3e3fd0e674..29f7700ef54 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/StorageNodeImpl.java
@@ -41,7 +41,17 @@ public class StorageNodeImpl implements StorageNode {
}
@Override
- public void setNodeState(OrchestratorContext context, ClusterControllerNodeState wantedNodeState)
+ public void setStorageNodeState(OrchestratorContext context, ClusterControllerNodeState wantedNodeState)
+ throws HostStateChangeDeniedException {
+ setNodeState(context, wantedNodeState, ContentService.STORAGE_NODE, false);
+ }
+
+ @Override
+ public void forceDistributorState(OrchestratorContext context, ClusterControllerNodeState wantedState) throws HostStateChangeDeniedException {
+ setNodeState(context, wantedState, ContentService.DISTRIBUTOR, true);
+ }
+
+ public void setNodeState(OrchestratorContext context, ClusterControllerNodeState wantedNodeState, ContentService contentService, boolean force)
throws HostStateChangeDeniedException {
// The "cluster name" used by the Cluster Controller IS the cluster ID.
String clusterId = this.clusterId.s();
@@ -52,17 +62,18 @@ public class StorageNodeImpl implements StorageNode {
clusterControllers,
clusterId);
- ConfigId configId = storageService.configId();
- int nodeIndex = VespaModelUtil.getStorageNodeIndex(configId);
+ int nodeIndex = VespaModelUtil.getStorageNodeIndex(storageService.configId());
- logger.log(Level.FINE, () -> "Setting cluster controller state for " +
- "application " + applicationInstance.reference().asString() +
- ", host " + hostName() +
- ", cluster name " + clusterId +
- ", node index " + nodeIndex +
- ", node state " + wantedNodeState);
+ logger.log(Level.FINE, () -> (force ? "Force" : "Safe") +
+ " setting cluster controller state for " +
+ "application " + applicationInstance.reference().asString() +
+ ", host " + hostName() +
+ ", cluster name " + clusterId +
+ ", service " + contentService.nameInClusterController() +
+ ", node index " + nodeIndex +
+ ", node state " + wantedNodeState);
- client.setNodeState(context, storageService.hostName(), nodeIndex, wantedNodeState);
+ client.setNodeState(context, storageService.hostName(), nodeIndex, wantedNodeState, contentService, force);
}
@Override
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java
index 2ce62081f51..3f23be5e514 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java
@@ -3,6 +3,10 @@ package com.yahoo.vespa.orchestrator.policy;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.flags.BooleanFlag;
+import com.yahoo.vespa.flags.FetchVector;
+import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerNodeState;
@@ -30,13 +34,16 @@ public class HostedVespaPolicy implements Policy {
private final HostedVespaClusterPolicy clusterPolicy;
private final ClusterControllerClientFactory clusterControllerClientFactory;
private final ApplicationApiFactory applicationApiFactory;
+ private final BooleanFlag keepStorageNodeUpFlag;
public HostedVespaPolicy(HostedVespaClusterPolicy clusterPolicy,
ClusterControllerClientFactory clusterControllerClientFactory,
- ApplicationApiFactory applicationApiFactory) {
+ ApplicationApiFactory applicationApiFactory,
+ FlagSource flagSource) {
this.clusterPolicy = clusterPolicy;
this.clusterControllerClientFactory = clusterControllerClientFactory;
this.applicationApiFactory = applicationApiFactory;
+ this.keepStorageNodeUpFlag = Flags.KEEP_STORAGE_NODE_UP.bindTo(flagSource);
}
@Override
@@ -52,7 +59,7 @@ public class HostedVespaPolicy implements Policy {
// Ask Cluster Controller to set storage nodes in maintenance, unless the node is already allowed
// to be down (or permanently down) in case they are guaranteed to be in maintenance already.
for (StorageNode storageNode : application.getNoRemarksStorageNodesInGroupInClusterOrder()) {
- storageNode.setNodeState(context, ClusterControllerNodeState.MAINTENANCE);
+ storageNode.setStorageNodeState(context, ClusterControllerNodeState.MAINTENANCE);
}
// Ensure all nodes in the group are marked as allowed to be down
@@ -68,7 +75,7 @@ public class HostedVespaPolicy implements Policy {
throws HostStateChangeDeniedException {
// Always defer to Cluster Controller whether it's OK to resume storage node
for (StorageNode storageNode : application.getSuspendedStorageNodesInGroupInReverseClusterOrder()) {
- storageNode.setNodeState(context, ClusterControllerNodeState.UP);
+ storageNode.setStorageNodeState(context, ClusterControllerNodeState.UP);
}
// In particular, we're not modifying the state of PERMANENTLY_DOWN nodes.
@@ -94,10 +101,18 @@ public class HostedVespaPolicy implements Policy {
clusterPolicy.verifyGroupGoingDownPermanentlyIsFine(cluster);
}
- // Ask Cluster Controller to set storage nodes to DOWN.
- // These storage nodes are guaranteed to be NO_REMARKS
+ boolean keepStorageNodeUp = keepStorageNodeUpFlag
+ .with(FetchVector.Dimension.APPLICATION_ID, applicationApi.applicationId().serializedForm())
+ .value();
+
+ // Get permission from the Cluster Controller to remove the content nodes.
for (StorageNode storageNode : applicationApi.getStorageNodesInGroupInClusterOrder()) {
- storageNode.setNodeState(context, ClusterControllerNodeState.DOWN);
+ if (keepStorageNodeUp) {
+ storageNode.setStorageNodeState(context.createSubcontextForSingleAppOp(true), ClusterControllerNodeState.DOWN);
+ storageNode.forceDistributorState(context, ClusterControllerNodeState.DOWN);
+ } else {
+ storageNode.setStorageNodeState(context, ClusterControllerNodeState.DOWN);
+ }
}
// Ensure all nodes in the group are marked as permanently down
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java
index f5b7771d5f4..9fd7b9dade9 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java
@@ -24,6 +24,7 @@ import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactoryMock;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerNodeState;
import com.yahoo.vespa.orchestrator.model.ApplicationApiFactory;
+import com.yahoo.vespa.orchestrator.model.ContentService;
import com.yahoo.vespa.orchestrator.model.NodeGroup;
import com.yahoo.vespa.orchestrator.policy.BatchHostStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
@@ -104,7 +105,8 @@ public class OrchestratorImplTest {
clustercontroller = new ClusterControllerClientFactoryMock();
orchestrator = new OrchestratorImpl(new HostedVespaPolicy(new HostedVespaClusterPolicy(flagSource, zone),
clustercontroller,
- applicationApiFactory),
+ applicationApiFactory,
+ flagSource),
clustercontroller,
statusService,
new DummyServiceMonitor(),
@@ -450,7 +452,8 @@ public class OrchestratorImplTest {
orchestrator = new OrchestratorImpl(new HostedVespaPolicy(new HostedVespaClusterPolicy(flagSource, zone),
clusterControllerClientFactory,
- applicationApiFactory),
+ applicationApiFactory,
+ flagSource),
clusterControllerClientFactory,
statusService,
serviceMonitor,
@@ -459,16 +462,16 @@ public class OrchestratorImplTest {
applicationApiFactory,
flagSource);
- when(fooClient.trySetNodeState(any(), any(), eq(1), eq(ClusterControllerNodeState.MAINTENANCE))).thenReturn(true);
- when(fooClient.trySetNodeState(any(), any(), eq(2), eq(ClusterControllerNodeState.MAINTENANCE))).thenReturn(true);
- when(barClient.trySetNodeState(any(), any(), eq(0), eq(ClusterControllerNodeState.MAINTENANCE))).thenReturn(true);
- when(barClient.trySetNodeState(any(), any(), eq(3), eq(ClusterControllerNodeState.MAINTENANCE))).thenReturn(true);
+ when(fooClient.trySetNodeState(any(), any(), eq(1), eq(ClusterControllerNodeState.MAINTENANCE), eq(ContentService.STORAGE_NODE), eq(false))).thenReturn(true);
+ when(fooClient.trySetNodeState(any(), any(), eq(2), eq(ClusterControllerNodeState.MAINTENANCE), eq(ContentService.STORAGE_NODE), eq(false))).thenReturn(true);
+ when(barClient.trySetNodeState(any(), any(), eq(0), eq(ClusterControllerNodeState.MAINTENANCE), eq(ContentService.STORAGE_NODE), eq(false))).thenReturn(true);
+ when(barClient.trySetNodeState(any(), any(), eq(3), eq(ClusterControllerNodeState.MAINTENANCE), eq(ContentService.STORAGE_NODE), eq(false))).thenReturn(true);
assertTrue(orchestrator.isQuiescent(id));
- when(fooClient.trySetNodeState(any(), any(), eq(2), eq(ClusterControllerNodeState.MAINTENANCE))).thenReturn(false);
+ when(fooClient.trySetNodeState(any(), any(), eq(2), eq(ClusterControllerNodeState.MAINTENANCE), eq(ContentService.STORAGE_NODE), eq(false))).thenReturn(false);
assertFalse(orchestrator.isQuiescent(id));
- when(fooClient.trySetNodeState(any(), any(), eq(2), eq(ClusterControllerNodeState.MAINTENANCE))).thenThrow(new RuntimeException());
+ when(fooClient.trySetNodeState(any(), any(), eq(2), eq(ClusterControllerNodeState.MAINTENANCE), eq(ContentService.STORAGE_NODE), eq(false))).thenThrow(new RuntimeException());
assertFalse(orchestrator.isQuiescent(id));
}
@@ -509,7 +512,8 @@ public class OrchestratorImplTest {
orchestrator = new OrchestratorImpl(new HostedVespaPolicy(new HostedVespaClusterPolicy(flagSource, zone),
clusterControllerClientFactory,
- applicationApiFactory),
+ applicationApiFactory,
+ flagSource),
clusterControllerClientFactory,
statusService,
serviceMonitor,
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorTest.java
index 29d6463a7d7..323ae678b0b 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorTest.java
@@ -57,7 +57,8 @@ public class OrchestratorTest {
var timer = new TestTimer();
var clustercontroller = new ClusterControllerClientFactoryMock();
var applicationApiFactory = new ApplicationApiFactory(3, 5, timer.toUtcClock());
- var policy = new HostedVespaPolicy(new HostedVespaClusterPolicy(flagSource, zone), clustercontroller, applicationApiFactory);
+ var clusterPolicy = new HostedVespaClusterPolicy(flagSource, zone);
+ var policy = new HostedVespaPolicy(clusterPolicy, clustercontroller, applicationApiFactory, flagSource);
var zone = new Zone(SystemName.cd, Environment.prod, RegionName.from("cd-us-east-1"));
this.superModelManager = new MySuperModelProvider();
var duperModel = new DuperModel();
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java
index 597487ccfc5..a5348ad3d07 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java
@@ -5,11 +5,10 @@ import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.orchestrator.ApplicationStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.DummyServiceMonitor;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
+import com.yahoo.vespa.orchestrator.model.ContentService;
import com.yahoo.vespa.orchestrator.model.VespaModelUtil;
-import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
import java.util.HashMap;
import java.util.List;
@@ -30,7 +29,7 @@ public class ClusterControllerClientFactoryMock implements ClusterControllerClie
try {
ClusterId clusterName = VespaModelUtil.getContentClusterName(appInstance, hostName);
int storageNodeIndex = VespaModelUtil.getStorageNodeIndex(appInstance, hostName);
- String globalMapKey = clusterName + "/" + storageNodeIndex;
+ String globalMapKey = clusterName + "/" + ContentService.STORAGE_NODE.nameInClusterController() + "/" + storageNodeIndex;
return nodes.getOrDefault(globalMapKey, ClusterControllerNodeState.UP) == ClusterControllerNodeState.MAINTENANCE;
} catch (Exception e) {
//Catch all - meant to catch cases where the node is not part of a storage cluster
@@ -44,7 +43,7 @@ public class ClusterControllerClientFactoryMock implements ClusterControllerClie
for (HostName host : hosts) {
ClusterId clusterName = VespaModelUtil.getContentClusterName(app, host);
int storageNodeIndex = VespaModelUtil.getStorageNodeIndex(app, host);
- String globalMapKey = clusterName + "/" + storageNodeIndex;
+ String globalMapKey = clusterName + "/" + ContentService.STORAGE_NODE.nameInClusterController() + "/" + storageNodeIndex;
nodes.put(globalMapKey, ClusterControllerNodeState.UP);
}
}
@@ -53,12 +52,12 @@ public class ClusterControllerClientFactoryMock implements ClusterControllerClie
@Override
public ClusterControllerClient createClient(List<HostName> clusterControllers, String clusterName) {
return new ClusterControllerClient() {
- @Override public boolean trySetNodeState(OrchestratorContext context, HostName host, int storageNodeIndex, ClusterControllerNodeState wantedState) {
- nodes.put(clusterName + "/" + storageNodeIndex, wantedState);
+ @Override public boolean trySetNodeState(OrchestratorContext context, HostName host, int storageNodeIndex, ClusterControllerNodeState wantedState, ContentService contentService, boolean force) {
+ nodes.put(clusterName + "/" + contentService.nameInClusterController() + "/" + storageNodeIndex, wantedState);
return true;
}
- @Override public void setNodeState(OrchestratorContext context, HostName host, int storageNodeIndex, ClusterControllerNodeState wantedState) {
- trySetNodeState(context, host, storageNodeIndex, wantedState);
+ @Override public void setNodeState(OrchestratorContext context, HostName host, int storageNodeIndex, ClusterControllerNodeState wantedState, ContentService contentService, boolean force) {
+ trySetNodeState(context, host, storageNodeIndex, wantedState, contentService, false);
}
@Override public void setApplicationState(OrchestratorContext context, ApplicationInstanceId applicationId, ClusterControllerNodeState wantedState) {
nodes.replaceAll((key, state) -> key.startsWith(clusterName + "/") ? wantedState : state);
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImplTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImplTest.java
index b8e078c60d4..81615f59be9 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImplTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientImplTest.java
@@ -7,13 +7,13 @@ import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.orchestrator.ApplicationStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
+import com.yahoo.vespa.orchestrator.model.ContentService;
import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
-import java.time.Clock;
import java.time.Duration;
import java.util.List;
@@ -66,7 +66,7 @@ public class ClusterControllerClientImplTest {
return "{ \"wasModified\": true }";
},
200);
- client.setNodeState(context, host, 2, DOWN);
+ client.setNodeState(context, host, 2, DOWN, ContentService.STORAGE_NODE, false);
clock.advance(Duration.ofSeconds(9));
wire.expect((url, body) -> {
@@ -79,7 +79,7 @@ public class ClusterControllerClientImplTest {
200);
assertEquals("Changing the state of node would violate controller-set-node-state: Failed to set state to DOWN in cluster controller: because",
assertThrows(HostStateChangeDeniedException.class,
- () -> client.setNodeState(context, host, 1, DOWN))
+ () -> client.setNodeState(context, host, 1, DOWN, ContentService.STORAGE_NODE, false))
.getMessage());
}
@@ -93,7 +93,7 @@ public class ClusterControllerClientImplTest {
return "{ \"wasModified\": false, \"reason\": \"no reason\" }";
},
200);
- assertFalse(client.trySetNodeState(OrchestratorContext.createContextForBatchProbe(clock), host, 2, MAINTENANCE));
+ assertFalse(client.trySetNodeState(OrchestratorContext.createContextForBatchProbe(clock), host, 2, MAINTENANCE, ContentService.STORAGE_NODE, false));
}
@Test
@@ -134,7 +134,7 @@ public class ClusterControllerClientImplTest {
assertEquals("Changing the state of node would violate deadline: Timeout while waiting for setNodeState(2, UP) " +
"against [host1, host2, host3]: Timed out after PT10S",
assertThrows(HostStateChangeDeniedException.class,
- () -> client.setNodeState(context, host, 2, UP))
+ () -> client.setNodeState(context, host, 2, UP, ContentService.STORAGE_NODE, false))
.getMessage());
}
@@ -154,7 +154,7 @@ public class ClusterControllerClientImplTest {
assertEquals("Changing the state of node would violate controller-set-node-state: Failed setting node 2 in cluster cc to state UP: " +
"got status code 503 for POST http://host1:19050/cluster/v2/cc/storage/2?timeout=9.6",
assertThrows(HostStateChangeDeniedException.class,
- () -> client.setNodeState(context, host, 2, UP))
+ () -> client.setNodeState(context, host, 2, UP, ContentService.STORAGE_NODE, false))
.getMessage());
}
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java
index 291f96e3dc3..f2e2972ae9f 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java
@@ -80,9 +80,9 @@ class ModelTestUtils {
mock(Metric.class),
new TestTimer(),
new DummyAntiServiceMonitor());
- private final Orchestrator orchestrator = new OrchestratorImpl(new HostedVespaPolicy(new HostedVespaClusterPolicy(flagSource, Zone.defaultZone()),
- clusterControllerClientFactory,
- applicationApiFactory()),
+ private final HostedVespaClusterPolicy clusterPolicy = new HostedVespaClusterPolicy(flagSource, Zone.defaultZone());
+ private final HostedVespaPolicy policy = new HostedVespaPolicy(clusterPolicy, clusterControllerClientFactory, applicationApiFactory(), flagSource);
+ private final Orchestrator orchestrator = new OrchestratorImpl(policy,
clusterControllerClientFactory,
statusService,
serviceMonitor,
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java
index f01ce5a2227..a622142b873 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicyTest.java
@@ -5,6 +5,7 @@ package com.yahoo.vespa.orchestrator.policy;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.orchestrator.OrchestrationException;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClient;
@@ -37,6 +38,7 @@ public class HostedVespaPolicyTest {
private final ClusterControllerClient client = mock(ClusterControllerClient.class);
private final ManualClock clock = new ManualClock();
private final ApplicationApiFactory applicationApiFactory = new ApplicationApiFactory(3, 5, clock);
+ private final InMemoryFlagSource flagSource = new InMemoryFlagSource();
@Before
public void setUp() {
@@ -47,7 +49,7 @@ public class HostedVespaPolicyTest {
public void testGrantSuspension() throws HostStateChangeDeniedException {
final HostedVespaClusterPolicy clusterPolicy = mock(HostedVespaClusterPolicy.class);
when(clusterPolicy.verifyGroupGoingDownIsFine(any())).thenReturn(SuspensionReasons.nothingNoteworthy());
- final HostedVespaPolicy policy = new HostedVespaPolicy(clusterPolicy, clientFactory, applicationApiFactory);
+ final HostedVespaPolicy policy = new HostedVespaPolicy(clusterPolicy, clientFactory, applicationApiFactory, flagSource);
final ApplicationApi applicationApi = mock(ApplicationApi.class);
when(applicationApi.applicationId()).thenReturn(ApplicationId.fromSerializedForm("tenant:app:default"));
@@ -85,8 +87,8 @@ public class HostedVespaPolicyTest {
order.verify(clusterPolicy).verifyGroupGoingDownIsFine(clusterApi3);
order.verify(applicationApi).getNoRemarksStorageNodesInGroupInClusterOrder();
- order.verify(storageNode1).setNodeState(context, ClusterControllerNodeState.MAINTENANCE);
- order.verify(storageNode3).setNodeState(context, ClusterControllerNodeState.MAINTENANCE);
+ order.verify(storageNode1).setStorageNodeState(context, ClusterControllerNodeState.MAINTENANCE);
+ order.verify(storageNode3).setStorageNodeState(context, ClusterControllerNodeState.MAINTENANCE);
order.verify(applicationApi).getNodesInGroupWithStatus(HostStatus.NO_REMARKS);
order.verify(applicationApi).setHostState(context, hostName1, HostStatus.ALLOWED_TO_BE_DOWN);
@@ -99,7 +101,7 @@ public class HostedVespaPolicyTest {
@Test
public void testAcquirePermissionToRemove() throws OrchestrationException {
final HostedVespaClusterPolicy clusterPolicy = mock(HostedVespaClusterPolicy.class);
- final HostedVespaPolicy policy = new HostedVespaPolicy(clusterPolicy, clientFactory, applicationApiFactory);
+ final HostedVespaPolicy policy = new HostedVespaPolicy(clusterPolicy, clientFactory, applicationApiFactory, flagSource);
final ApplicationApi applicationApi = mock(ApplicationApi.class);
when(applicationApi.applicationId()).thenReturn(ApplicationId.fromSerializedForm("tenant:app:default"));
@@ -128,6 +130,8 @@ public class HostedVespaPolicyTest {
InOrder order = inOrder(applicationApi, clusterPolicy, storageNode1, storageNode3);
OrchestratorContext context = mock(OrchestratorContext.class);
+ OrchestratorContext probeContext = mock(OrchestratorContext.class);
+ when(context.createSubcontextForSingleAppOp(true)).thenReturn(probeContext);
policy.acquirePermissionToRemove(context, applicationApi);
order.verify(applicationApi).getClusters();
@@ -136,8 +140,8 @@ public class HostedVespaPolicyTest {
order.verify(clusterPolicy).verifyGroupGoingDownPermanentlyIsFine(clusterApi3);
order.verify(applicationApi).getStorageNodesInGroupInClusterOrder();
- order.verify(storageNode1).setNodeState(context, ClusterControllerNodeState.DOWN);
- order.verify(storageNode3).setNodeState(context, ClusterControllerNodeState.DOWN);
+ order.verify(storageNode1).setStorageNodeState(probeContext, ClusterControllerNodeState.DOWN);
+ order.verify(storageNode3).setStorageNodeState(probeContext, ClusterControllerNodeState.DOWN);
order.verify(applicationApi).getNodesInGroupWith(any());
order.verify(applicationApi).setHostState(context, hostName1, HostStatus.PERMANENTLY_DOWN);
@@ -150,7 +154,7 @@ public class HostedVespaPolicyTest {
@Test
public void testAcquirePermissionToRemoveConfigServer() throws OrchestrationException {
final HostedVespaClusterPolicy clusterPolicy = mock(HostedVespaClusterPolicy.class);
- final HostedVespaPolicy policy = new HostedVespaPolicy(clusterPolicy, clientFactory, applicationApiFactory);
+ final HostedVespaPolicy policy = new HostedVespaPolicy(clusterPolicy, clientFactory, applicationApiFactory, flagSource);
final ApplicationApi applicationApi = mock(ApplicationApi.class);
when(applicationApi.applicationId()).thenReturn(ApplicationId.fromSerializedForm("tenant:app:default"));
@@ -179,6 +183,8 @@ public class HostedVespaPolicyTest {
InOrder order = inOrder(applicationApi, clusterPolicy, storageNode1, storageNode3);
OrchestratorContext context = mock(OrchestratorContext.class);
+ OrchestratorContext probeContext = mock(OrchestratorContext.class);
+ when(context.createSubcontextForSingleAppOp(true)).thenReturn(probeContext);
policy.acquirePermissionToRemove(context, applicationApi);
order.verify(applicationApi).getClusters();
@@ -187,8 +193,8 @@ public class HostedVespaPolicyTest {
order.verify(clusterPolicy).verifyGroupGoingDownPermanentlyIsFine(clusterApi3);
order.verify(applicationApi).getStorageNodesInGroupInClusterOrder();
- order.verify(storageNode1).setNodeState(context, ClusterControllerNodeState.DOWN);
- order.verify(storageNode3).setNodeState(context, ClusterControllerNodeState.DOWN);
+ order.verify(storageNode1).setStorageNodeState(probeContext, ClusterControllerNodeState.DOWN);
+ order.verify(storageNode3).setStorageNodeState(probeContext, ClusterControllerNodeState.DOWN);
order.verify(applicationApi).getNodesInGroupWith(any());
order.verify(applicationApi).setHostState(context, hostName1, HostStatus.PERMANENTLY_DOWN);
diff --git a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
index bf1c828e2e6..e03403f601d 100644
--- a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
+++ b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
@@ -765,11 +765,11 @@ TEST_F(ConformanceTest, testRemoveMulti)
docs.push_back(testDocMan.createRandomDocumentAtLocation(0x01, i));
}
- std::vector<PersistenceProvider::TimeStampAndDocumentId> ids;
+ std::vector<spi::IdAndTimestamp> ids;
for (size_t i(0); i < docs.size(); i++) {
spi->put(bucket1, Timestamp(i), docs[i]);
if (i & 0x1) {
- ids.emplace_back(Timestamp(i), docs[i]->getId());
+ ids.emplace_back(docs[i]->getId(), Timestamp(i));
}
}
diff --git a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
index 81bfdf7f9a3..6eabadc2f86 100644
--- a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
+++ b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.cpp
@@ -476,16 +476,16 @@ DummyPersistence::updateAsync(const Bucket& bucket, Timestamp ts, DocumentUpdate
}
void
-DummyPersistence::removeAsync(const Bucket& b, std::vector<TimeStampAndDocumentId> ids, OperationComplete::UP onComplete)
+DummyPersistence::removeAsync(const Bucket& b, std::vector<spi::IdAndTimestamp> ids, OperationComplete::UP onComplete)
{
DUMMYPERSISTENCE_VERIFY_INITIALIZED;
assert(b.getBucketSpace() == FixedBucketSpaces::default_space());
BucketContentGuard::UP bc(acquireBucketWithLock(b));
uint32_t numRemoves(0);
- for (const TimeStampAndDocumentId & stampedId : ids) {
- const DocumentId & id = stampedId.second;
- Timestamp t = stampedId.first;
+ for (const spi::IdAndTimestamp & stampedId : ids) {
+ const DocumentId & id = stampedId.id;
+ Timestamp t = stampedId.timestamp;
LOG(debug, "remove(%s, %" PRIu64 ", %s)", b.toString().c_str(), uint64_t(t), id.toString().c_str());
while (!bc) {
diff --git a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h
index e015185e5b0..56602b3ab00 100644
--- a/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h
+++ b/persistence/src/vespa/persistence/dummyimpl/dummypersistence.h
@@ -160,7 +160,7 @@ public:
BucketInfoResult getBucketInfo(const Bucket&) const override;
GetResult get(const Bucket&, const document::FieldSet&, const DocumentId&, Context&) const override;
void putAsync(const Bucket&, Timestamp, DocumentSP, OperationComplete::UP) override;
- void removeAsync(const Bucket& b, std::vector<TimeStampAndDocumentId> ids, OperationComplete::UP) override;
+ void removeAsync(const Bucket& b, std::vector<spi::IdAndTimestamp> ids, OperationComplete::UP) override;
void updateAsync(const Bucket&, Timestamp, DocumentUpdateSP, OperationComplete::UP) override;
CreateIteratorResult
diff --git a/persistence/src/vespa/persistence/spi/CMakeLists.txt b/persistence/src/vespa/persistence/spi/CMakeLists.txt
index e4bae1c7551..bc94020ae95 100644
--- a/persistence/src/vespa/persistence/spi/CMakeLists.txt
+++ b/persistence/src/vespa/persistence/spi/CMakeLists.txt
@@ -10,6 +10,7 @@ vespa_add_library(persistence_spi OBJECT
context.cpp
docentry.cpp
exceptions.cpp
+ id_and_timestamp.cpp
persistenceprovider.cpp
read_consistency.cpp
resource_usage.cpp
diff --git a/persistence/src/vespa/persistence/spi/abstractpersistenceprovider.cpp b/persistence/src/vespa/persistence/spi/abstractpersistenceprovider.cpp
index f301a9c5428..04d06235f59 100644
--- a/persistence/src/vespa/persistence/spi/abstractpersistenceprovider.cpp
+++ b/persistence/src/vespa/persistence/spi/abstractpersistenceprovider.cpp
@@ -10,8 +10,8 @@ void
AbstractPersistenceProvider::removeIfFoundAsync(const Bucket& b, Timestamp timestamp,
const DocumentId& id, OperationComplete::UP onComplete)
{
- std::vector<TimeStampAndDocumentId> ids;
- ids.emplace_back(timestamp, id);
+ std::vector<IdAndTimestamp> ids;
+ ids.emplace_back(id, timestamp);
removeAsync(b, std::move(ids), std::move(onComplete));
}
diff --git a/persistence/src/vespa/persistence/spi/id_and_timestamp.cpp b/persistence/src/vespa/persistence/spi/id_and_timestamp.cpp
new file mode 100644
index 00000000000..fba45990744
--- /dev/null
+++ b/persistence/src/vespa/persistence/spi/id_and_timestamp.cpp
@@ -0,0 +1,17 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "id_and_timestamp.h"
+
+namespace storage::spi {
+
+IdAndTimestamp::IdAndTimestamp() : id(), timestamp(0) {}
+IdAndTimestamp::IdAndTimestamp(document::DocumentId id_, Timestamp timestamp_) noexcept
+ : id(std::move(id_)),
+ timestamp(timestamp_)
+{}
+
+IdAndTimestamp::IdAndTimestamp(const IdAndTimestamp&) = default;
+IdAndTimestamp& IdAndTimestamp::operator=(const IdAndTimestamp&) = default;
+IdAndTimestamp::IdAndTimestamp(IdAndTimestamp&&) noexcept = default;
+IdAndTimestamp& IdAndTimestamp::operator=(IdAndTimestamp&&) noexcept = default;
+
+}
diff --git a/persistence/src/vespa/persistence/spi/id_and_timestamp.h b/persistence/src/vespa/persistence/spi/id_and_timestamp.h
new file mode 100644
index 00000000000..d8cdba3d063
--- /dev/null
+++ b/persistence/src/vespa/persistence/spi/id_and_timestamp.h
@@ -0,0 +1,38 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include "types.h"
+#include <vespa/document/base/documentid.h>
+
+namespace storage::spi {
+
+/**
+ * Convenience wrapper for referencing a document ID at a particular timestamp.
+ *
+ * Prefer this instead of a std::pair due to named fields and a pre-provided hash function.
+ */
+struct IdAndTimestamp {
+ document::DocumentId id;
+ Timestamp timestamp;
+
+ IdAndTimestamp();
+ IdAndTimestamp(document::DocumentId id_, Timestamp timestamp_) noexcept;
+
+ IdAndTimestamp(const IdAndTimestamp&);
+ IdAndTimestamp& operator=(const IdAndTimestamp&);
+ IdAndTimestamp(IdAndTimestamp&&) noexcept;
+ IdAndTimestamp& operator=(IdAndTimestamp&&) noexcept;
+
+ bool operator==(const IdAndTimestamp& rhs) const noexcept {
+ return ((id == rhs.id) && (timestamp == rhs.timestamp));
+ }
+
+ struct hash {
+ size_t operator()(const IdAndTimestamp& id_ts) const noexcept {
+ const size_t h = document::GlobalId::hash()(id_ts.id.getGlobalId());
+ return h ^ (id_ts.timestamp + 0x9e3779b9U + (h << 6U) + (h >> 2U)); // Basically boost::hash_combine
+ }
+ };
+};
+
+}
diff --git a/persistence/src/vespa/persistence/spi/persistenceprovider.cpp b/persistence/src/vespa/persistence/spi/persistenceprovider.cpp
index 03cefb8df89..911b3753b1f 100644
--- a/persistence/src/vespa/persistence/spi/persistenceprovider.cpp
+++ b/persistence/src/vespa/persistence/spi/persistenceprovider.cpp
@@ -44,8 +44,8 @@ RemoveResult
PersistenceProvider::remove(const Bucket& bucket, Timestamp timestamp, const DocumentId & docId) {
auto catcher = std::make_unique<CatchResult>();
auto future = catcher->future_result();
- std::vector<TimeStampAndDocumentId> ids;
- ids.emplace_back(timestamp, docId);
+ std::vector<IdAndTimestamp> ids;
+ ids.emplace_back(docId, timestamp);
removeAsync(bucket, std::move(ids), std::move(catcher));
return dynamic_cast<const RemoveResult &>(*future.get());
}
diff --git a/persistence/src/vespa/persistence/spi/persistenceprovider.h b/persistence/src/vespa/persistence/spi/persistenceprovider.h
index a90d39e8334..d3e1465e528 100644
--- a/persistence/src/vespa/persistence/spi/persistenceprovider.h
+++ b/persistence/src/vespa/persistence/spi/persistenceprovider.h
@@ -4,6 +4,7 @@
#include "bucket.h"
#include "bucketinfo.h"
#include "context.h"
+#include "id_and_timestamp.h"
#include "result.h"
#include "selection.h"
#include "clusterstate.h"
@@ -170,7 +171,7 @@ struct PersistenceProvider
* @param timestamp The timestamp for the new bucket entry.
* @param id The ID to remove
*/
- virtual void removeAsync(const Bucket&, std::vector<TimeStampAndDocumentId> ids, OperationComplete::UP) = 0;
+ virtual void removeAsync(const Bucket&, std::vector<IdAndTimestamp> ids, OperationComplete::UP) = 0;
/**
* @see remove()
diff --git a/screwdriver/build-vespa.sh b/screwdriver/build-vespa.sh
index ca9ada81205..6a93474c620 100755
--- a/screwdriver/build-vespa.sh
+++ b/screwdriver/build-vespa.sh
@@ -53,23 +53,6 @@ case $SHOULD_BUILD in
esac
if [[ $SHOULD_BUILD == systemtest ]]; then
- dnf module enable -y ruby:2.7
- dnf install -y \
- gcc-toolset-11-annobin \
- gcc-toolset-11-annobin-plugin-gcc \
- gcc-toolset-11-binutils \
- gcc-toolset-11-gcc-c++ \
- gcc-toolset-11-libatomic-devel \
- libxml2-devel \
- ruby \
- ruby-devel \
- rubygems-devel \
- rubygem-net-telnet \
- zstd
-
- source /opt/rh/gcc-toolset-11/enable
- gem install libxml-ruby gnuplot distribution test-unit builder concurrent-ruby bigdecimal ffi parallel
-
cd $HOME
git clone https://github.com/vespa-engine/system-test
export SYSTEM_TEST_DIR=$(pwd)/system-test
diff --git a/searchcore/src/tests/proton/docsummary/docsummary.cpp b/searchcore/src/tests/proton/docsummary/docsummary.cpp
index 49fd82c3a36..fd545fcdf77 100644
--- a/searchcore/src/tests/proton/docsummary/docsummary.cpp
+++ b/searchcore/src/tests/proton/docsummary/docsummary.cpp
@@ -322,8 +322,8 @@ assertString(const std::string & exp, const std::string & fieldName,
DocumentStoreAdapter &dsa, uint32_t id)
{
GeneralResultPtr res = getResult(dsa, id);
- return EXPECT_EQUAL(exp, std::string(res->GetEntry(fieldName.c_str())->_stringval,
- res->GetEntry(fieldName.c_str())->_stringlen));
+ return EXPECT_EQUAL(exp, std::string(res->GetPresentEntry(fieldName.c_str())->_stringval,
+ res->GetPresentEntry(fieldName.c_str())->_stringlen));
}
void
@@ -392,24 +392,24 @@ TEST_F("requireThatAdapterHandlesAllFieldTypes", Fixture)
bc.createFieldCacheRepo(f.getResultConfig())->getFieldCache("class0"),
f.getMarkupFields());
GeneralResultPtr res = getResult(dsa, 0);
- EXPECT_EQUAL(255u, res->GetEntry("a")->_intval);
- EXPECT_EQUAL(32767u, res->GetEntry("b")->_intval);
- EXPECT_EQUAL(2147483647u, res->GetEntry("c")->_intval);
- EXPECT_EQUAL(2147483648u, res->GetEntry("d")->_int64val);
- EXPECT_APPROX(1234.56, res->GetEntry("e")->_doubleval, 10e-5);
- EXPECT_APPROX(9876.54, res->GetEntry("f")->_doubleval, 10e-5);
- EXPECT_EQUAL("foo", std::string(res->GetEntry("g")->_stringval,
- res->GetEntry("g")->_stringlen));
- EXPECT_EQUAL("bar", std::string(res->GetEntry("h")->_stringval,
- res->GetEntry("h")->_stringlen));
- EXPECT_EQUAL("baz", std::string(res->GetEntry("i")->_dataval,
- res->GetEntry("i")->_datalen));
- EXPECT_EQUAL("qux", std::string(res->GetEntry("j")->_dataval,
- res->GetEntry("j")->_datalen));
- EXPECT_EQUAL("<foo>", std::string(res->GetEntry("k")->_stringval,
- res->GetEntry("k")->_stringlen));
- EXPECT_EQUAL("{foo:10}", std::string(res->GetEntry("l")->_stringval,
- res->GetEntry("l")->_stringlen));
+ EXPECT_EQUAL(255u, res->GetPresentEntry("a")->_intval);
+ EXPECT_EQUAL(32767u, res->GetPresentEntry("b")->_intval);
+ EXPECT_EQUAL(2147483647u, res->GetPresentEntry("c")->_intval);
+ EXPECT_EQUAL(2147483648u, res->GetPresentEntry("d")->_int64val);
+ EXPECT_APPROX(1234.56, res->GetPresentEntry("e")->_doubleval, 10e-5);
+ EXPECT_APPROX(9876.54, res->GetPresentEntry("f")->_doubleval, 10e-5);
+ EXPECT_EQUAL("foo", std::string(res->GetPresentEntry("g")->_stringval,
+ res->GetPresentEntry("g")->_stringlen));
+ EXPECT_EQUAL("bar", std::string(res->GetPresentEntry("h")->_stringval,
+ res->GetPresentEntry("h")->_stringlen));
+ EXPECT_EQUAL("baz", std::string(res->GetPresentEntry("i")->_dataval,
+ res->GetPresentEntry("i")->_datalen));
+ EXPECT_EQUAL("qux", std::string(res->GetPresentEntry("j")->_dataval,
+ res->GetPresentEntry("j")->_datalen));
+ EXPECT_EQUAL("<foo>", std::string(res->GetPresentEntry("k")->_stringval,
+ res->GetPresentEntry("k")->_stringlen));
+ EXPECT_EQUAL("{foo:10}", std::string(res->GetPresentEntry("l")->_stringval,
+ res->GetPresentEntry("l")->_stringlen));
}
TEST_F("requireThatAdapterHandlesMultipleDocuments", Fixture)
@@ -433,11 +433,11 @@ TEST_F("requireThatAdapterHandlesMultipleDocuments", Fixture)
f.getMarkupFields());
{ // doc 0
GeneralResultPtr res = getResult(dsa, 0);
- EXPECT_EQUAL(1000u, res->GetEntry("a")->_intval);
+ EXPECT_EQUAL(1000u, res->GetPresentEntry("a")->_intval);
}
{ // doc 1
GeneralResultPtr res = getResult(dsa, 1);
- EXPECT_EQUAL(2000u, res->GetEntry("a")->_intval);
+ EXPECT_EQUAL(2000u, res->GetPresentEntry("a")->_intval);
}
{ // doc 2
DocsumStoreValue docsum = dsa.getMappedDocsum(2);
@@ -445,7 +445,7 @@ TEST_F("requireThatAdapterHandlesMultipleDocuments", Fixture)
}
{ // doc 0 (again)
GeneralResultPtr res = getResult(dsa, 0);
- EXPECT_EQUAL(1000u, res->GetEntry("a")->_intval);
+ EXPECT_EQUAL(1000u, res->GetPresentEntry("a")->_intval);
}
EXPECT_EQUAL(0u, bc._str.lastSyncToken());
uint64_t flushToken = bc._str.initFlush(bc._serialNum - 1);
@@ -466,8 +466,8 @@ TEST_F("requireThatAdapterHandlesDocumentIdField", Fixture)
bc.createFieldCacheRepo(f.getResultConfig())->getFieldCache("class4"),
f.getMarkupFields());
GeneralResultPtr res = getResult(dsa, 0);
- EXPECT_EQUAL("id:ns:searchdocument::0", std::string(res->GetEntry("documentid")->_stringval,
- res->GetEntry("documentid")->_stringlen));
+ EXPECT_EQUAL("id:ns:searchdocument::0", std::string(res->GetPresentEntry("documentid")->_stringval,
+ res->GetPresentEntry("documentid")->_stringlen));
}
GlobalId gid1 = DocumentId("id:ns:searchdocument::1").getGlobalId(); // lid 1
@@ -960,14 +960,14 @@ TEST_F("requireThatUrisAreUsed", Fixture)
GeneralResultPtr res = getResult(dsa, 1);
{
vespalib::Slime slime;
- decode(res->GetEntry("uriarray"), slime);
+ decode(res->GetPresentEntry("uriarray"), slime);
EXPECT_TRUE(slime.get().valid());
EXPECT_EQUAL("http://www.example.com:82/fluke?ab=2#8", asVstring(slime.get()[0]));
EXPECT_EQUAL("http://www.flickr.com:82/fluke?ab=2#9", asVstring(slime.get()[1]));
}
{
vespalib::Slime slime;
- decode(res->GetEntry("uriwset"), slime);
+ decode(res->GetPresentEntry("uriwset"), slime);
EXPECT_TRUE(slime.get().valid());
EXPECT_EQUAL(4L, slime.get()[0]["weight"].asLong());
EXPECT_EQUAL(7L, slime.get()[1]["weight"].asLong());
@@ -1089,14 +1089,14 @@ TEST_F("requireThatRawFieldsWorks", Fixture)
GeneralResultPtr res = getResult(dsa, 1);
{
vespalib::Slime slime;
- decode(res->GetEntry("araw"), slime);
+ decode(res->GetPresentEntry("araw"), slime);
EXPECT_TRUE(slime.get().valid());
EXPECT_EQUAL(vespalib::Base64::encode(raw1a0), b64encode(slime.get()[0]));
EXPECT_EQUAL(vespalib::Base64::encode(raw1a1), b64encode(slime.get()[1]));
}
{
vespalib::Slime slime;
- decode(res->GetEntry("wraw"), slime);
+ decode(res->GetPresentEntry("wraw"), slime);
EXPECT_TRUE(slime.get().valid());
EXPECT_EQUAL(46L, slime.get()[0]["weight"].asLong());
EXPECT_EQUAL(45L, slime.get()[1]["weight"].asLong());
diff --git a/searchcore/src/vespa/searchcore/bmcluster/spi_bm_feed_handler.cpp b/searchcore/src/vespa/searchcore/bmcluster/spi_bm_feed_handler.cpp
index 69013e8d7c5..dcdba3b0715 100644
--- a/searchcore/src/vespa/searchcore/bmcluster/spi_bm_feed_handler.cpp
+++ b/searchcore/src/vespa/searchcore/bmcluster/spi_bm_feed_handler.cpp
@@ -134,8 +134,8 @@ SpiBmFeedHandler::remove(const document::Bucket& bucket, const DocumentId& docum
auto provider = get_provider(bucket);
if (provider) {
Bucket spi_bucket(bucket);
- std::vector<storage::spi::PersistenceProvider::TimeStampAndDocumentId> ids;
- ids.emplace_back(Timestamp(timestamp), document_id);
+ std::vector<storage::spi::IdAndTimestamp> ids;
+ ids.emplace_back(document_id, Timestamp(timestamp));
provider->removeAsync(spi_bucket, std::move(ids), std::make_unique<MyOperationComplete>(provider, _errors, spi_bucket, tracker));
} else {
++_errors;
diff --git a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp
index 425ac6aefd0..5bd92fd788f 100644
--- a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanager.cpp
@@ -95,6 +95,13 @@ SummarySetup(const vespalib::string & baseDir, const DocTypeName & docTypeName,
{
DocsumBlobEntryFilter docsum_blob_entry_filter;
docsum_blob_entry_filter.add_skip(RES_INT);
+ docsum_blob_entry_filter.add_skip(RES_SHORT);
+ docsum_blob_entry_filter.add_skip(RES_BOOL);
+ docsum_blob_entry_filter.add_skip(RES_BYTE);
+ docsum_blob_entry_filter.add_skip(RES_FLOAT);
+ docsum_blob_entry_filter.add_skip(RES_DOUBLE);
+ docsum_blob_entry_filter.add_skip(RES_INT64);
+ docsum_blob_entry_filter.add_skip(RES_TENSOR);
auto resultConfig = std::make_unique<ResultConfig>(docsum_blob_entry_filter);
if (!resultConfig->ReadConfig(summaryCfg, make_string("SummaryManager(%s)", baseDir.c_str()).c_str())) {
std::ostringstream oss;
diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
index 5a0bcb1cd41..81a7244ba1d 100644
--- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
+++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
@@ -370,21 +370,21 @@ PersistenceEngine::putAsync(const Bucket &bucket, Timestamp ts, storage::spi::Do
}
void
-PersistenceEngine::removeAsync(const Bucket& b, std::vector<TimeStampAndDocumentId> ids, OperationComplete::UP onComplete)
+PersistenceEngine::removeAsync(const Bucket& b, std::vector<storage::spi::IdAndTimestamp> ids, OperationComplete::UP onComplete)
{
if (ids.size() == 1) {
- removeAsyncSingle(b, ids[0].first, ids[0].second, std::move(onComplete));
+ removeAsyncSingle(b, ids[0].timestamp, ids[0].id, std::move(onComplete));
} else {
removeAsyncMulti(b, std::move(ids), std::move(onComplete));
}
}
void
-PersistenceEngine::removeAsyncMulti(const Bucket& b, std::vector<TimeStampAndDocumentId> ids, OperationComplete::UP onComplete) {
+PersistenceEngine::removeAsyncMulti(const Bucket& b, std::vector<storage::spi::IdAndTimestamp> ids, OperationComplete::UP onComplete) {
ReadGuard rguard(_rwMutex);
//TODO Group per document type/handler and handle in one go.
- for (const TimeStampAndDocumentId & stampedId : ids) {
- const document::DocumentId & id = stampedId.second;
+ for (const auto & stampedId : ids) {
+ const document::DocumentId & id = stampedId.id;
if (!id.hasDocType()) {
return onComplete->onComplete(
std::make_unique<RemoveResult>(Result::ErrorType::PERMANENT_ERROR,
@@ -399,11 +399,11 @@ PersistenceEngine::removeAsyncMulti(const Bucket& b, std::vector<TimeStampAndDoc
}
}
auto transportContext = std::make_shared<AsyncRemoveTransportContext>(ids.size(), std::move(onComplete));
- for (const TimeStampAndDocumentId & stampedId : ids) {
- const document::DocumentId & id = stampedId.second;
+ for (const auto & stampedId : ids) {
+ const document::DocumentId & id = stampedId.id;
DocTypeName docType(id.getDocType());
IPersistenceHandler *handler = getHandler(rguard, b.getBucketSpace(), docType);
- handler->handleRemove(feedtoken::make(transportContext), b, stampedId.first, id);
+ handler->handleRemove(feedtoken::make(transportContext), b, stampedId.timestamp, id);
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h
index a8886e19def..c16cc6e6a83 100644
--- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h
+++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.h
@@ -89,7 +89,7 @@ private:
ClusterState::SP savedClusterState(BucketSpace bucketSpace) const;
std::shared_ptr<BucketExecutor> get_bucket_executor() noexcept { return _bucket_executor.lock(); }
void removeAsyncSingle(const Bucket&, Timestamp, const document::DocumentId &id, OperationComplete::UP);
- void removeAsyncMulti(const Bucket&, std::vector<TimeStampAndDocumentId> ids, OperationComplete::UP);
+ void removeAsyncMulti(const Bucket&, std::vector<storage::spi::IdAndTimestamp> ids, OperationComplete::UP);
public:
typedef std::unique_ptr<PersistenceEngine> UP;
@@ -107,7 +107,7 @@ public:
void setActiveStateAsync(const Bucket&, BucketInfo::ActiveState, OperationComplete::UP) override;
BucketInfoResult getBucketInfo(const Bucket&) const override;
void putAsync(const Bucket &, Timestamp, storage::spi::DocumentSP, OperationComplete::UP) override;
- void removeAsync(const Bucket&, std::vector<TimeStampAndDocumentId> ids, OperationComplete::UP) override;
+ void removeAsync(const Bucket&, std::vector<storage::spi::IdAndTimestamp> ids, OperationComplete::UP) override;
void updateAsync(const Bucket&, Timestamp, storage::spi::DocumentUpdateSP, OperationComplete::UP) override;
GetResult get(const Bucket&, const document::FieldSet&, const document::DocumentId&, Context&) const override;
CreateIteratorResult
diff --git a/searchlib/CMakeLists.txt b/searchlib/CMakeLists.txt
index 43a8da19191..f9b6271b47b 100644
--- a/searchlib/CMakeLists.txt
+++ b/searchlib/CMakeLists.txt
@@ -46,6 +46,7 @@ vespa_define_module(
src/vespa/searchlib/test
src/vespa/searchlib/test/diskindex
src/vespa/searchlib/test/fakedata
+ src/vespa/searchlib/test/features
src/vespa/searchlib/test/memoryindex
src/vespa/searchlib/transactionlog
src/vespa/searchlib/uca
diff --git a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
index b6953ec5dca..b93398e16a1 100644
--- a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
+++ b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
@@ -46,8 +46,8 @@ using search::queryeval::NearestNeighborBlueprint;
using search::tensor::DefaultNearestNeighborIndexFactory;
using search::tensor::DenseTensorAttribute;
using search::tensor::DirectTensorAttribute;
+using search::tensor::DistanceCalculator;
using search::tensor::DocVectorAccess;
-using search::tensor::SerializedFastValueAttribute;
using search::tensor::HnswIndex;
using search::tensor::HnswNode;
using search::tensor::NearestNeighborIndex;
@@ -55,13 +55,14 @@ using search::tensor::NearestNeighborIndexFactory;
using search::tensor::NearestNeighborIndexLoader;
using search::tensor::NearestNeighborIndexSaver;
using search::tensor::PrepareResult;
+using search::tensor::SerializedFastValueAttribute;
using search::tensor::TensorAttribute;
using vespalib::datastore::CompactionStrategy;
-using vespalib::eval::TensorSpec;
using vespalib::eval::CellType;
-using vespalib::eval::ValueType;
-using vespalib::eval::Value;
using vespalib::eval::SimpleValue;
+using vespalib::eval::TensorSpec;
+using vespalib::eval::Value;
+using vespalib::eval::ValueType;
using DoubleVector = std::vector<double>;
using generation_t = vespalib::GenerationHandler::generation_t;
@@ -1072,8 +1073,8 @@ public:
search::queryeval::FieldSpec field("foo", 0, 0);
auto bp = std::make_unique<NearestNeighborBlueprint>(
field,
- this->as_dense_tensor(),
- create_query_tensor(vec_2d(17, 42)),
+ std::make_unique<DistanceCalculator>(this->as_dense_tensor(),
+ create_query_tensor(vec_2d(17, 42))),
3, approximate, 5,
100100.25,
global_filter_lower_limit, 1.0);
diff --git a/searchlib/src/tests/features/nns_closeness/CMakeLists.txt b/searchlib/src/tests/features/nns_closeness/CMakeLists.txt
index 9a8c2d7c99f..d5f9ece096b 100644
--- a/searchlib/src/tests/features/nns_closeness/CMakeLists.txt
+++ b/searchlib/src/tests/features/nns_closeness/CMakeLists.txt
@@ -5,5 +5,6 @@ vespa_add_executable(searchlib_nns_closeness_test_app TEST
nns_closeness_test.cpp
DEPENDS
searchlib
+ searchlib_test
)
vespa_add_test(NAME searchlib_nns_closeness_test_app COMMAND searchlib_nns_closeness_test_app)
diff --git a/searchlib/src/tests/features/nns_closeness/nns_closeness_test.cpp b/searchlib/src/tests/features/nns_closeness/nns_closeness_test.cpp
index c7667b2cecd..661ee884e46 100644
--- a/searchlib/src/tests/features/nns_closeness/nns_closeness_test.cpp
+++ b/searchlib/src/tests/features/nns_closeness/nns_closeness_test.cpp
@@ -1,106 +1,24 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/vespalib/testkit/test_kit.h>
-#include <vespa/searchlib/features/setup.h>
-#include <vespa/searchlib/fef/test/indexenvironment.h>
-#include <vespa/searchlib/fef/test/indexenvironmentbuilder.h>
-#include <vespa/searchlib/fef/test/queryenvironment.h>
-#include <vespa/searchlib/fef/test/labels.h>
#include <vespa/searchlib/features/closenessfeature.h>
-#include <vespa/searchlib/fef/fef.h>
+#include <vespa/searchlib/features/setup.h>
#include <vespa/searchlib/fef/test/dummy_dependency_handler.h>
+#include <vespa/searchlib/fef/test/labels.h>
+#include <vespa/searchlib/test/features/distance_closeness_fixture.h>
#include <vespa/vespalib/stllike/asciistream.h>
+#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/util/stringfmt.h>
using search::feature_t;
-using namespace search::fef;
-using namespace search::fef::test;
+using namespace search::features::test;
using namespace search::features;
-using CollectionType = FieldInfo::CollectionType;
-using DataType = FieldInfo::DataType;
+using namespace search::fef::test;
+using namespace search::fef;
const vespalib::string labelFeatureName("closeness(label,nns)");
const vespalib::string fieldFeatureName("closeness(bar)");
-struct BlueprintFactoryFixture {
- BlueprintFactory factory;
- BlueprintFactoryFixture() : factory()
- {
- setup_search_features(factory);
- }
-};
-
-struct IndexFixture {
- IndexEnvironment indexEnv;
- IndexFixture() : indexEnv()
- {
- IndexEnvironmentBuilder builder(indexEnv);
- builder.addField(FieldType::ATTRIBUTE, CollectionType::SINGLE, DataType::INT64, "foo");
- builder.addField(FieldType::ATTRIBUTE, CollectionType::SINGLE, DataType::TENSOR, "bar");
- }
-};
-
-struct FeatureDumpFixture : public IDumpFeatureVisitor {
- virtual void visitDumpFeature(const vespalib::string &) override {
- TEST_ERROR("no features should be dumped");
- }
- FeatureDumpFixture() : IDumpFeatureVisitor() {}
- ~FeatureDumpFixture() override;
-};
-
-FeatureDumpFixture::~FeatureDumpFixture() = default;
-
-struct RankFixture : BlueprintFactoryFixture, IndexFixture {
- QueryEnvironment queryEnv;
- RankSetup rankSetup;
- MatchDataLayout mdl;
- MatchData::UP match_data;
- RankProgram::UP rankProgram;
- std::vector<TermFieldHandle> fooHandles;
- std::vector<TermFieldHandle> barHandles;
- RankFixture(size_t fooCnt, size_t barCnt, const Labels &labels, const vespalib::string &featureName)
- : queryEnv(&indexEnv), rankSetup(factory, indexEnv),
- mdl(), match_data(), rankProgram(), fooHandles(), barHandles()
- {
- for (size_t i = 0; i < fooCnt; ++i) {
- uint32_t fieldId = indexEnv.getFieldByName("foo")->id();
- fooHandles.push_back(mdl.allocTermField(fieldId));
- SimpleTermData term;
- term.setUniqueId(i + 1);
- term.addField(fieldId).setHandle(fooHandles.back());
- queryEnv.getTerms().push_back(term);
- }
- for (size_t i = 0; i < barCnt; ++i) {
- uint32_t fieldId = indexEnv.getFieldByName("bar")->id();
- barHandles.push_back(mdl.allocTermField(fieldId));
- SimpleTermData term;
- term.setUniqueId(fooCnt + i + 1);
- term.addField(fieldId).setHandle(barHandles.back());
- queryEnv.getTerms().push_back(term);
- }
- labels.inject(queryEnv.getProperties());
- rankSetup.setFirstPhaseRank(featureName);
- rankSetup.setIgnoreDefaultRankFeatures(true);
- ASSERT_TRUE(rankSetup.compile());
- match_data = mdl.createMatchData();
- rankProgram = rankSetup.create_first_phase_program();
- rankProgram->setup(*match_data, queryEnv);
- }
- feature_t getScore(uint32_t docId) {
- return Utils::getScoreFeature(*rankProgram, docId);
- }
- void setScore(TermFieldHandle handle, uint32_t docId, feature_t score) {
- match_data->resolveTermField(handle)->setRawScore(docId, score);
- }
- void setFooScore(uint32_t i, uint32_t docId, feature_t distance) {
- ASSERT_LESS(i, fooHandles.size());
- setScore(fooHandles[i], docId, 1.0/(1.0+distance));
- }
- void setBarScore(uint32_t i, uint32_t docId, feature_t distance) {
- ASSERT_LESS(i, barHandles.size());
- setScore(barHandles[i], docId, 1.0/(1.0+distance));
- }
-};
+using RankFixture = DistanceClosenessFixture;
TEST_F("require that blueprint can be created from factory", BlueprintFactoryFixture) {
Blueprint::SP bp = f.factory.createBlueprint("closeness");
@@ -108,11 +26,11 @@ TEST_F("require that blueprint can be created from factory", BlueprintFactoryFix
EXPECT_TRUE(dynamic_cast<ClosenessBlueprint*>(bp.get()) != 0);
}
-TEST_FFF("require that no features are dumped", ClosenessBlueprint, IndexFixture, FeatureDumpFixture) {
+TEST_FFF("require that no features are dumped", ClosenessBlueprint, IndexEnvironmentFixture, FeatureDumpFixture) {
f1.visitDumpFeatures(f2.indexEnv, f3);
}
-TEST_FF("require that setup can be done on random label", ClosenessBlueprint, IndexFixture) {
+TEST_FF("require that setup can be done on random label", ClosenessBlueprint, IndexEnvironmentFixture) {
DummyDependencyHandler deps(f1);
f1.setName(vespalib::make_string("%s(label,random_label)", f1.getBaseName().c_str()));
EXPECT_TRUE(static_cast<Blueprint&>(f1).setup(f2.indexEnv, std::vector<vespalib::string>{"label", "random_label"}));
diff --git a/searchlib/src/tests/features/nns_distance/CMakeLists.txt b/searchlib/src/tests/features/nns_distance/CMakeLists.txt
index 8dbee37a194..bf4e533ea45 100644
--- a/searchlib/src/tests/features/nns_distance/CMakeLists.txt
+++ b/searchlib/src/tests/features/nns_distance/CMakeLists.txt
@@ -5,5 +5,6 @@ vespa_add_executable(searchlib_nns_distance_test_app TEST
nns_distance_test.cpp
DEPENDS
searchlib
+ searchlib_test
)
vespa_add_test(NAME searchlib_nns_distance_test_app COMMAND searchlib_nns_distance_test_app)
diff --git a/searchlib/src/tests/features/nns_distance/nns_distance_test.cpp b/searchlib/src/tests/features/nns_distance/nns_distance_test.cpp
index 1e81b5576c1..6b2669367ad 100644
--- a/searchlib/src/tests/features/nns_distance/nns_distance_test.cpp
+++ b/searchlib/src/tests/features/nns_distance/nns_distance_test.cpp
@@ -2,105 +2,23 @@
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/features/setup.h>
-#include <vespa/searchlib/fef/test/indexenvironment.h>
-#include <vespa/searchlib/fef/test/indexenvironmentbuilder.h>
-#include <vespa/searchlib/fef/test/queryenvironment.h>
#include <vespa/searchlib/fef/test/labels.h>
#include <vespa/searchlib/features/distancefeature.h>
-#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/fef/test/dummy_dependency_handler.h>
+#include <vespa/searchlib/test/features/distance_closeness_fixture.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/stringfmt.h>
using search::feature_t;
-using namespace search::fef;
-using namespace search::fef::test;
+using namespace search::features::test;
using namespace search::features;
-using CollectionType = FieldInfo::CollectionType;
-using DataType = FieldInfo::DataType;
+using namespace search::fef::test;
+using namespace search::fef;
-const vespalib::string labelFeatureName("distance(label,label)");
+const vespalib::string labelFeatureName("distance(label,nns)");
const vespalib::string fieldFeatureName("distance(bar)");
-struct BlueprintFactoryFixture {
- BlueprintFactory factory;
- BlueprintFactoryFixture() : factory()
- {
- setup_search_features(factory);
- }
-};
-
-struct IndexFixture {
- IndexEnvironment indexEnv;
- IndexFixture() : indexEnv()
- {
- IndexEnvironmentBuilder builder(indexEnv);
- builder.addField(FieldType::ATTRIBUTE, CollectionType::SINGLE, DataType::INT64, "foo");
- builder.addField(FieldType::ATTRIBUTE, CollectionType::SINGLE, DataType::TENSOR, "bar");
- }
-};
-
-struct FeatureDumpFixture : public IDumpFeatureVisitor {
- virtual void visitDumpFeature(const vespalib::string &) override {
- TEST_ERROR("no features should be dumped");
- }
- FeatureDumpFixture() : IDumpFeatureVisitor() {}
- ~FeatureDumpFixture() override;
-};
-
-FeatureDumpFixture::~FeatureDumpFixture() = default;
-
-struct RankFixture : BlueprintFactoryFixture, IndexFixture {
- QueryEnvironment queryEnv;
- RankSetup rankSetup;
- MatchDataLayout mdl;
- MatchData::UP match_data;
- RankProgram::UP rankProgram;
- std::vector<TermFieldHandle> fooHandles;
- std::vector<TermFieldHandle> barHandles;
- RankFixture(size_t fooCnt, size_t barCnt, const Labels &labels, const vespalib::string &featureName)
- : queryEnv(&indexEnv), rankSetup(factory, indexEnv),
- mdl(), match_data(), rankProgram(), fooHandles(), barHandles()
- {
- for (size_t i = 0; i < fooCnt; ++i) {
- uint32_t fieldId = indexEnv.getFieldByName("foo")->id();
- fooHandles.push_back(mdl.allocTermField(fieldId));
- SimpleTermData term;
- term.setUniqueId(i + 1);
- term.addField(fieldId).setHandle(fooHandles.back());
- queryEnv.getTerms().push_back(term);
- }
- for (size_t i = 0; i < barCnt; ++i) {
- uint32_t fieldId = indexEnv.getFieldByName("bar")->id();
- barHandles.push_back(mdl.allocTermField(fieldId));
- SimpleTermData term;
- term.setUniqueId(fooCnt + i + 1);
- term.addField(fieldId).setHandle(barHandles.back());
- queryEnv.getTerms().push_back(term);
- }
- labels.inject(queryEnv.getProperties());
- rankSetup.setFirstPhaseRank(featureName);
- rankSetup.setIgnoreDefaultRankFeatures(true);
- ASSERT_TRUE(rankSetup.compile());
- match_data = mdl.createMatchData();
- rankProgram = rankSetup.create_first_phase_program();
- rankProgram->setup(*match_data, queryEnv);
- }
- feature_t getScore(uint32_t docId) {
- return Utils::getScoreFeature(*rankProgram, docId);
- }
- void setScore(TermFieldHandle handle, uint32_t docId, feature_t score) {
- match_data->resolveTermField(handle)->setRawScore(docId, score);
- }
- void setFooScore(uint32_t i, uint32_t docId, feature_t distance) {
- ASSERT_LESS(i, fooHandles.size());
- setScore(fooHandles[i], docId, 1.0/(1.0+distance));
- }
- void setBarScore(uint32_t i, uint32_t docId, feature_t distance) {
- ASSERT_LESS(i, barHandles.size());
- setScore(barHandles[i], docId, 1.0/(1.0+distance));
- }
-};
+using RankFixture = DistanceClosenessFixture;
TEST_F("require that blueprint can be created from factory", BlueprintFactoryFixture) {
Blueprint::SP bp = f.factory.createBlueprint("distance");
@@ -108,17 +26,17 @@ TEST_F("require that blueprint can be created from factory", BlueprintFactoryFix
EXPECT_TRUE(dynamic_cast<DistanceBlueprint*>(bp.get()) != 0);
}
-TEST_FFF("require that no features are dumped", DistanceBlueprint, IndexFixture, FeatureDumpFixture) {
+TEST_FFF("require that no features are dumped", DistanceBlueprint, IndexEnvironmentFixture, FeatureDumpFixture) {
f1.visitDumpFeatures(f2.indexEnv, f3);
}
-TEST_FF("require that setup can be done on random label", DistanceBlueprint, IndexFixture) {
+TEST_FF("require that setup can be done on random label", DistanceBlueprint, IndexEnvironmentFixture) {
DummyDependencyHandler deps(f1);
f1.setName(vespalib::make_string("%s(label,random_label)", f1.getBaseName().c_str()));
EXPECT_TRUE(static_cast<Blueprint&>(f1).setup(f2.indexEnv, std::vector<vespalib::string>{"label", "random_label"}));
}
-TEST_FF("require that setup with unknown field fails", DistanceBlueprint, IndexFixture) {
+TEST_FF("require that setup with unknown field fails", DistanceBlueprint, IndexEnvironmentFixture) {
DummyDependencyHandler deps(f1);
f1.setName(vespalib::make_string("%s(field,random_fieldname)", f1.getBaseName().c_str()));
EXPECT_FALSE(static_cast<Blueprint&>(f1).setup(f2.indexEnv, std::vector<vespalib::string>{"field", "random_fieldname"}));
@@ -132,7 +50,7 @@ TEST_FF("require that unrelated label gives max-double distance", SingleLabel("u
EXPECT_EQUAL(std::numeric_limits<feature_t>::max(), f2.getScore(10));
}
-TEST_FF("require that labeled item raw score can be obtained", SingleLabel("label", 1), RankFixture(2, 2, f1, labelFeatureName)) {
+TEST_FF("require that labeled item raw score can be obtained", SingleLabel("nns", 1), RankFixture(2, 2, f1, labelFeatureName)) {
f2.setFooScore(0, 10, 5.0);
EXPECT_EQUAL(5.0, f2.getScore(10));
}
@@ -142,7 +60,7 @@ TEST_FF("require that field raw score can be obtained", NoLabel(), RankFixture(2
EXPECT_EQUAL(5.0, f2.getScore(10));
}
-TEST_FF("require that other raw scores are ignored", SingleLabel("label", 2), RankFixture(2, 2, f1, labelFeatureName)) {
+TEST_FF("require that other raw scores are ignored", SingleLabel("nns", 2), RankFixture(2, 2, f1, labelFeatureName)) {
f2.setFooScore(0, 10, 1.0);
f2.setFooScore(1, 10, 2.0);
f2.setBarScore(0, 10, 5.0);
@@ -158,7 +76,7 @@ TEST_FF("require that the correct raw score is used", NoLabel(), RankFixture(2,
EXPECT_EQUAL(7.0, f2.getScore(10));
}
-TEST_FF("require that stale data is ignored", SingleLabel("label", 2), RankFixture(2, 2, f1, labelFeatureName)) {
+TEST_FF("require that stale data is ignored", SingleLabel("nns", 2), RankFixture(2, 2, f1, labelFeatureName)) {
f2.setFooScore(0, 10, 1.0);
f2.setFooScore(1, 5, 2.0);
EXPECT_EQUAL(std::numeric_limits<feature_t>::max(), f2.getScore(10));
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
index d9db50ae816..7af2186ed1e 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
@@ -599,13 +599,6 @@ bool check_valid_diversity_attr(const IAttributeVector *attr) {
return (attr->hasEnum() || attr->isIntegerType() || attr->isFloatingPointType());
}
-bool
-is_compatible_for_nearest_neighbor(const vespalib::eval::ValueType& lhs,
- const vespalib::eval::ValueType& rhs)
-{
- return (lhs.dimensions() == rhs.dimensions());
-}
-
//-----------------------------------------------------------------------------
@@ -760,40 +753,24 @@ public:
setResult(std::make_unique<queryeval::EmptyBlueprint>(_field));
}
void visit(query::NearestNeighborTerm &n) override {
- const ITensorAttribute *tensor_attr = _attr.asTensorAttribute();
- if (tensor_attr == nullptr) {
- return fail_nearest_neighbor_term(n, "Attribute is not a tensor");
- }
- const auto & ta_type = tensor_attr->getTensorType();
- if ((! ta_type.is_dense()) || (ta_type.dimensions().size() != 1)) {
- return fail_nearest_neighbor_term(n, make_string("Attribute tensor type (%s) is not a dense tensor of order 1",
- ta_type.to_spec().c_str()));
- }
const auto* query_tensor = getRequestContext().get_query_tensor(n.get_query_tensor_name());
if (query_tensor == nullptr) {
return fail_nearest_neighbor_term(n, "Query tensor was not found in request context");
}
- const auto & qt_type = query_tensor->type();
- if (! qt_type.is_dense()) {
- return fail_nearest_neighbor_term(n, make_string("Query tensor is not a dense tensor (type=%s)",
- qt_type.to_spec().c_str()));
- }
- if (!is_compatible_for_nearest_neighbor(ta_type, qt_type)) {
- return fail_nearest_neighbor_term(n, make_string("Attribute tensor type (%s) and query tensor type (%s) are not compatible",
- ta_type.to_spec().c_str(), qt_type.to_spec().c_str()));
- }
- if (tensor_attr->supports_extract_cells_ref() == false) {
- return fail_nearest_neighbor_term(n, make_string("Attribute does not support access to tensor data (type=%s)",
- ta_type.to_spec().c_str()));
+ try {
+ auto calc = tensor::DistanceCalculator::make_with_validation(_attr, *query_tensor);
+ setResult(std::make_unique<queryeval::NearestNeighborBlueprint>(_field,
+ std::move(calc),
+ n.get_target_num_hits(),
+ n.get_allow_approximate(),
+ n.get_explore_additional_hits(),
+ n.get_distance_threshold(),
+ getRequestContext().get_attribute_blueprint_params().global_filter_lower_limit,
+ getRequestContext().get_attribute_blueprint_params().global_filter_upper_limit));
+ } catch (const vespalib::IllegalArgumentException& ex) {
+ return fail_nearest_neighbor_term(n, ex.getMessage());
+
}
- setResult(std::make_unique<queryeval::NearestNeighborBlueprint>(_field, *tensor_attr,
- *query_tensor,
- n.get_target_num_hits(),
- n.get_allow_approximate(),
- n.get_explore_additional_hits(),
- n.get_distance_threshold(),
- getRequestContext().get_attribute_blueprint_params().global_filter_lower_limit,
- getRequestContext().get_attribute_blueprint_params().global_filter_upper_limit));
}
void visit(query::FuzzyTerm &n) override { visitTerm(n); }
diff --git a/searchlib/src/vespa/searchlib/common/bitvector.h b/searchlib/src/vespa/searchlib/common/bitvector.h
index 912c4a47c39..d3671d61ccf 100644
--- a/searchlib/src/vespa/searchlib/common/bitvector.h
+++ b/searchlib/src/vespa/searchlib/common/bitvector.h
@@ -7,6 +7,7 @@
#include <vespa/vespalib/util/alloc.h>
#include <vespa/vespalib/util/atomic.h>
#include <vespa/fastos/types.h>
+#include <algorithm>
namespace vespalib {
class nbostream;
diff --git a/searchlib/src/vespa/searchlib/features/CMakeLists.txt b/searchlib/src/vespa/searchlib/features/CMakeLists.txt
index 88531a46cb1..8acf28f4a2f 100644
--- a/searchlib/src/vespa/searchlib/features/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/features/CMakeLists.txt
@@ -12,7 +12,7 @@ vespa_add_library(searchlib_features OBJECT
debug_wait.cpp
dense_tensor_attribute_executor.cpp
direct_tensor_attribute_executor.cpp
- great_circle_distance_feature.cpp
+ distance_calculator_bundle.cpp
distancefeature.cpp
distancetopathfeature.cpp
documenttestutils.cpp
@@ -29,6 +29,7 @@ vespa_add_library(searchlib_features OBJECT
foreachfeature.cpp
freshnessfeature.cpp
global_sequence_feature.cpp
+ great_circle_distance_feature.cpp
internal_max_reduce_prod_join_feature.cpp
item_raw_score_feature.cpp
jarowinklerdistancefeature.cpp
diff --git a/searchlib/src/vespa/searchlib/features/closenessfeature.cpp b/searchlib/src/vespa/searchlib/features/closenessfeature.cpp
index 04fc2a263be..e44c94dbb2d 100644
--- a/searchlib/src/vespa/searchlib/features/closenessfeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/closenessfeature.cpp
@@ -1,9 +1,11 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "closenessfeature.h"
+#include "distance_calculator_bundle.h"
#include "utils.h"
#include <vespa/searchcommon/common/schema.h>
#include <vespa/searchlib/fef/properties.h>
+#include <vespa/searchlib/tensor/distance_calculator.h>
#include <vespa/vespalib/util/stash.h>
#include <vespa/log/log.h>
@@ -16,8 +18,8 @@ namespace search::features {
/** Implements the executor for converting NNS rawscore to a closeness feature. */
class ConvertRawScoreToCloseness : public fef::FeatureExecutor {
private:
- std::vector<fef::TermFieldHandle> _handles;
- const fef::MatchData *_md;
+ DistanceCalculatorBundle _bundle;
+ const fef::MatchData *_md;
void handle_bind_match_data(const fef::MatchData &md) override {
_md = &md;
}
@@ -28,32 +30,15 @@ public:
};
ConvertRawScoreToCloseness::ConvertRawScoreToCloseness(const fef::IQueryEnvironment &env, uint32_t fieldId)
- : _handles(),
+ : _bundle(env, fieldId, "closeness"),
_md(nullptr)
{
- _handles.reserve(env.getNumTerms());
- for (uint32_t i = 0; i < env.getNumTerms(); ++i) {
- search::fef::TermFieldHandle handle = util::getTermFieldHandle(env, i, fieldId);
- if (handle != search::fef::IllegalHandle) {
- _handles.push_back(handle);
- }
- }
}
ConvertRawScoreToCloseness::ConvertRawScoreToCloseness(const fef::IQueryEnvironment &env, const vespalib::string &label)
- : _handles(),
+ : _bundle(env, label, "closeness"),
_md(nullptr)
{
- const ITermData *term = util::getTermByLabel(env, label);
- if (term != nullptr) {
- // expect numFields() == 1
- for (uint32_t i = 0; i < term->numFields(); ++i) {
- TermFieldHandle handle = term->field(i).getHandle();
- if (handle != IllegalHandle) {
- _handles.push_back(handle);
- }
- }
- }
}
void
@@ -61,11 +46,14 @@ ConvertRawScoreToCloseness::execute(uint32_t docId)
{
feature_t max_closeness = 0.0;
assert(_md);
- for (auto handle : _handles) {
- const TermFieldMatchData *tfmd = _md->resolveTermField(handle);
+ for (const auto& elem : _bundle.elements()) {
+ const TermFieldMatchData *tfmd = _md->resolveTermField(elem.handle);
if (tfmd->getDocId() == docId) {
feature_t converted = tfmd->getRawScore();
max_closeness = std::max(max_closeness, converted);
+ } else if (elem.calc) {
+ feature_t converted = elem.calc->calc_raw_score(docId);
+ max_closeness = std::max(max_closeness, converted);
}
}
outputs().set_number(0, max_closeness);
@@ -201,15 +189,26 @@ ClosenessBlueprint::createInstance() const
return std::make_unique<ClosenessBlueprint>();
}
-FeatureExecutor &
-ClosenessBlueprint::createExecutor(const IQueryEnvironment &env, vespalib::Stash &stash) const
+void
+ClosenessBlueprint::prepareSharedState(const fef::IQueryEnvironment& env, fef::IObjectStore& store) const
{
+ if (_use_nns_tensor) {
+ DistanceCalculatorBundle::prepare_shared_state(env, store, _attr_id, "closeness");
+ }
if (_use_item_label) {
- return stash.create<ConvertRawScoreToCloseness>(env, _arg_string);
+ DistanceCalculatorBundle::prepare_shared_state(env, store, _arg_string, "closeness");
}
+}
+
+FeatureExecutor &
+ClosenessBlueprint::createExecutor(const IQueryEnvironment &env, vespalib::Stash &stash) const
+{
if (_use_nns_tensor) {
return stash.create<ConvertRawScoreToCloseness>(env, _attr_id);
}
+ if (_use_item_label) {
+ return stash.create<ConvertRawScoreToCloseness>(env, _arg_string);
+ }
assert(_use_geo_pos);
return stash.create<ClosenessExecutor>(_maxDistance, _scaleDistance);
}
diff --git a/searchlib/src/vespa/searchlib/features/closenessfeature.h b/searchlib/src/vespa/searchlib/features/closenessfeature.h
index 799495eaff5..6e265e5dcb8 100644
--- a/searchlib/src/vespa/searchlib/features/closenessfeature.h
+++ b/searchlib/src/vespa/searchlib/features/closenessfeature.h
@@ -45,6 +45,7 @@ public:
return fef::ParameterDescriptions().desc().string().desc().string().string();
}
bool setup(const fef::IIndexEnvironment & env, const fef::ParameterList & params) override;
+ void prepareSharedState(const fef::IQueryEnvironment& env, fef::IObjectStore& store) const override;
fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
};
diff --git a/searchlib/src/vespa/searchlib/features/distance_calculator_bundle.cpp b/searchlib/src/vespa/searchlib/features/distance_calculator_bundle.cpp
new file mode 100644
index 00000000000..90386dffd51
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/features/distance_calculator_bundle.cpp
@@ -0,0 +1,172 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "distance_calculator_bundle.h"
+#include "utils.h"
+#include <vespa/searchlib/fef/iqueryenvironment.h>
+#include <vespa/searchlib/fef/query_value.h>
+#include <vespa/searchlib/tensor/distance_calculator.h>
+#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/vespalib/util/issue.h>
+
+using search::fef::ITermData;
+using search::fef::IllegalHandle;
+using search::fef::InvalidValueTypeException;
+using search::fef::QueryValue;
+using search::fef::TermFieldHandle;
+using search::tensor::DistanceCalculator;
+using vespalib::Issue;
+
+namespace search::features {
+
+namespace {
+
+void
+prepare_query_tensor(const fef::IQueryEnvironment& env,
+ fef::IObjectStore& store,
+ const vespalib::string& query_tensor_name,
+ const vespalib::string& feature_name)
+{
+ try {
+ auto qvalue = QueryValue::from_config(query_tensor_name, env.getIndexEnvironment());
+ qvalue.prepare_shared_state(env, store);
+ } catch (const InvalidValueTypeException& ex) {
+ Issue::report("%s feature: Query tensor '%s' has invalid type '%s'.",
+ feature_name.c_str(), query_tensor_name.c_str(), ex.type_str().c_str());
+ }
+}
+
+std::unique_ptr<DistanceCalculator>
+make_distance_calculator(const fef::IQueryEnvironment& env,
+ const search::attribute::IAttributeVector& attr,
+ const vespalib::string& query_tensor_name,
+ const vespalib::string& feature_name)
+{
+ try {
+ auto qvalue = QueryValue::from_config(query_tensor_name, env.getIndexEnvironment());
+ const auto* query_tensor = qvalue.lookup_value(env.getObjectStore());
+ if (query_tensor == nullptr) {
+ Issue::report("%s feature: Query tensor '%s' is not found in the object store.",
+ feature_name.c_str(), query_tensor_name.c_str());
+ return {};
+ }
+ return DistanceCalculator::make_with_validation(attr, *query_tensor);
+ } catch (const InvalidValueTypeException& ex) {
+ Issue::report("%s feature: Query tensor '%s' has invalid type '%s'.",
+ feature_name.c_str(), query_tensor_name.c_str(), ex.type_str().c_str());
+ } catch (const vespalib::IllegalArgumentException& ex) {
+ Issue::report("%s feature: Could not create distance calculator for attribute '%s' and query tensor '%s': %s.",
+ feature_name.c_str(), attr.getName().c_str(), query_tensor_name.c_str(), ex.getMessage().c_str());
+ }
+ return {};
+}
+
+const search::attribute::IAttributeVector*
+resolve_attribute_for_field(const fef::IQueryEnvironment& env,
+ uint32_t field_id,
+ const vespalib::string& feature_name)
+{
+ const auto* field = env.getIndexEnvironment().getField(field_id);
+ if (field != nullptr) {
+ const auto* attr = env.getAttributeContext().getAttribute(field->name());
+ if (attr == nullptr) {
+ Issue::report("%s feature: The attribute vector '%s' for field id '%u' doesn't exist.",
+ feature_name.c_str(), field->name().c_str(), field_id);
+ }
+ return attr;
+ }
+ return nullptr;
+}
+
+}
+
+DistanceCalculatorBundle::Element::Element(fef::TermFieldHandle handle_in)
+ : handle(handle_in),
+ calc()
+{
+}
+
+DistanceCalculatorBundle::Element::Element(fef::TermFieldHandle handle_in, std::unique_ptr<search::tensor::DistanceCalculator> calc_in)
+ : handle(handle_in),
+ calc(std::move(calc_in))
+{
+}
+
+DistanceCalculatorBundle::Element::~Element() = default;
+
+DistanceCalculatorBundle::DistanceCalculatorBundle(const fef::IQueryEnvironment& env,
+ uint32_t field_id,
+ const vespalib::string& feature_name)
+
+ : _elems()
+{
+ _elems.reserve(env.getNumTerms());
+ const auto* attr = resolve_attribute_for_field(env, field_id, feature_name);
+ for (uint32_t i = 0; i < env.getNumTerms(); ++i) {
+ search::fef::TermFieldHandle handle = util::getTermFieldHandle(env, i, field_id);
+ if (handle != search::fef::IllegalHandle) {
+ const auto* term = env.getTerm(i);
+ if (term->query_tensor_name().has_value() && (attr != nullptr)) {
+ _elems.emplace_back(handle, make_distance_calculator(env, *attr, term->query_tensor_name().value(), feature_name));
+ } else {
+ _elems.emplace_back(handle);
+ }
+ }
+ }
+}
+
+DistanceCalculatorBundle::DistanceCalculatorBundle(const fef::IQueryEnvironment& env,
+ const vespalib::string& label,
+ const vespalib::string& feature_name)
+ : _elems()
+{
+ const ITermData* term = util::getTermByLabel(env, label);
+ if (term != nullptr) {
+ // expect numFields() == 1
+ for (uint32_t i = 0; i < term->numFields(); ++i) {
+ const auto& term_field = term->field(i);
+ TermFieldHandle handle = term_field.getHandle();
+ if (handle != IllegalHandle) {
+ std::unique_ptr<DistanceCalculator> calc;
+ if (term->query_tensor_name().has_value()) {
+ const auto* attr = resolve_attribute_for_field(env, term_field.getFieldId(), feature_name);
+ if (attr != nullptr) {
+ calc = make_distance_calculator(env, *attr, term->query_tensor_name().value(), feature_name);
+ }
+ }
+ _elems.emplace_back(handle, std::move(calc));
+ }
+ }
+ }
+}
+
+void
+DistanceCalculatorBundle::prepare_shared_state(const fef::IQueryEnvironment& env,
+ fef::IObjectStore& store,
+ uint32_t field_id,
+ const vespalib::string& feature_name)
+{
+ for (uint32_t i = 0; i < env.getNumTerms(); ++i) {
+ search::fef::TermFieldHandle handle = util::getTermFieldHandle(env, i, field_id);
+ if (handle != search::fef::IllegalHandle) {
+ const auto* term = env.getTerm(i);
+ if (term->query_tensor_name().has_value()) {
+ prepare_query_tensor(env, store, term->query_tensor_name().value(), feature_name);
+ }
+ }
+ }
+}
+
+void
+DistanceCalculatorBundle::prepare_shared_state(const fef::IQueryEnvironment& env,
+ fef::IObjectStore& store,
+ const vespalib::string& label,
+ const vespalib::string& feature_name)
+{
+ const auto* term = util::getTermByLabel(env, label);
+ if ((term != nullptr) && term->query_tensor_name().has_value()) {
+ prepare_query_tensor(env, store, term->query_tensor_name().value(), feature_name);
+ }
+}
+
+}
+
diff --git a/searchlib/src/vespa/searchlib/features/distance_calculator_bundle.h b/searchlib/src/vespa/searchlib/features/distance_calculator_bundle.h
new file mode 100644
index 00000000000..dd3fc521d96
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/features/distance_calculator_bundle.h
@@ -0,0 +1,59 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/searchlib/fef/handle.h>
+#include <vespa/vespalib/stllike/string.h>
+#include <memory>
+#include <vector>
+
+namespace search::tensor { class DistanceCalculator; }
+namespace search::fef {
+class IObjectStore;
+class IQueryEnvironment;
+}
+
+namespace search::features {
+
+/**
+ * A bundle of term-field tuples (TermFieldHandle, DistanceCalculator) used by the closeness and distance rank features.
+ *
+ * For most document ids the raw score is available in the TermFieldMatchData retrieved using the TermFieldHandle,
+ * as it was calculated during matching. In the other cases the DistanceCalculator can be used to calculate the score on the fly.
+ */
+class DistanceCalculatorBundle {
+public:
+ struct Element {
+ fef::TermFieldHandle handle;
+ std::unique_ptr<search::tensor::DistanceCalculator> calc;
+ Element(Element&& rhs) noexcept = default; // Needed as std::vector::reserve() is used.
+ Element(fef::TermFieldHandle handle_in);
+ Element(fef::TermFieldHandle handle_in, std::unique_ptr<search::tensor::DistanceCalculator> calc_in);
+ ~Element();
+ };
+private:
+ std::vector<Element> _elems;
+
+public:
+ DistanceCalculatorBundle(const fef::IQueryEnvironment& env,
+ uint32_t field_id,
+ const vespalib::string& feature_name);
+
+ DistanceCalculatorBundle(const fef::IQueryEnvironment& env,
+ const vespalib::string& label,
+ const vespalib::string& feature_name);
+
+ const std::vector<Element>& elements() const { return _elems; }
+
+ static void prepare_shared_state(const fef::IQueryEnvironment& env,
+ fef::IObjectStore& store,
+ uint32_t field_id,
+ const vespalib::string& feature_name);
+
+ static void prepare_shared_state(const fef::IQueryEnvironment& env,
+ fef::IObjectStore& store,
+ const vespalib::string& label,
+ const vespalib::string& feature_name);
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/features/distancefeature.cpp b/searchlib/src/vespa/searchlib/features/distancefeature.cpp
index d0a2c1a3838..6add65054ac 100644
--- a/searchlib/src/vespa/searchlib/features/distancefeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/distancefeature.cpp
@@ -1,16 +1,18 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "distance_calculator_bundle.h"
#include "distancefeature.h"
+#include "utils.h"
+#include <vespa/document/datatype/positiondatatype.h>
#include <vespa/searchcommon/common/schema.h>
#include <vespa/searchlib/common/geo_location_spec.h>
#include <vespa/searchlib/fef/matchdata.h>
-#include <vespa/document/datatype/positiondatatype.h>
+#include <vespa/searchlib/tensor/distance_calculator.h>
#include <vespa/vespalib/geo/zcurve.h>
#include <vespa/vespalib/util/issue.h>
#include <vespa/vespalib/util/stash.h>
#include <cmath>
#include <limits>
-#include "utils.h"
#include <vespa/log/log.h>
LOG_SETUP(".features.distancefeature");
@@ -24,8 +26,8 @@ namespace search::features {
/** Implements the executor for converting NNS rawscore to a distance feature. */
class ConvertRawscoreToDistance : public fef::FeatureExecutor {
private:
- std::vector<fef::TermFieldHandle> _handles;
- const fef::MatchData *_md;
+ DistanceCalculatorBundle _bundle;
+ const fef::MatchData *_md;
void handle_bind_match_data(const fef::MatchData &md) override {
_md = &md;
}
@@ -36,32 +38,15 @@ public:
};
ConvertRawscoreToDistance::ConvertRawscoreToDistance(const fef::IQueryEnvironment &env, uint32_t fieldId)
- : _handles(),
+ : _bundle(env, fieldId, "distance"),
_md(nullptr)
{
- _handles.reserve(env.getNumTerms());
- for (uint32_t i = 0; i < env.getNumTerms(); ++i) {
- search::fef::TermFieldHandle handle = util::getTermFieldHandle(env, i, fieldId);
- if (handle != search::fef::IllegalHandle) {
- _handles.push_back(handle);
- }
- }
}
ConvertRawscoreToDistance::ConvertRawscoreToDistance(const fef::IQueryEnvironment &env, const vespalib::string &label)
- : _handles(),
+ : _bundle(env, label, "distance"),
_md(nullptr)
{
- const ITermData *term = util::getTermByLabel(env, label);
- if (term != nullptr) {
- // expect numFields() == 1
- for (uint32_t i = 0; i < term->numFields(); ++i) {
- TermFieldHandle handle = term->field(i).getHandle();
- if (handle != IllegalHandle) {
- _handles.push_back(handle);
- }
- }
- }
}
void
@@ -69,12 +54,16 @@ ConvertRawscoreToDistance::execute(uint32_t docId)
{
feature_t min_distance = std::numeric_limits<feature_t>::max();
assert(_md);
- for (auto handle : _handles) {
- const TermFieldMatchData *tfmd = _md->resolveTermField(handle);
+ for (const auto& elem : _bundle.elements()) {
+ const TermFieldMatchData *tfmd = _md->resolveTermField(elem.handle);
if (tfmd->getDocId() == docId) {
feature_t invdist = tfmd->getRawScore();
feature_t converted = (1.0 / invdist) - 1.0;
min_distance = std::min(min_distance, converted);
+ } else if (elem.calc) {
+ feature_t invdist = elem.calc->calc_raw_score(docId);
+ feature_t converted = (1.0 / invdist) - 1.0;
+ min_distance = std::min(min_distance, converted);
}
}
outputs().set_number(0, min_distance);
@@ -249,6 +238,17 @@ DistanceBlueprint::setup(const IIndexEnvironment & env,
return false;
}
+void
+DistanceBlueprint::prepareSharedState(const fef::IQueryEnvironment& env, fef::IObjectStore& store) const
+{
+ if (_use_nns_tensor) {
+ DistanceCalculatorBundle::prepare_shared_state(env, store, _attr_id, "distance");
+ }
+ if (_use_item_label) {
+ DistanceCalculatorBundle::prepare_shared_state(env, store, _arg_string, "distance");
+ }
+}
+
FeatureExecutor &
DistanceBlueprint::createExecutor(const IQueryEnvironment &env, vespalib::Stash &stash) const
{
diff --git a/searchlib/src/vespa/searchlib/features/distancefeature.h b/searchlib/src/vespa/searchlib/features/distancefeature.h
index 6eff0380c3a..bf578d45f42 100644
--- a/searchlib/src/vespa/searchlib/features/distancefeature.h
+++ b/searchlib/src/vespa/searchlib/features/distancefeature.h
@@ -63,6 +63,7 @@ public:
return fef::ParameterDescriptions().desc().string().desc().string().string();
}
bool setup(const fef::IIndexEnvironment & env, const fef::ParameterList & params) override;
+ void prepareSharedState(const fef::IQueryEnvironment& env, fef::IObjectStore& store) const override;
fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
};
diff --git a/searchlib/src/vespa/searchlib/fef/query_value.cpp b/searchlib/src/vespa/searchlib/fef/query_value.cpp
index d9cdb0aa23d..a60a24425b5 100644
--- a/searchlib/src/vespa/searchlib/fef/query_value.cpp
+++ b/searchlib/src/vespa/searchlib/fef/query_value.cpp
@@ -161,6 +161,8 @@ QueryValue::QueryValue(const vespalib::string& key, const vespalib::eval::ValueT
{
}
+QueryValue::~QueryValue() = default;
+
QueryValue
QueryValue::from_config(const vespalib::string& key, const IIndexEnvironment& env)
{
diff --git a/searchlib/src/vespa/searchlib/fef/query_value.h b/searchlib/src/vespa/searchlib/fef/query_value.h
index 477b6aa451f..3cdb90ea871 100644
--- a/searchlib/src/vespa/searchlib/fef/query_value.h
+++ b/searchlib/src/vespa/searchlib/fef/query_value.h
@@ -59,6 +59,7 @@ private:
public:
QueryValue();
QueryValue(const vespalib::string& key, const vespalib::eval::ValueType& type);
+ ~QueryValue();
/**
* Create a QueryValue using properties from the given index environment to extract the value type.
diff --git a/searchlib/src/vespa/searchlib/fef/test/labels.h b/searchlib/src/vespa/searchlib/fef/test/labels.h
index 5f27f786405..a69634003b4 100644
--- a/searchlib/src/vespa/searchlib/fef/test/labels.h
+++ b/searchlib/src/vespa/searchlib/fef/test/labels.h
@@ -1,5 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
#include <vespa/searchlib/fef/properties.h>
#include <vespa/vespalib/stllike/asciistream.h>
diff --git a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp
index a36a0006c76..6a891341afd 100644
--- a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp
@@ -34,8 +34,7 @@ to_string(NearestNeighborBlueprint::Algorithm algorithm)
} // namespace <unnamed>
NearestNeighborBlueprint::NearestNeighborBlueprint(const queryeval::FieldSpec& field,
- const tensor::ITensorAttribute& attr_tensor,
- const Value& query_tensor,
+ std::unique_ptr<search::tensor::DistanceCalculator> distance_calc,
uint32_t target_hits,
bool approximate,
uint32_t explore_additional_hits,
@@ -43,9 +42,9 @@ NearestNeighborBlueprint::NearestNeighborBlueprint(const queryeval::FieldSpec& f
double global_filter_lower_limit,
double global_filter_upper_limit)
: ComplexLeafBlueprint(field),
- _attr_tensor(attr_tensor),
- _distance_calc(_attr_tensor, query_tensor),
- _query_tensor(_distance_calc.query_tensor()),
+ _distance_calc(std::move(distance_calc)),
+ _attr_tensor(_distance_calc->attribute_tensor()),
+ _query_tensor(_distance_calc->query_tensor()),
_target_hits(target_hits),
_adjusted_target_hits(target_hits),
_approximate(approximate),
@@ -62,7 +61,7 @@ NearestNeighborBlueprint::NearestNeighborBlueprint(const queryeval::FieldSpec& f
_global_filter_hit_ratio()
{
if (distance_threshold < std::numeric_limits<double>::max()) {
- _distance_threshold = _distance_calc.function().convert_threshold(distance_threshold);
+ _distance_threshold = _distance_calc->function().convert_threshold(distance_threshold);
_distance_heap.set_distance_threshold(_distance_threshold);
}
uint32_t est_hits = _attr_tensor.get_num_docs();
@@ -127,11 +126,11 @@ NearestNeighborBlueprint::createLeafSearch(const search::fef::TermFieldMatchData
switch (_algorithm) {
case Algorithm::INDEX_TOP_K_WITH_FILTER:
case Algorithm::INDEX_TOP_K:
- return NnsIndexIterator::create(tfmd, _found_hits, _distance_calc.function());
+ return NnsIndexIterator::create(tfmd, _found_hits, _distance_calc->function());
default:
;
}
- return NearestNeighborIterator::create(strict, tfmd, _distance_calc,
+ return NearestNeighborIterator::create(strict, tfmd, *_distance_calc,
_distance_heap, _global_filter->filter());
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h
index 9948cce1407..3dd03291b97 100644
--- a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h
@@ -28,8 +28,8 @@ public:
INDEX_TOP_K_WITH_FILTER
};
private:
+ std::unique_ptr<search::tensor::DistanceCalculator> _distance_calc;
const tensor::ITensorAttribute& _attr_tensor;
- search::tensor::DistanceCalculator _distance_calc;
const vespalib::eval::Value& _query_tensor;
uint32_t _target_hits;
uint32_t _adjusted_target_hits;
@@ -49,8 +49,7 @@ private:
void perform_top_k(const search::tensor::NearestNeighborIndex* nns_index);
public:
NearestNeighborBlueprint(const queryeval::FieldSpec& field,
- const tensor::ITensorAttribute& attr_tensor,
- const vespalib::eval::Value& query_tensor,
+ std::unique_ptr<search::tensor::DistanceCalculator> distance_calc,
uint32_t target_hits, bool approximate, uint32_t explore_additional_hits,
double distance_threshold,
double global_filter_lower_limit,
diff --git a/searchlib/src/vespa/searchlib/tensor/distance_calculator.cpp b/searchlib/src/vespa/searchlib/tensor/distance_calculator.cpp
index adfa5b7ee4a..d6d5433ff15 100644
--- a/searchlib/src/vespa/searchlib/tensor/distance_calculator.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/distance_calculator.cpp
@@ -4,12 +4,17 @@
#include "distance_function_factory.h"
#include "nearest_neighbor_index.h"
#include <vespa/eval/eval/fast_value.h>
+#include <vespa/searchcommon/attribute/iattributevector.h>
+#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/vespalib/util/stringfmt.h>
+using vespalib::IllegalArgumentException;
using vespalib::eval::CellType;
using vespalib::eval::FastValueBuilderFactory;
using vespalib::eval::TypedCells;
using vespalib::eval::Value;
using vespalib::eval::ValueType;
+using vespalib::make_string;
namespace {
@@ -42,6 +47,13 @@ struct ConvertCellsSelector
}
};
+bool
+is_compatible(const vespalib::eval::ValueType& lhs,
+ const vespalib::eval::ValueType& rhs)
+{
+ return (lhs.dimensions() == rhs.dimensions());
+}
+
}
namespace search::tensor {
@@ -86,5 +98,40 @@ DistanceCalculator::DistanceCalculator(const tensor::ITensorAttribute& attr_tens
DistanceCalculator::~DistanceCalculator() = default;
+namespace {
+
+
+
+}
+
+std::unique_ptr<DistanceCalculator>
+DistanceCalculator::make_with_validation(const search::attribute::IAttributeVector& attr,
+ const vespalib::eval::Value& query_tensor_in)
+{
+ const ITensorAttribute* attr_tensor = attr.asTensorAttribute();
+ if (attr_tensor == nullptr) {
+ throw IllegalArgumentException("Attribute is not a tensor");
+ }
+ const auto& at_type = attr_tensor->getTensorType();
+ if ((!at_type.is_dense()) || (at_type.dimensions().size() != 1)) {
+ throw IllegalArgumentException(make_string("Attribute tensor type (%s) is not a dense tensor of order 1",
+ at_type.to_spec().c_str()));
+ }
+ const auto& qt_type = query_tensor_in.type();
+ if (!qt_type.is_dense()) {
+ throw IllegalArgumentException(make_string("Query tensor type (%s) is not a dense tensor",
+ qt_type.to_spec().c_str()));
+ }
+ if (!is_compatible(at_type, qt_type)) {
+ throw IllegalArgumentException(make_string("Attribute tensor type (%s) and query tensor type (%s) are not compatible",
+ at_type.to_spec().c_str(), qt_type.to_spec().c_str()));
+ }
+ if (!attr_tensor->supports_extract_cells_ref()) {
+ throw IllegalArgumentException(make_string("Attribute tensor does not support access to tensor data (type=%s)",
+ at_type.to_spec().c_str()));
+ }
+ return std::make_unique<DistanceCalculator>(*attr_tensor, query_tensor_in);
+}
+
}
diff --git a/searchlib/src/vespa/searchlib/tensor/distance_calculator.h b/searchlib/src/vespa/searchlib/tensor/distance_calculator.h
index f1cc7feb9df..3ef41906b92 100644
--- a/searchlib/src/vespa/searchlib/tensor/distance_calculator.h
+++ b/searchlib/src/vespa/searchlib/tensor/distance_calculator.h
@@ -6,6 +6,8 @@
namespace vespalib::eval { struct Value; }
+namespace search::attribute { class IAttributeVector; }
+
namespace search::tensor {
/**
@@ -40,9 +42,23 @@ public:
const vespalib::eval::Value& query_tensor() const { return *_query_tensor; }
const DistanceFunction& function() const { return *_dist_fun; }
+ double calc_raw_score(uint32_t docid) const {
+ double distance = _dist_fun->calc(_query_tensor_cells, _attr_tensor.extract_cells_ref(docid));
+ return _dist_fun->to_rawscore(distance);
+ }
+
double calc_with_limit(uint32_t docid, double limit) const {
return _dist_fun->calc_with_limit(_query_tensor_cells, _attr_tensor.extract_cells_ref(docid), limit);
}
+
+ /**
+ * Create a calculator for the given attribute tensor and query tensor, if possible.
+ *
+ * Throws vespalib::IllegalArgumentException if the inputs are not supported or incompatible.
+ */
+ static std::unique_ptr<DistanceCalculator> make_with_validation(const search::attribute::IAttributeVector& attr,
+ const vespalib::eval::Value& query_tensor_in);
+
};
}
diff --git a/searchlib/src/vespa/searchlib/test/CMakeLists.txt b/searchlib/src/vespa/searchlib/test/CMakeLists.txt
index 0d56fb8b4be..ed884a46217 100644
--- a/searchlib/src/vespa/searchlib/test/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/test/CMakeLists.txt
@@ -13,6 +13,7 @@ vespa_add_library(searchlib_test
$<TARGET_OBJECTS:searchlib_test_gtest_migration>
DEPENDS
searchlib
+ searchlib_searchlib_test_features
searchlib_searchlib_test_memoryindex
GTest::GTest
)
diff --git a/searchlib/src/vespa/searchlib/test/features/CMakeLists.txt b/searchlib/src/vespa/searchlib/test/features/CMakeLists.txt
new file mode 100644
index 00000000000..ba70fe57e88
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/test/features/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(searchlib_searchlib_test_features
+ SOURCES
+ distance_closeness_fixture.cpp
+ DEPENDS
+ searchlib
+)
diff --git a/searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.cpp b/searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.cpp
new file mode 100644
index 00000000000..76d40e14f48
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.cpp
@@ -0,0 +1,39 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "distance_closeness_fixture.h"
+
+namespace search::features::test {
+
+FeatureDumpFixture::~FeatureDumpFixture() = default;
+
+DistanceClosenessFixture::DistanceClosenessFixture(size_t fooCnt, size_t barCnt, const Labels &labels, const vespalib::string &featureName)
+ : queryEnv(&indexEnv), rankSetup(factory, indexEnv),
+ mdl(), match_data(), rankProgram(), fooHandles(), barHandles()
+{
+ for (size_t i = 0; i < fooCnt; ++i) {
+ uint32_t fieldId = indexEnv.getFieldByName("foo")->id();
+ fooHandles.push_back(mdl.allocTermField(fieldId));
+ SimpleTermData term;
+ term.setUniqueId(i + 1);
+ term.addField(fieldId).setHandle(fooHandles.back());
+ queryEnv.getTerms().push_back(term);
+ }
+ for (size_t i = 0; i < barCnt; ++i) {
+ uint32_t fieldId = indexEnv.getFieldByName("bar")->id();
+ barHandles.push_back(mdl.allocTermField(fieldId));
+ SimpleTermData term;
+ term.setUniqueId(fooCnt + i + 1);
+ term.addField(fieldId).setHandle(barHandles.back());
+ queryEnv.getTerms().push_back(term);
+ }
+ labels.inject(queryEnv.getProperties());
+ rankSetup.setFirstPhaseRank(featureName);
+ rankSetup.setIgnoreDefaultRankFeatures(true);
+ ASSERT_TRUE(rankSetup.compile());
+ match_data = mdl.createMatchData();
+ rankProgram = rankSetup.create_first_phase_program();
+ rankProgram->setup(*match_data, queryEnv);
+}
+
+}
+
diff --git a/searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.h b/searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.h
new file mode 100644
index 00000000000..cdb1379659e
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/test/features/distance_closeness_fixture.h
@@ -0,0 +1,75 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/searchlib/features/setup.h>
+#include <vespa/searchlib/fef/fef.h>
+#include <vespa/searchlib/fef/test/indexenvironment.h>
+#include <vespa/searchlib/fef/test/indexenvironmentbuilder.h>
+#include <vespa/searchlib/fef/test/labels.h>
+#include <vespa/searchlib/fef/test/queryenvironment.h>
+#include <vespa/vespalib/testkit/test_kit.h>
+
+using namespace search::fef;
+using namespace search::fef::test;
+
+using CollectionType = FieldInfo::CollectionType;
+using DataType = FieldInfo::DataType;
+
+namespace search::features::test {
+
+struct BlueprintFactoryFixture {
+ BlueprintFactory factory;
+ BlueprintFactoryFixture() : factory()
+ {
+ setup_search_features(factory);
+ }
+};
+
+struct IndexEnvironmentFixture {
+ IndexEnvironment indexEnv;
+ IndexEnvironmentFixture() : indexEnv()
+ {
+ IndexEnvironmentBuilder builder(indexEnv);
+ builder.addField(FieldType::ATTRIBUTE, CollectionType::SINGLE, DataType::INT64, "foo");
+ builder.addField(FieldType::ATTRIBUTE, CollectionType::SINGLE, DataType::TENSOR, "bar");
+ }
+};
+
+struct FeatureDumpFixture : public IDumpFeatureVisitor {
+ virtual void visitDumpFeature(const vespalib::string &) override {
+ TEST_ERROR("no features should be dumped");
+ }
+ FeatureDumpFixture() : IDumpFeatureVisitor() {}
+ ~FeatureDumpFixture() override;
+};
+
+/**
+ * Fixture used by unit tests for distance and closeness rank features.
+ */
+struct DistanceClosenessFixture : BlueprintFactoryFixture, IndexEnvironmentFixture {
+ QueryEnvironment queryEnv;
+ RankSetup rankSetup;
+ MatchDataLayout mdl;
+ MatchData::UP match_data;
+ RankProgram::UP rankProgram;
+ std::vector<TermFieldHandle> fooHandles;
+ std::vector<TermFieldHandle> barHandles;
+ DistanceClosenessFixture(size_t fooCnt, size_t barCnt, const Labels &labels, const vespalib::string &featureName);
+ feature_t getScore(uint32_t docId) {
+ return Utils::getScoreFeature(*rankProgram, docId);
+ }
+ void setScore(TermFieldHandle handle, uint32_t docId, feature_t score) {
+ match_data->resolveTermField(handle)->setRawScore(docId, score);
+ }
+ void setFooScore(uint32_t i, uint32_t docId, feature_t distance) {
+ ASSERT_LESS(i, fooHandles.size());
+ setScore(fooHandles[i], docId, 1.0/(1.0+distance));
+ }
+ void setBarScore(uint32_t i, uint32_t docId, feature_t distance) {
+ ASSERT_LESS(i, barHandles.size());
+ setScore(barHandles[i], docId, 1.0/(1.0+distance));
+ }
+};
+
+}
diff --git a/searchsummary/src/tests/docsumformat/docsum-pack.cpp b/searchsummary/src/tests/docsumformat/docsum-pack.cpp
index ed1ba23017c..07aceea83e0 100644
--- a/searchsummary/src/tests/docsumformat/docsum-pack.cpp
+++ b/searchsummary/src/tests/docsumformat/docsum-pack.cpp
@@ -113,7 +113,7 @@ MyApp::Equal(GeneralResult *a, GeneralResult *b)
void
MyApp::TestIntValue(uint32_t line, GeneralResult *gres, const char *field, uint32_t value)
{
- ResEntry *entry = (gres != nullptr) ? gres->GetEntry(field) : nullptr;
+ ResEntry *entry = (gres != nullptr) ? gres->GetPresentEntry(field) : nullptr;
bool rc = (entry != nullptr &&
entry->_type == RES_INT &&
@@ -125,7 +125,7 @@ MyApp::TestIntValue(uint32_t line, GeneralResult *gres, const char *field, uint3
void
MyApp::TestDoubleValue(uint32_t line, GeneralResult *gres, const char *field, double value)
{
- ResEntry *entry = (gres != nullptr) ? gres->GetEntry(field) : nullptr;
+ ResEntry *entry = (gres != nullptr) ? gres->GetPresentEntry(field) : nullptr;
bool rc = (entry != nullptr &&
entry->_type == RES_DOUBLE &&
@@ -137,7 +137,7 @@ MyApp::TestDoubleValue(uint32_t line, GeneralResult *gres, const char *field, do
void
MyApp::TestInt64Value(uint32_t line, GeneralResult *gres, const char *field, uint64_t value)
{
- ResEntry *entry = (gres != nullptr) ? gres->GetEntry(field) : nullptr;
+ ResEntry *entry = (gres != nullptr) ? gres->GetPresentEntry(field) : nullptr;
bool rc = (entry != nullptr &&
entry->_type == RES_INT64 &&
@@ -150,7 +150,7 @@ MyApp::TestInt64Value(uint32_t line, GeneralResult *gres, const char *field, uin
void
MyApp::TestStringValue(uint32_t line, GeneralResult *gres, const char *field, const char *value)
{
- ResEntry *entry = (gres != nullptr) ? gres->GetEntry(field) : nullptr;
+ ResEntry *entry = (gres != nullptr) ? gres->GetPresentEntry(field) : nullptr;
bool rc = (entry != nullptr &&
entry->_type == RES_STRING &&
@@ -168,7 +168,7 @@ MyApp::TestStringValue(uint32_t line, GeneralResult *gres, const char *field, co
void
MyApp::TestDataValue(uint32_t line, GeneralResult *gres, const char *field, const char *value)
{
- ResEntry *entry = (gres != nullptr) ? gres->GetEntry(field) : nullptr;
+ ResEntry *entry = (gres != nullptr) ? gres->GetPresentEntry(field) : nullptr;
bool rc = (entry != nullptr &&
entry->_type == RES_DATA &&
diff --git a/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp b/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp
index d5a908fc8bd..7265dd89be4 100644
--- a/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp
+++ b/searchsummary/src/tests/docsummary/attribute_combiner/attribute_combiner_test.cpp
@@ -5,7 +5,7 @@
#include <vespa/searchlib/common/matching_elements.h>
#include <vespa/searchlib/common/matching_elements_fields.h>
#include <vespa/searchlib/util/slime_output_raw_buf_adapter.h>
-#include <vespa/searchsummary/docsummary/docsumfieldwriter.h>
+#include <vespa/searchsummary/docsummary/docsum_field_writer.h>
#include <vespa/searchsummary/docsummary/docsumstate.h>
#include <vespa/searchsummary/docsummary/docsum_field_writer_state.h>
#include <vespa/searchsummary/docsummary/attribute_combiner_dfw.h>
@@ -24,7 +24,7 @@ using search::docsummary::AttributeCombinerDFW;
using search::docsummary::GetDocsumsState;
using search::docsummary::GetDocsumsStateCallback;
using search::docsummary::IDocsumEnvironment;
-using search::docsummary::IDocsumFieldWriter;
+using search::docsummary::DocsumFieldWriter;
using search::docsummary::test::MockAttributeManager;
using search::docsummary::test::MockStateCallback;
using search::docsummary::test::SlimeValue;
@@ -34,7 +34,7 @@ namespace {
struct AttributeCombinerTest : public ::testing::Test
{
MockAttributeManager attrs;
- std::unique_ptr<IDocsumFieldWriter> writer;
+ std::unique_ptr<DocsumFieldWriter> writer;
MockStateCallback callback;
GetDocsumsState state;
std::shared_ptr<search::MatchingElementsFields> _matching_elems_fields;
diff --git a/searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp b/searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp
index 67d505582d8..42443cf1058 100644
--- a/searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp
+++ b/searchsummary/src/tests/docsummary/attributedfw/attributedfw_test.cpp
@@ -16,7 +16,7 @@ using search::attribute::BasicType;
using search::attribute::CollectionType;
using search::docsummary::AttributeDFWFactory;
using search::docsummary::GetDocsumsState;
-using search::docsummary::IDocsumFieldWriter;
+using search::docsummary::DocsumFieldWriter;
using search::docsummary::test::MockAttributeManager;
using search::docsummary::test::MockStateCallback;
using search::docsummary::test::SlimeValue;
@@ -26,7 +26,7 @@ using ElementVector = std::vector<uint32_t>;
class AttributeDFWTest : public ::testing::Test {
protected:
MockAttributeManager _attrs;
- std::unique_ptr<IDocsumFieldWriter> _writer;
+ std::unique_ptr<DocsumFieldWriter> _writer;
MockStateCallback _callback;
GetDocsumsState _state;
std::shared_ptr<search::MatchingElementsFields> _matching_elems_fields;
diff --git a/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp b/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp
index 675af283ee8..82aa9ceba92 100644
--- a/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp
+++ b/searchsummary/src/tests/docsummary/matched_elements_filter/matched_elements_filter_test.cpp
@@ -16,8 +16,10 @@
#include <vespa/searchlib/common/matching_elements_fields.h>
#include <vespa/searchlib/util/slime_output_raw_buf_adapter.h>
#include <vespa/searchsummary/docsummary/docsum_store_document.h>
+#include <vespa/searchsummary/docsummary/docsumstorevalue.h>
#include <vespa/searchsummary/docsummary/docsumstate.h>
#include <vespa/searchsummary/docsummary/idocsumenvironment.h>
+#include <vespa/searchsummary/docsummary/general_result.h>
#include <vespa/searchsummary/docsummary/matched_elements_filter_dfw.h>
#include <vespa/searchsummary/docsummary/resultconfig.h>
#include <vespa/searchsummary/docsummary/resultpacker.h>
@@ -215,7 +217,7 @@ public:
{
}
~MatchedElementsFilterTest();
- std::unique_ptr<IDocsumFieldWriter> make_field_writer(const std::string& input_field_name) {
+ std::unique_ptr<DocsumFieldWriter> make_field_writer(const std::string& input_field_name) {
int input_field_enum = _doc_store.get_config().GetFieldNameEnum().Lookup(input_field_name.c_str());
return MatchedElementsFilterDFW::create(input_field_name, input_field_enum,
_attr_ctx, _fields);
diff --git a/searchsummary/src/tests/docsummary/positionsdfw_test.cpp b/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
index 11f0d0eb6d6..60584b26e31 100644
--- a/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
+++ b/searchsummary/src/tests/docsummary/positionsdfw_test.cpp
@@ -4,7 +4,7 @@
#include <vespa/searchlib/attribute/extendableattributes.h>
#include <vespa/searchlib/attribute/iattributemanager.h>
#include <vespa/searchlib/common/matching_elements.h>
-#include <vespa/searchsummary/docsummary/docsumfieldwriter.h>
+#include <vespa/searchsummary/docsummary/docsum_field_writer.h>
#include <vespa/searchsummary/docsummary/positionsdfw.h>
#include <vespa/searchsummary/docsummary/idocsumenvironment.h>
#include <vespa/searchsummary/docsummary/docsumstate.h>
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/CMakeLists.txt b/searchsummary/src/vespa/searchsummary/docsummary/CMakeLists.txt
index 947fe9deeab..e3272fb36de 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/CMakeLists.txt
+++ b/searchsummary/src/vespa/searchsummary/docsummary/CMakeLists.txt
@@ -6,13 +6,15 @@ vespa_add_library(searchsummary_docsummary OBJECT
attribute_field_writer.cpp
attributedfw.cpp
check_undefined_value_visitor.cpp
+ copy_dfw.cpp
docsumconfig.cpp
- docsumfieldwriter.cpp
- docsumstate.cpp
+ docsum_field_writer.cpp
docsum_store_document.cpp
+ docsumstate.cpp
docsumstorevalue.cpp
docsumwriter.cpp
dynamicteaserdfw.cpp
+ empty_dfw.cpp
general_result.cpp
geoposdfw.cpp
getdocsumargs.cpp
@@ -22,10 +24,12 @@ vespa_add_library(searchsummary_docsummary OBJECT
matched_elements_filter_dfw.cpp
positionsdfw.cpp
rankfeaturesdfw.cpp
+ res_type_utils.cpp
resultclass.cpp
resultconfig.cpp
resultpacker.cpp
searchdatatype.cpp
+ simple_dfw.cpp
struct_fields_resolver.cpp
struct_map_attribute_combiner_dfw.cpp
summaryfeaturesdfw.cpp
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.cpp
index ff5c2c5e05b..f308795a1bc 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.cpp
@@ -9,6 +9,7 @@
#include <vespa/searchlib/common/matching_elements.h>
#include <vespa/searchlib/common/matching_elements_fields.h>
#include <vespa/vespalib/data/slime/cursor.h>
+#include <vespa/vespalib/data/slime/inserter.h>
#include <vespa/vespalib/util/stash.h>
#include <algorithm>
#include <cassert>
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.h b/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.h
index 18b4fd34e66..e5bed876b63 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/array_attribute_combiner_dfw.h
@@ -3,6 +3,7 @@
#pragma once
#include "attribute_combiner_dfw.h"
+#include <vector>
namespace search::attribute { class IAttributeContext; }
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.cpp
index 79c11b20479..bf5578f38d6 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.cpp
@@ -18,7 +18,7 @@ namespace search::docsummary {
AttributeCombinerDFW::AttributeCombinerDFW(const vespalib::string &fieldName, bool filter_elements,
std::shared_ptr<MatchingElementsFields> matching_elems_fields)
- : ISimpleDFW(),
+ : SimpleDFW(),
_stateIndex(0),
_filter_elements(filter_elements),
_fieldName(fieldName),
@@ -41,13 +41,13 @@ AttributeCombinerDFW::setFieldWriterStateIndex(uint32_t fieldWriterStateIndex)
return true;
}
-std::unique_ptr<IDocsumFieldWriter>
+std::unique_ptr<DocsumFieldWriter>
AttributeCombinerDFW::create(const vespalib::string &fieldName, IAttributeContext &attrCtx, bool filter_elements,
std::shared_ptr<MatchingElementsFields> matching_elems_fields)
{
StructFieldsResolver structFields(fieldName, attrCtx, true);
if (structFields.has_error()) {
- return std::unique_ptr<IDocsumFieldWriter>();
+ return std::unique_ptr<DocsumFieldWriter>();
} else if (structFields.is_map_of_struct()) {
return std::make_unique<StructMapAttributeCombinerDFW>(fieldName, structFields, filter_elements, std::move(matching_elems_fields));
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.h b/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.h
index c1742595745..39f2d498c5b 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/attribute_combiner_dfw.h
@@ -2,7 +2,8 @@
#pragma once
-#include "docsumfieldwriter.h"
+#include "simple_dfw.h"
+#include <memory>
namespace search {
class MatchingElements;
@@ -21,7 +22,7 @@ class DynamicDocsumWriter;
* This class reads values from multiple struct field attributes and
* inserts them as an array of struct or a map of struct.
*/
-class AttributeCombinerDFW : public ISimpleDFW
+class AttributeCombinerDFW : public SimpleDFW
{
protected:
uint32_t _stateIndex;
@@ -36,8 +37,8 @@ public:
~AttributeCombinerDFW() override;
bool IsGenerated() const override;
bool setFieldWriterStateIndex(uint32_t fieldWriterStateIndex) override;
- static std::unique_ptr<IDocsumFieldWriter> create(const vespalib::string &fieldName, search::attribute::IAttributeContext &attrCtx,
- bool filter_elements, std::shared_ptr<MatchingElementsFields> matching_elems_fields);
+ static std::unique_ptr<DocsumFieldWriter> create(const vespalib::string &fieldName, search::attribute::IAttributeContext &attrCtx,
+ bool filter_elements, std::shared_ptr<MatchingElementsFields> matching_elems_fields);
void insertField(uint32_t docid, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target) override;
};
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp
index d5fdee096b1..e7b6acec646 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.cpp
@@ -2,6 +2,7 @@
#include "attributedfw.h"
#include "docsumwriter.h"
+#include "docsumstate.h"
#include "docsum_field_writer_state.h"
#include <vespa/eval/eval/value.h>
#include <vespa/eval/eval/value_codec.h>
@@ -38,6 +39,24 @@ AttrDFW::AttrDFW(const vespalib::string & attrName) :
{
}
+const attribute::IAttributeVector&
+AttrDFW::get_attribute(const GetDocsumsState& s) const
+{
+ return *s.getAttribute(getIndex());
+}
+
+const vespalib::string &
+AttrDFW::getAttributeName() const
+{
+ return _attrName;
+}
+
+bool
+AttrDFW::IsGenerated() const
+{
+ return true;
+}
+
namespace {
class SingleAttrDFW : public AttrDFW
@@ -333,7 +352,7 @@ MultiAttrDFW::insertField(uint32_t docid, GetDocsumsState *state, ResType, vespa
field_writer_state->insertField(docid, target);
}
-std::unique_ptr<IDocsumFieldWriter>
+std::unique_ptr<DocsumFieldWriter>
create_multi_writer(const IAttributeVector& attr, bool filter_elements, std::shared_ptr<MatchingElementsFields> matching_elems_fields)
{
auto type = attr.getBasicType();
@@ -355,7 +374,7 @@ create_multi_writer(const IAttributeVector& attr, bool filter_elements, std::sha
}
-std::unique_ptr<IDocsumFieldWriter>
+std::unique_ptr<DocsumFieldWriter>
AttributeDFWFactory::create(IAttributeManager& attr_mgr,
const vespalib::string& attr_name,
bool filter_elements,
@@ -365,7 +384,7 @@ AttributeDFWFactory::create(IAttributeManager& attr_mgr,
const auto* attr = ctx->getAttribute(attr_name);
if (attr == nullptr) {
Issue::report("No valid attribute vector found: '%s'", attr_name.c_str());
- return std::unique_ptr<IDocsumFieldWriter>();
+ return std::unique_ptr<DocsumFieldWriter>();
}
if (attr->hasMultiValue()) {
return create_multi_writer(*attr, filter_elements, std::move(matching_elems_fields));
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h
index 35f67fd5446..26351bdf501 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/attributedfw.h
@@ -2,38 +2,38 @@
#pragma once
-#include "docsumfieldwriter.h"
-#include "docsumstate.h"
+#include "simple_dfw.h"
+#include <memory>
-namespace search { class MatchingElementsFields; }
+namespace search {
+class IAttributeManager;
+class MatchingElementsFields;
+}
namespace search::attribute { class IAttributeVector; }
namespace search::docsummary {
/**
- * Factory to create an IDocsumFieldWriter to write an attribute vector to slime.
+ * Factory to create an DocsumFieldWriter to write an attribute vector to slime.
*/
class AttributeDFWFactory {
public:
- static std::unique_ptr<IDocsumFieldWriter> create(IAttributeManager& attr_mgr,
- const vespalib::string& attr_name,
- bool filter_elements = false,
- std::shared_ptr<MatchingElementsFields> matching_elems_fields
- = std::shared_ptr<MatchingElementsFields>());
+ static std::unique_ptr<DocsumFieldWriter> create(IAttributeManager& attr_mgr,
+ const vespalib::string& attr_name,
+ bool filter_elements = false,
+ std::shared_ptr<MatchingElementsFields> matching_elems_fields = std::shared_ptr<MatchingElementsFields>());
};
-class AttrDFW : public ISimpleDFW
+class AttrDFW : public SimpleDFW
{
private:
vespalib::string _attrName;
protected:
- const attribute::IAttributeVector& get_attribute(const GetDocsumsState& s) const {
- return *s.getAttribute(getIndex());
- }
- const vespalib::string & getAttributeName() const override { return _attrName; }
+ const attribute::IAttributeVector& get_attribute(const GetDocsumsState& s) const;
+ const vespalib::string & getAttributeName() const override;
public:
AttrDFW(const vespalib::string & attrName);
- bool IsGenerated() const override { return true; }
+ bool IsGenerated() const override;
};
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.cpp b/searchsummary/src/vespa/searchsummary/docsummary/copy_dfw.cpp
index e70b094aa64..836273ce3d8 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/copy_dfw.cpp
@@ -1,51 +1,16 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "docsumfieldwriter.h"
-#include "check_undefined_value_visitor.h"
-#include "idocsumenvironment.h"
-#include "docsumstate.h"
-#include "summaryfieldconverter.h"
-#include <vespa/searchlib/common/documentlocations.h>
-#include <vespa/searchlib/common/location.h>
-#include <vespa/searchlib/parsequery/stackdumpiterator.h>
+#include "copy_dfw.h"
+#include "general_result.h"
+#include "i_docsum_store_document.h"
+#include "resultconfig.h"
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/log/log.h>
-LOG_SETUP(".searchlib.docsummary.docsumfieldwriter");
+LOG_SETUP(".searchlib.docsummary.copy_dfw");
namespace search::docsummary {
-using search::attribute::IAttributeContext;
-using search::attribute::IAttributeVector;
-using search::attribute::BasicType;
-using search::common::Location;
-
-//--------------------------------------------------------------------------
-
-const vespalib::string IDocsumFieldWriter::_empty("");
-
-bool
-IDocsumFieldWriter::setFieldWriterStateIndex(uint32_t)
-{
- return false; // Don't need any field writer state by default
-}
-
-//--------------------------------------------------------------------------
-
-EmptyDFW::EmptyDFW() = default;
-
-EmptyDFW::~EmptyDFW() = default;
-
-void
-EmptyDFW::insertField(uint32_t, GetDocsumsState *, ResType, vespalib::slime::Inserter &target)
-{
- // insert explicitly-empty field?
- // target.insertNix();
- (void)target;
-}
-
-//--------------------------------------------------------------------------
-
CopyDFW::CopyDFW()
: _inputFieldEnumValue(static_cast<uint32_t>(-1)),
_input_field_name()
@@ -64,10 +29,10 @@ CopyDFW::Init(const ResultConfig & config, const char *inputField)
LOG(warning, "no docsum format contains field '%s'; copied fields will be empty", inputField);
}
- for (const auto & field : config) {
- const ResConfigEntry *entry = field.GetEntry(field.GetIndexFromEnumValue(_inputFieldEnumValue));
+ for (const auto & result_class : config) {
+ const ResConfigEntry *entry = result_class.GetEntry(result_class.GetIndexFromEnumValue(_inputFieldEnumValue));
- if (entry != nullptr &&
+ if (entry != nullptr && !entry->_not_present &&
!IsRuntimeCompatible(entry->_type, RES_INT) &&
!IsRuntimeCompatible(entry->_type, RES_DOUBLE) &&
!IsRuntimeCompatible(entry->_type, RES_INT64) &&
@@ -75,7 +40,7 @@ CopyDFW::Init(const ResultConfig & config, const char *inputField)
!IsRuntimeCompatible(entry->_type, RES_DATA)) {
LOG(warning, "cannot use docsum field '%s' as input to copy; type conflict with result class %d (%s)",
- inputField, field.GetClassID(), field.GetClassName());
+ inputField, result_class.GetClassID(), result_class.GetClassName());
return false;
}
}
@@ -87,19 +52,14 @@ CopyDFW::insertField(uint32_t /*docid*/, GeneralResult *gres, GetDocsumsState *,
vespalib::slime::Inserter &target)
{
int idx = gres->GetClass()->GetIndexFromEnumValue(_inputFieldEnumValue);
- ResEntry *entry = gres->GetEntry(idx);
+ ResEntry *entry = gres->GetPresentEntry(idx);
if (entry == nullptr) {
- auto input_field_value = gres->get_field_value(_input_field_name);
- if (input_field_value) {
- CheckUndefinedValueVisitor check_undefined;
- input_field_value->accept(check_undefined);
- if (!check_undefined.is_undefined()) {
- SummaryFieldConverter::insert_summary_field(false, *input_field_value, target);
- }
+ const auto* document = gres->get_document();
+ if (document != nullptr) {
+ document->insert_summary_field(_input_field_name, target);
}
- } else if (IsRuntimeCompatible(entry->_type, type))
- {
+ } else if (IsRuntimeCompatible(entry->_type, type)) {
switch (type) {
case RES_INT: {
uint32_t val32 = entry->_intval;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/copy_dfw.h b/searchsummary/src/vespa/searchsummary/docsummary/copy_dfw.h
new file mode 100644
index 00000000000..dab7417f60a
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/copy_dfw.h
@@ -0,0 +1,31 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "docsum_field_writer.h"
+
+namespace search::docsummary {
+
+class ResultConfig;
+
+/*
+ * Class for writing document summaries with content from another field.
+ */
+class CopyDFW : public DocsumFieldWriter
+{
+private:
+ uint32_t _inputFieldEnumValue;
+ vespalib::string _input_field_name;
+
+public:
+ CopyDFW();
+ ~CopyDFW() override;
+
+ bool Init(const ResultConfig & config, const char *inputField);
+
+ bool IsGenerated() const override { return false; }
+ void insertField(uint32_t docid, GeneralResult *gres, GetDocsumsState *state, ResType type,
+ vespalib::slime::Inserter &target) override;
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsum_blob_entry_filter.h b/searchsummary/src/vespa/searchsummary/docsummary/docsum_blob_entry_filter.h
new file mode 100644
index 00000000000..1d006386d35
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsum_blob_entry_filter.h
@@ -0,0 +1,29 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "res_type.h"
+#include <bitset>
+
+namespace search::docsummary {
+
+/*
+ * Class containing the set of result types not stored in docsum blobs.
+ * This is used for gradual migration towards elimination of docsum blobs.
+ */
+class DocsumBlobEntryFilter {
+ std::bitset<14> _skip_types;
+
+public:
+ DocsumBlobEntryFilter()
+ : _skip_types()
+ {
+ }
+ bool skip(ResType type) const noexcept { return _skip_types.test(type); }
+ DocsumBlobEntryFilter &add_skip(ResType type) {
+ _skip_types.set(type);
+ return *this;
+ }
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.cpp b/searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.cpp
new file mode 100644
index 00000000000..c698f0603c6
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.cpp
@@ -0,0 +1,27 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "docsum_field_writer.h"
+
+namespace search::docsummary {
+
+const vespalib::string DocsumFieldWriter::_empty("");
+
+const vespalib::string&
+DocsumFieldWriter::getAttributeName() const
+{
+ return _empty;
+}
+
+bool
+DocsumFieldWriter::isDefaultValue(uint32_t, const GetDocsumsState*) const
+{
+ return false;
+}
+
+bool
+DocsumFieldWriter::setFieldWriterStateIndex(uint32_t)
+{
+ return false; // Don't need any field writer state by default
+}
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.h b/searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.h
new file mode 100644
index 00000000000..764f3507380
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsum_field_writer.h
@@ -0,0 +1,41 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "res_type_utils.h"
+#include <vespa/vespalib/stllike/string.h>
+
+namespace vespalib::slime { struct Inserter; }
+
+namespace search::docsummary {
+
+class GeneralResult;
+class GetDocsumsState;
+
+/*
+ * Abstract class for writing document summaries.
+ */
+class DocsumFieldWriter
+{
+public:
+ DocsumFieldWriter()
+ : _index(0)
+ {
+ }
+ virtual ~DocsumFieldWriter() = default;
+ static bool IsRuntimeCompatible(ResType a, ResType b) {
+ return ResTypeUtils::IsRuntimeCompatible(a, b);
+ }
+ virtual bool IsGenerated() const = 0;
+ virtual void insertField(uint32_t docid, GeneralResult *gres, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target) = 0;
+ virtual const vespalib::string & getAttributeName() const;
+ virtual bool isDefaultValue(uint32_t docid, const GetDocsumsState * state) const;
+ void setIndex(size_t v) { _index = v; }
+ size_t getIndex() const { return _index; }
+ virtual bool setFieldWriterStateIndex(uint32_t fieldWriterStateIndex);
+private:
+ size_t _index;
+ static const vespalib::string _empty;
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.cpp b/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.cpp
index c0b894ddc0a..e525989e972 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.cpp
@@ -1,6 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "docsum_store_document.h"
+#include "check_undefined_value_visitor.h"
+#include "summaryfieldconverter.h"
#include <vespa/document/datatype/datatype.h>
#include <vespa/document/fieldvalue/document.h>
@@ -28,4 +30,13 @@ DocsumStoreDocument::get_field_value(const vespalib::string& field_name) const
return {};
}
+void
+DocsumStoreDocument::insert_summary_field(const vespalib::string& field_name, vespalib::slime::Inserter& inserter) const
+{
+ auto field_value = get_field_value(field_name);
+ if (field_value) {
+ SummaryFieldConverter::insert_summary_field(*field_value, inserter);
+ }
+}
+
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.h b/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.h
index 4508132e7e0..66a5a74fa8d 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsum_store_document.h
@@ -18,6 +18,7 @@ public:
DocsumStoreDocument(std::unique_ptr<document::Document> document);
~DocsumStoreDocument() override;
std::unique_ptr<document::FieldValue> get_field_value(const vespalib::string& field_name) const override;
+ void insert_summary_field(const vespalib::string& field_name, vespalib::slime::Inserter& inserter) const override;
};
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp b/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp
index 24642c418fd..376d4f90204 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.cpp
@@ -1,8 +1,10 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "attribute_combiner_dfw.h"
#include "docsumconfig.h"
+#include "attribute_combiner_dfw.h"
+#include "copy_dfw.h"
#include "docsumwriter.h"
+#include "empty_dfw.h"
#include "geoposdfw.h"
#include "idocsumenvironment.h"
#include "juniperdfw.h"
@@ -10,6 +12,7 @@
#include "positionsdfw.h"
#include "rankfeaturesdfw.h"
#include "textextractordfw.h"
+#include "summaryfeaturesdfw.h"
#include <vespa/searchlib/common/matching_elements_fields.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/exceptions.h>
@@ -24,12 +27,12 @@ DynamicDocsumConfig::getResultConfig() const {
return *_writer->GetResultConfig();
}
-IDocsumFieldWriter::UP
+std::unique_ptr<DocsumFieldWriter>
DynamicDocsumConfig::createFieldWriter(const string & fieldName, const string & overrideName, const string & argument, bool & rc, std::shared_ptr<MatchingElementsFields> matching_elems_fields)
{
const ResultConfig & resultConfig = getResultConfig();
rc = false;
- IDocsumFieldWriter::UP fieldWriter;
+ std::unique_ptr<DocsumFieldWriter> fieldWriter;
if (overrideName == "dynamicteaser") {
if ( ! argument.empty() ) {
const char *langFieldName = "something unused";
@@ -127,7 +130,7 @@ DynamicDocsumConfig::configure(const vespa::config::search::SummarymapConfig &cf
for (size_t i = 0; i < cfg.override.size(); ++i) {
const vespa::config::search::SummarymapConfig::Override & o = cfg.override[i];
bool rc(false);
- IDocsumFieldWriter::UP fieldWriter = createFieldWriter(o.field, o.command, o.arguments, rc, matching_elems_fields);
+ std::unique_ptr<DocsumFieldWriter> fieldWriter = createFieldWriter(o.field, o.command, o.arguments, rc, matching_elems_fields);
if (rc && fieldWriter) {
rc = _writer->Override(o.field.c_str(), fieldWriter.release()); // OBJECT HAND-OVER
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.h b/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.h
index 70c8d524527..b86313dfbd4 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsumconfig.h
@@ -8,9 +8,9 @@ namespace search { class MatchingElementsFields; }
namespace search::docsummary {
class IDocsumEnvironment;
+class DocsumFieldWriter;
class DynamicDocsumWriter;
class ResultConfig;
-class IDocsumFieldWriter;
class DynamicDocsumConfig
{
@@ -27,7 +27,7 @@ protected:
const IDocsumEnvironment * getEnvironment() const { return _env; }
const ResultConfig & getResultConfig() const;
- virtual std::unique_ptr<IDocsumFieldWriter>
+ virtual std::unique_ptr<DocsumFieldWriter>
createFieldWriter(const string & fieldName, const string & overrideName,
const string & argument, bool & rc, std::shared_ptr<MatchingElementsFields> matching_elems_fields);
private:
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h b/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h
deleted file mode 100644
index bc135404de1..00000000000
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsumfieldwriter.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include "general_result.h"
-#include "resultconfig.h"
-#include <vespa/searchlib/util/rawbuf.h>
-#include <vespa/vespalib/data/slime/inserter.h>
-
-namespace search { class IAttributeManager; }
-
-namespace search::docsummary {
-
-class GetDocsumsState;
-
-class IDocsumFieldWriter
-{
-public:
- using UP = std::unique_ptr<IDocsumFieldWriter>;
- IDocsumFieldWriter() : _index(0) { }
- virtual ~IDocsumFieldWriter() = default;
-
- static bool IsRuntimeCompatible(ResType a, ResType b) {
- return ResultConfig::IsRuntimeCompatible(a, b);
- }
-
- virtual bool IsGenerated() const = 0;
- virtual void insertField(uint32_t docid, GeneralResult *gres, GetDocsumsState *state, ResType type,
- vespalib::slime::Inserter &target) = 0;
- virtual const vespalib::string & getAttributeName() const { return _empty; }
- virtual bool isDefaultValue(uint32_t docid, const GetDocsumsState * state) const {
- (void) docid;
- (void) state;
- return false;
- }
- void setIndex(size_t v) { _index = v; }
- size_t getIndex() const { return _index; }
- virtual bool setFieldWriterStateIndex(uint32_t fieldWriterStateIndex);
-private:
- size_t _index;
- static const vespalib::string _empty;
-};
-
-class ISimpleDFW : public IDocsumFieldWriter
-{
-public:
- virtual void insertField(uint32_t docid, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target) = 0;
- void insertField(uint32_t docid, GeneralResult *, GetDocsumsState *state, ResType type,
- vespalib::slime::Inserter &target) override
- {
- insertField(docid, state, type, target);
- }
-};
-
-//--------------------------------------------------------------------------
-
-class EmptyDFW : public ISimpleDFW
-{
-public:
- EmptyDFW();
- ~EmptyDFW() override;
-
- bool IsGenerated() const override { return true; }
- void insertField(uint32_t docid, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target) override;
-};
-
-//--------------------------------------------------------------------------
-
-class CopyDFW : public IDocsumFieldWriter
-{
-private:
- uint32_t _inputFieldEnumValue;
- vespalib::string _input_field_name;
-
-public:
- CopyDFW();
- ~CopyDFW() override;
-
- bool Init(const ResultConfig & config, const char *inputField);
-
- bool IsGenerated() const override { return false; }
- void insertField(uint32_t docid, GeneralResult *gres, GetDocsumsState *state, ResType type,
- vespalib::slime::Inserter &target) override;
-};
-
-}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.cpp b/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.cpp
index de8bb36e98b..1492ce2b435 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.cpp
@@ -1,9 +1,9 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "docsumwriter.h"
-#include "check_undefined_value_visitor.h"
#include "docsumstate.h"
#include "docsum_field_writer_state.h"
+#include "i_docsum_store_document.h"
#include "summaryfieldconverter.h"
#include <vespa/document/fieldvalue/fieldvalue.h>
#include <vespa/searchcommon/common/undefinedvalues.h>
@@ -20,22 +20,6 @@ using vespalib::Issue;
namespace search::docsummary {
-namespace {
-
-void insert_document_field(const vespalib::string& field_name, const GeneralResult& gres, Inserter &inserter)
-{
- auto input_field_value = gres.get_field_value(field_name);
- if (input_field_value) {
- CheckUndefinedValueVisitor check_undefined;
- input_field_value->accept(check_undefined);
- if (!check_undefined.is_undefined()) {
- SummaryFieldConverter::insert_summary_field(false, *input_field_value, inserter);
- }
- }
-}
-
-}
-
uint32_t
IDocsumWriter::slime2RawBuf(const Slime & slime, RawBuf & buf)
{
@@ -116,7 +100,10 @@ static void convertEntry(const ResConfigEntry *resCfg,
LOG_ASSERT(resCfg != nullptr);
if (entry == nullptr || entry->_not_present) {
// Entry is not present in docsum blob
- insert_document_field(resCfg->_bindname, gres, inserter);
+ const auto* document = gres.get_document();
+ if (document != nullptr) {
+ document->insert_summary_field(resCfg->_bindname, inserter);
+ }
return;
}
@@ -181,7 +168,7 @@ DynamicDocsumWriter::insertDocsum(const ResolveClassInfo & rci, uint32_t docid,
vespalib::slime::Cursor & docsum = topInserter.insertObject();
for (uint32_t i = 0; i < rci.outputClass->GetNumEntries(); ++i) {
const ResConfigEntry *resCfg = rci.outputClass->GetEntry(i);
- IDocsumFieldWriter *writer = _overrideTable[resCfg->_enumValue];
+ DocsumFieldWriter *writer = _overrideTable[resCfg->_enumValue];
if (! writer->isDefaultValue(docid, state)) {
const Memory field_name(resCfg->_bindname.data(), resCfg->_bindname.size());
ObjectInserter inserter(docsum, field_name);
@@ -201,7 +188,7 @@ DynamicDocsumWriter::insertDocsum(const ResolveClassInfo & rci, uint32_t docid,
vespalib::slime::Cursor & docsum = topInserter.insertObject();
for (uint32_t i = 0; i < rci.outputClass->GetNumEntries(); ++i) {
const ResConfigEntry *outCfg = rci.outputClass->GetEntry(i);
- IDocsumFieldWriter *writer = _overrideTable[outCfg->_enumValue];
+ DocsumFieldWriter *writer = _overrideTable[outCfg->_enumValue];
const Memory field_name(outCfg->_bindname.data(), outCfg->_bindname.size());
ObjectInserter inserter(docsum, field_name);
if (writer != nullptr) {
@@ -214,13 +201,16 @@ DynamicDocsumWriter::insertDocsum(const ResolveClassInfo & rci, uint32_t docid,
} else {
int inIdx = rci.inputClass->GetIndexFromEnumValue(outCfg->_enumValue);
const ResConfigEntry *inCfg = rci.inputClass->GetEntry(inIdx);
- if (inCfg != nullptr && inCfg->_type == outCfg->_type) {
+ if (inCfg != nullptr && inCfg->_type == outCfg->_type && !inCfg->_not_present) {
// copy field
const ResEntry *entry = gres.GetEntry(inIdx);
LOG_ASSERT(entry != nullptr);
convertEntry(outCfg, entry, gres, inserter, slime);
} else {
- insert_document_field(outCfg->_bindname, gres, inserter);
+ const auto* document = gres.get_document();
+ if (document != nullptr) {
+ document->insert_summary_field(outCfg->_bindname, inserter);
+ }
}
}
}
@@ -240,7 +230,7 @@ DynamicDocsumWriter::DynamicDocsumWriter( ResultConfig *config, KeywordExtractor
{
LOG_ASSERT(config != nullptr);
_classInfoTable = new ResultClass::DynamicInfo[_numClasses];
- _overrideTable = new IDocsumFieldWriter*[_numEnumValues];
+ _overrideTable = new DocsumFieldWriter*[_numEnumValues];
uint32_t i = 0;
for (ResultConfig::iterator it(config->begin()), mt(config->end()); it != mt; it++, i++) {
@@ -289,7 +279,7 @@ DynamicDocsumWriter::SetDefaultOutputClass(uint32_t classID)
bool
-DynamicDocsumWriter::Override(const char *fieldName, IDocsumFieldWriter *writer)
+DynamicDocsumWriter::Override(const char *fieldName, DocsumFieldWriter *writer)
{
uint32_t fieldEnumValue = _resultConfig->GetFieldNameEnum().Lookup(fieldName);
@@ -312,10 +302,10 @@ DynamicDocsumWriter::Override(const char *fieldName, IDocsumFieldWriter *writer)
++_numFieldWriterStates;
}
- for (auto & entry : *_resultConfig) {
+ for (auto & result_class : *_resultConfig) {
- if (entry.GetIndexFromEnumValue(fieldEnumValue) >= 0) {
- ResultClass::DynamicInfo *info = entry.getDynamicInfo();
+ if (result_class.GetIndexFromEnumValue(fieldEnumValue) >= 0) {
+ ResultClass::DynamicInfo *info = result_class.getDynamicInfo();
info->_overrideCnt++;
if (writer->IsGenerated())
info->_generateCnt++;
@@ -334,7 +324,7 @@ DynamicDocsumWriter::InitState(IAttributeManager & attrMan, GetDocsumsState *sta
state->_attributes.resize(_numEnumValues);
state->_fieldWriterStates.resize(_numFieldWriterStates);
for (size_t i(0); i < state->_attributes.size(); i++) {
- const IDocsumFieldWriter *fw = _overrideTable[i];
+ const DocsumFieldWriter *fw = _overrideTable[i];
if (fw) {
const vespalib::string & attributeName = fw->getAttributeName();
if (!attributeName.empty()) {
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.h b/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.h
index e70e3db8655..b3182221b68 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/docsumwriter.h
@@ -7,12 +7,14 @@
#include "resultconfig.h"
#include "docsumstore.h"
#include "keywordextractor.h"
-#include "docsumfieldwriter.h"
+#include "docsum_field_writer.h"
#include <vespa/searchlib/util/rawbuf.h>
#include <vespa/fastlib/text/unicodeutil.h>
#include <vespa/fastlib/text/wordfolder.h>
-using search::IAttributeManager;
+namespace search { class IAttributeManager; }
+
+namespace vespalib { class Slime; }
namespace search::docsummary {
@@ -36,7 +38,7 @@ public:
};
virtual ~IDocsumWriter() {}
- virtual void InitState(IAttributeManager & attrMan, GetDocsumsState *state) = 0;
+ virtual void InitState(search::IAttributeManager & attrMan, GetDocsumsState *state) = 0;
virtual uint32_t WriteDocsum(uint32_t docid, GetDocsumsState *state,
IDocsumStore *docinfos, search::RawBuf *target) = 0;
virtual void insertDocsum(const ResolveClassInfo & rci, uint32_t docid, GetDocsumsState *state,
@@ -58,7 +60,7 @@ private:
uint32_t _numEnumValues;
uint32_t _numFieldWriterStates;
ResultClass::DynamicInfo *_classInfoTable;
- IDocsumFieldWriter **_overrideTable;
+ DocsumFieldWriter** _overrideTable;
void resolveInputClass(ResolveClassInfo &rci, uint32_t id) const;
@@ -73,8 +75,8 @@ public:
ResultConfig *GetResultConfig() { return _resultConfig; }
bool SetDefaultOutputClass(uint32_t classID);
- bool Override(const char *fieldName, IDocsumFieldWriter *writer);
- void InitState(IAttributeManager & attrMan, GetDocsumsState *state) override;
+ bool Override(const char *fieldName, DocsumFieldWriter *writer);
+ void InitState(search::IAttributeManager & attrMan, GetDocsumsState *state) override;
uint32_t WriteDocsum(uint32_t docid, GetDocsumsState *state,
IDocsumStore *docinfos, search::RawBuf *target) override;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp
index ef1ffded941..57adbcc8163 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp
@@ -332,7 +332,7 @@ JuniperTeaserDFW::Init(
const ResConfigEntry *entry =
it->GetEntry(it->GetIndexFromEnumValue(_inputFieldEnumValue));
- if (entry != nullptr &&
+ if (entry != nullptr && !entry->_not_present &&
!IsRuntimeCompatible(entry->_type, RES_STRING) &&
!IsRuntimeCompatible(entry->_type, RES_DATA))
{
@@ -347,7 +347,7 @@ JuniperTeaserDFW::Init(
vespalib::stringref
DynamicTeaserDFW::getJuniperInput(GeneralResult *gres) {
int idx = gres->GetClass()->GetIndexFromEnumValue(_inputFieldEnumValue);
- ResEntry *entry = gres->GetEntry(idx);
+ ResEntry *entry = gres->GetPresentEntry(idx);
if (entry != nullptr) {
const char *buf;
uint32_t buflen;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.cpp
new file mode 100644
index 00000000000..3d3b1e11626
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.cpp
@@ -0,0 +1,19 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "empty_dfw.h"
+
+namespace search::docsummary {
+
+EmptyDFW::EmptyDFW() = default;
+
+EmptyDFW::~EmptyDFW() = default;
+
+void
+EmptyDFW::insertField(uint32_t, GetDocsumsState *, ResType, vespalib::slime::Inserter &target)
+{
+ // insert explicitly-empty field?
+ // target.insertNix();
+ (void)target;
+}
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.h b/searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.h
new file mode 100644
index 00000000000..d43eed8e9c6
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/empty_dfw.h
@@ -0,0 +1,22 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "simple_dfw.h"
+
+namespace search::docsummary {
+
+/*
+ * Class for writing empty document summaries.
+ */
+class EmptyDFW : public SimpleDFW
+{
+public:
+ EmptyDFW();
+ ~EmptyDFW() override;
+
+ bool IsGenerated() const override { return true; }
+ void insertField(uint32_t docid, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target) override;
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/general_result.cpp b/searchsummary/src/vespa/searchsummary/docsummary/general_result.cpp
index 12391d26ce1..825c3b39c1b 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/general_result.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/general_result.cpp
@@ -49,25 +49,17 @@ GeneralResult::~GeneralResult()
}
ResEntry *
-GeneralResult::GetEntry(uint32_t idx)
-{
- return (idx < _entrycnt) ? &_entries[idx] : nullptr;
-}
-
-ResEntry *
-GeneralResult::GetEntry(const char *name)
+GeneralResult::GetPresentEntry(const char *name)
{
int idx = _resClass->GetIndexFromName(name);
-
- return (idx >= 0 && (uint32_t)idx < _entrycnt) ? &_entries[idx] : nullptr;
+ return GetPresentEntry(idx);
}
-
ResEntry *
-GeneralResult::GetEntryFromEnumValue(uint32_t value)
+GeneralResult::GetPresentEntryFromEnumValue(uint32_t value)
{
int idx = _resClass->GetIndexFromEnumValue(value);
- return (idx >= 0 && (uint32_t)idx < _entrycnt) ? &_entries[idx] : nullptr;
+ return GetPresentEntry(idx);
}
std::unique_ptr<document::FieldValue>
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/general_result.h b/searchsummary/src/vespa/searchsummary/docsummary/general_result.h
index 93114dbb44d..cff27a496e3 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/general_result.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/general_result.h
@@ -32,9 +32,16 @@ public:
~GeneralResult();
const ResultClass *GetClass() const { return _resClass; }
- ResEntry *GetEntry(uint32_t idx);
- ResEntry *GetEntry(const char *name);
- ResEntry *GetEntryFromEnumValue(uint32_t val);
+ ResEntry *GetEntry(uint32_t idx) { return (idx < _entrycnt) ? &_entries[idx] : nullptr; }
+ ResEntry *GetPresentEntry(uint32_t idx) {
+ if (idx >= _entrycnt) {
+ return nullptr;
+ }
+ ResEntry* entry = &_entries[idx];
+ return entry->_not_present ? nullptr : entry;
+ }
+ ResEntry *GetPresentEntry(const char *name);
+ ResEntry *GetPresentEntryFromEnumValue(uint32_t val);
std::unique_ptr<document::FieldValue> get_field_value(const vespalib::string& field_name) const;
bool unpack(const char *buf, const size_t buflen);
@@ -46,6 +53,8 @@ public:
return false;
}
}
+
+ const IDocsumStoreDocument *get_document() const noexcept { return _document; }
};
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp
index 8f627ac1b9a..c3806d6e7ea 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/geoposdfw.cpp
@@ -6,6 +6,7 @@
#include <vespa/searchlib/common/location.h>
#include <vespa/vespalib/util/jsonwriter.h>
#include <vespa/vespalib/data/slime/cursor.h>
+#include <vespa/vespalib/data/slime/inserter.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/issue.h>
#include <climits>
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/i_docsum_store_document.h b/searchsummary/src/vespa/searchsummary/docsummary/i_docsum_store_document.h
index 3fbf54b18a9..f81412eb34c 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/i_docsum_store_document.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/i_docsum_store_document.h
@@ -7,6 +7,8 @@
namespace document { class FieldValue; }
+namespace vespalib::slime { struct Inserter; }
+
namespace search::docsummary {
/**
@@ -19,6 +21,7 @@ class IDocsumStoreDocument
public:
virtual ~IDocsumStoreDocument() = default;
virtual std::unique_ptr<document::FieldValue> get_field_value(const vespalib::string& field_name) const = 0;
+ virtual void insert_summary_field(const vespalib::string& field_name, vespalib::slime::Inserter& inserter) const = 0;
};
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/juniperdfw.h b/searchsummary/src/vespa/searchsummary/docsummary/juniperdfw.h
index d9a657038c4..5e2ec517a47 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/juniperdfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/juniperdfw.h
@@ -4,14 +4,14 @@
#include "general_result.h"
#include "resultconfig.h"
-#include "docsumfieldwriter.h"
+#include "docsum_field_writer.h"
#include <vespa/searchlib/util/rawbuf.h>
#include <vespa/vespalib/data/slime/inserter.h>
#include <vespa/juniper/rpinterface.h>
namespace search::docsummary {
-class JuniperDFW : public IDocsumFieldWriter
+class JuniperDFW : public DocsumFieldWriter
{
public:
virtual bool Init(
@@ -21,7 +21,7 @@ public:
const char *inputField);
protected:
JuniperDFW(juniper::Juniper * juniper);
- virtual ~JuniperDFW();
+ ~JuniperDFW() override;
uint32_t _inputFieldEnumValue;
std::unique_ptr<juniper::Config> _juniperConfig;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.cpp
index fb53ddcc470..c05fec7a0ce 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.cpp
@@ -1,7 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "docsumstate.h"
#include "matched_elements_filter_dfw.h"
+#include "docsumstate.h"
+#include "general_result.h"
#include "struct_fields_resolver.h"
#include "summaryfieldconverter.h"
#include <vespa/document/fieldvalue/document.h>
@@ -40,21 +41,21 @@ MatchedElementsFilterDFW::MatchedElementsFilterDFW(const std::string& input_fiel
{
}
-std::unique_ptr<IDocsumFieldWriter>
+std::unique_ptr<DocsumFieldWriter>
MatchedElementsFilterDFW::create(const std::string& input_field_name, uint32_t input_field_enum,
std::shared_ptr<MatchingElementsFields> matching_elems_fields)
{
return std::make_unique<MatchedElementsFilterDFW>(input_field_name, input_field_enum, std::move(matching_elems_fields));
}
-std::unique_ptr<IDocsumFieldWriter>
+std::unique_ptr<DocsumFieldWriter>
MatchedElementsFilterDFW::create(const std::string& input_field_name, uint32_t input_field_enum,
search::attribute::IAttributeContext& attr_ctx,
std::shared_ptr<MatchingElementsFields> matching_elems_fields)
{
StructFieldsResolver resolver(input_field_name, attr_ctx, false);
if (resolver.has_error()) {
- return std::unique_ptr<IDocsumFieldWriter>();
+ return std::unique_ptr<DocsumFieldWriter>();
}
resolver.apply_to(*matching_elems_fields);
return std::make_unique<MatchedElementsFilterDFW>(input_field_name, input_field_enum, std::move(matching_elems_fields));
@@ -93,7 +94,7 @@ filter_matching_elements_in_input_field_while_converting_to_slime(const FieldVal
bool
resolve_input_field_as_slime(GeneralResult& result, int entry_idx, Slime& input_field_as_slime)
{
- ResEntry* entry = result.GetEntry(entry_idx);
+ ResEntry* entry = result.GetPresentEntry(entry_idx);
if (entry != nullptr) {
decode_input_field_to_slime(*entry, input_field_as_slime);
return true;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.h b/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.h
index 505a2557408..b117da541d6 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/matched_elements_filter_dfw.h
@@ -2,7 +2,11 @@
#pragma once
-#include "docsumfieldwriter.h"
+#include "docsum_field_writer.h"
+#include <memory>
+#include <vector>
+
+namespace search { class MatchingElementsFields; }
namespace search::attribute { class IAttributeContext; }
@@ -13,7 +17,7 @@ namespace search::docsummary {
* (array of primitive, weighted set of primitive, map of primitives, map of struct, array of struct)
* that is retrieved from the document store.
*/
-class MatchedElementsFilterDFW : public IDocsumFieldWriter {
+class MatchedElementsFilterDFW : public DocsumFieldWriter {
private:
std::string _input_field_name;
uint32_t _input_field_enum;
@@ -24,11 +28,11 @@ private:
public:
MatchedElementsFilterDFW(const std::string& input_field_name, uint32_t input_field_enum,
std::shared_ptr<MatchingElementsFields> matching_elems_fields);
- static std::unique_ptr<IDocsumFieldWriter> create(const std::string& input_field_name, uint32_t input_field_enum,
- std::shared_ptr<MatchingElementsFields> matching_elems_fields);
- static std::unique_ptr<IDocsumFieldWriter> create(const std::string& input_field_name, uint32_t input_field_enum,
- search::attribute::IAttributeContext& attr_ctx,
- std::shared_ptr<MatchingElementsFields> matching_elems_fields);
+ static std::unique_ptr<DocsumFieldWriter> create(const std::string& input_field_name, uint32_t input_field_enum,
+ std::shared_ptr<MatchingElementsFields> matching_elems_fields);
+ static std::unique_ptr<DocsumFieldWriter> create(const std::string& input_field_name, uint32_t input_field_enum,
+ search::attribute::IAttributeContext& attr_ctx,
+ std::shared_ptr<MatchingElementsFields> matching_elems_fields);
~MatchedElementsFilterDFW();
bool IsGenerated() const override { return false; }
void insertField(uint32_t docid, GeneralResult* result, GetDocsumsState *state,
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp
index 1fcb0a49be1..7f3a929a62f 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.cpp
@@ -270,8 +270,9 @@ PositionsDFW::UP PositionsDFW::create(const char *attribute_name, IAttributeMana
return std::make_unique<PositionsDFW>(attribute_name, useV8geoPositions);
}
-AbsDistanceDFW::UP AbsDistanceDFW::create(const char *attribute_name, IAttributeManager *attribute_manager) {
- AbsDistanceDFW::UP ret;
+std::unique_ptr<DocsumFieldWriter>
+AbsDistanceDFW::create(const char *attribute_name, IAttributeManager *attribute_manager) {
+ std::unique_ptr<DocsumFieldWriter> ret;
if (attribute_manager != nullptr) {
if (!attribute_name) {
LOG(debug, "createAbsDistanceDFW: missing attribute name '%p'", attribute_name);
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.h b/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.h
index b3e041c1379..d9445abd2ff 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/positionsdfw.h
@@ -45,7 +45,7 @@ public:
void insertField(uint32_t docid, GetDocsumsState *state,
ResType type, vespalib::slime::Inserter &target) override;
- static UP create(const char *attribute_name, IAttributeManager *index_man);
+ static std::unique_ptr<DocsumFieldWriter> create(const char *attribute_name, IAttributeManager *index_man);
};
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp
index 38b58ef94fc..5d3b104189b 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.cpp
@@ -3,6 +3,7 @@
#include "rankfeaturesdfw.h"
#include "docsumstate.h"
#include <vespa/vespalib/data/slime/cursor.h>
+#include <vespa/vespalib/data/slime/inserter.h>
namespace search::docsummary {
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.h b/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.h
index eab9fab60b2..91f9e80d303 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/rankfeaturesdfw.h
@@ -2,11 +2,13 @@
#pragma once
-#include "summaryfeaturesdfw.h"
+#include "simple_dfw.h"
namespace search::docsummary {
-class RankFeaturesDFW : public ISimpleDFW
+class IDocsumEnvironment;
+
+class RankFeaturesDFW : public SimpleDFW
{
private:
IDocsumEnvironment * _env;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/res_type.h b/searchsummary/src/vespa/searchsummary/docsummary/res_type.h
new file mode 100644
index 00000000000..02c9f1522a4
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/res_type.h
@@ -0,0 +1,30 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace search::docsummary {
+
+/**
+ * This enumeration contains values denoting the different types of
+ * docsum fields. NOTE: The internal implementation depends on RES_INT
+ * having the value 0. All types < RES_STRING must be fixed size and
+ * all types > RES_STRING must be variable size.
+ **/
+enum ResType {
+ RES_INT = 0,
+ RES_SHORT,
+ RES_BOOL,
+ RES_BYTE,
+ RES_FLOAT,
+ RES_DOUBLE,
+ RES_INT64,
+ RES_STRING,
+ RES_DATA,
+ RES_LONG_STRING,
+ RES_LONG_DATA,
+ RES_JSONSTRING,
+ RES_TENSOR,
+ RES_FEATUREDATA
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.cpp b/searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.cpp
new file mode 100644
index 00000000000..98cc8372ac1
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.cpp
@@ -0,0 +1,29 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "res_type_utils.h"
+
+namespace search::docsummary {
+
+const char *
+ResTypeUtils::GetResTypeName(ResType type)
+{
+ switch (type) {
+ case RES_INT: return "integer";
+ case RES_SHORT: return "short";
+ case RES_BYTE: return "byte";
+ case RES_BOOL: return "bool";
+ case RES_FLOAT: return "float";
+ case RES_DOUBLE: return "double";
+ case RES_INT64: return "int64";
+ case RES_STRING: return "string";
+ case RES_DATA: return "data";
+ case RES_LONG_STRING: return "longstring";
+ case RES_LONG_DATA: return "longdata";
+ case RES_JSONSTRING: return "jsonstring";
+ case RES_TENSOR: return "tensor";
+ case RES_FEATUREDATA: return "featuredata";
+ }
+ return "unknown-type";
+}
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.h b/searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.h
new file mode 100644
index 00000000000..194a008c179
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/res_type_utils.h
@@ -0,0 +1,102 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "res_type.h"
+
+namespace search::docsummary {
+
+
+/*
+ * Utilitiy functions for checking if result type is ok.
+ */
+struct ResTypeUtils
+{
+ /**
+ * Determine if a result field type is of variable size.
+ *
+ * @return true for variable size field types, false for fixed
+ * size field types
+ **/
+ static bool IsVariableSize(ResType t) { return (t >= RES_STRING); }
+
+
+ /**
+ * Determine if a pair of result field types are binary
+ * compatible. A pair of types are binary compatible if the packed
+ * representation is identical.
+ *
+ * @return true if the given types are binary compatible.
+ * @param a enum value of a result field type.
+ * @param b enum value of a result field type.
+ **/
+ static bool IsBinaryCompatible(ResType a, ResType b)
+ {
+ if (a == b) {
+ return true;
+ }
+ switch (a) {
+ case RES_BYTE:
+ case RES_BOOL:
+ return (b == RES_BYTE || b == RES_BOOL);
+ case RES_STRING:
+ case RES_DATA:
+ return (b == RES_STRING || b == RES_DATA);
+ case RES_LONG_STRING:
+ case RES_LONG_DATA:
+ case RES_FEATUREDATA:
+ case RES_JSONSTRING:
+ return (b == RES_LONG_STRING || b == RES_LONG_DATA ||
+ b == RES_FEATUREDATA || b == RES_JSONSTRING);
+ default:
+ return false;
+ }
+ return false;
+ }
+
+
+ /**
+ * Determine if a pair of result field types are runtime
+ * compatible. A pair of types are runtime compatible if the
+ * unpacked (@ref ResEntry) representation is identical.
+ *
+ * @return true if the given types are runtime compatible.
+ * @param a enum value of a result field type.
+ * @param b enum value of a result field type.
+ **/
+ static bool IsRuntimeCompatible(ResType a, ResType b)
+ {
+ switch (a) {
+ case RES_INT:
+ case RES_SHORT:
+ case RES_BYTE:
+ case RES_BOOL:
+ return (b == RES_INT || b == RES_SHORT || b == RES_BYTE || b == RES_BOOL);
+ case RES_FLOAT:
+ case RES_DOUBLE:
+ return (b == RES_FLOAT || b == RES_DOUBLE);
+ case RES_INT64:
+ return b == RES_INT64;
+ case RES_STRING:
+ case RES_LONG_STRING:
+ case RES_JSONSTRING:
+ return (b == RES_STRING || b == RES_LONG_STRING || b == RES_JSONSTRING);
+ case RES_DATA:
+ case RES_LONG_DATA:
+ return (b == RES_DATA || b == RES_LONG_DATA);
+ case RES_TENSOR:
+ return (b == RES_TENSOR);
+ case RES_FEATUREDATA:
+ return (b == RES_FEATUREDATA);
+ }
+ return false;
+ }
+
+ /**
+ * @return the name of the given result field type.
+ * @param resType enum value of a result field type.
+ **/
+ static const char *GetResTypeName(ResType type);
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/resultclass.cpp b/searchsummary/src/vespa/searchsummary/docsummary/resultclass.cpp
index ed1cd8b542b..65db72efc40 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/resultclass.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/resultclass.cpp
@@ -26,11 +26,7 @@ int
ResultClass::GetIndexFromName(const char* name) const
{
NameIdMap::const_iterator found(_nameMap.find(name));
- if (found == _nameMap.end()) {
- return -1;
- }
- int idx = found->second;
- return _entries[idx]._not_present ? -1 : idx;
+ return (found != _nameMap.end()) ? found->second : -1;
}
bool
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/resultclass.h b/searchsummary/src/vespa/searchsummary/docsummary/resultclass.h
index 8865e28acc6..47feed70e97 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/resultclass.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/resultclass.h
@@ -2,57 +2,15 @@
#pragma once
+#include "docsum_blob_entry_filter.h"
#include <vespa/searchlib/util/rawbuf.h>
#include <vespa/vespalib/stllike/string.h>
#include <vespa/vespalib/stllike/hash_map.h>
#include <vespa/searchlib/util/stringenum.h>
-#include <bitset>
namespace search::docsummary {
/**
- * This enumeration contains values denoting the different types of
- * docsum fields. NOTE: The internal implementation depends on RES_INT
- * having the value 0. All types < RES_STRING must be fixed size and
- * all types > RES_STRING must be variable size.
- **/
-enum ResType {
- RES_INT = 0,
- RES_SHORT,
- RES_BOOL,
- RES_BYTE,
- RES_FLOAT,
- RES_DOUBLE,
- RES_INT64,
- RES_STRING,
- RES_DATA,
- RES_LONG_STRING,
- RES_LONG_DATA,
- RES_JSONSTRING,
- RES_TENSOR,
- RES_FEATUREDATA
-};
-
-/*
- * Class containing the set of result types not stored in docsum blobs.
- * This is used for gradual migration towards elimination of docsum blobs.
- */
-class DocsumBlobEntryFilter {
- std::bitset<14> _skip_types;
-
-public:
- DocsumBlobEntryFilter()
- : _skip_types()
- {
- }
- bool skip(ResType type) const noexcept { return _skip_types.test(type); }
- DocsumBlobEntryFilter &add_skip(ResType type) {
- _skip_types.set(type);
- return *this;
- }
-};
-
-/**
* This struct describes a single docsum field (name and type). A
* docsum blob is unpacked into an array of ResEntry instances
* by interpreting it as described by an array of ResConfigEntry
@@ -238,7 +196,7 @@ public:
* GeneralResult::GetEntry(string) method; no need to call it
* directly.
*
- * @return field index or -1 if not found or _not_present is set.
+ * @return field index or -1 if not found
**/
int GetIndexFromName(const char* name) const;
@@ -255,15 +213,11 @@ public:
* call it directly. NOTE3: You need to call the CreateEnumMap
* method before calling this one.
*
- * @return field index or -1 if not found or _not_present is set.
+ * @return field index or -1 if not found
**/
int GetIndexFromEnumValue(uint32_t value) const
{
- if (value >= _enumMap.size()) {
- return -1;
- }
- int idx = _enumMap[value];
- return ((idx < 0) || _entries[idx]._not_present) ? -1 : idx;
+ return (value < _enumMap.size()) ? _enumMap[value] : -1;
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.cpp b/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.cpp
index 4096e26a6e3..168b4c81374 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.cpp
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "resultconfig.h"
+#include "resultclass.h"
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/stllike/hash_map.hpp>
#include <atomic>
@@ -45,28 +46,6 @@ ResultConfig::~ResultConfig()
}
-const char *
-ResultConfig::GetResTypeName(ResType type)
-{
- switch (type) {
- case RES_INT: return "integer";
- case RES_SHORT: return "short";
- case RES_BYTE: return "byte";
- case RES_BOOL: return "bool";
- case RES_FLOAT: return "float";
- case RES_DOUBLE: return "double";
- case RES_INT64: return "int64";
- case RES_STRING: return "string";
- case RES_DATA: return "data";
- case RES_LONG_STRING: return "longstring";
- case RES_LONG_DATA: return "longdata";
- case RES_JSONSTRING: return "jsonstring";
- case RES_TENSOR: return "tensor";
- case RES_FEATUREDATA: return "featuredata";
- }
- return "unknown-type";
-}
-
void
ResultConfig::Reset()
{
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.h b/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.h
index 1438aee73ce..945eef8514f 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/resultconfig.h
@@ -2,14 +2,15 @@
#pragma once
-#include "resultclass.h"
-#include "general_result.h"
+#include "docsum_blob_entry_filter.h"
+#include "res_type_utils.h"
#include <vespa/config-summary.h>
-#include <vespa/searchlib/util/rawbuf.h>
#include <vespa/searchlib/util/stringenum.h>
namespace search::docsummary {
+class ResultClass;
+
/**
* This class represents the overall result configuration. A result
* configuration may contain multiple result classes, where each
@@ -31,7 +32,7 @@ private:
ResultConfig& operator=(const ResultConfig &);
typedef vespalib::hash_map<vespalib::string, uint32_t> NameMap;
- typedef vespalib::hash_map<uint32_t, ResultClass::UP> IdMap;
+ typedef vespalib::hash_map<uint32_t, std::unique_ptr<ResultClass>> IdMap;
uint32_t _defaultSummaryId;
bool _useV8geoPositions;
search::util::StringEnum _fieldEnum;
@@ -95,85 +96,9 @@ public:
static uint32_t NoClassID() { return static_cast<uint32_t>(-1); }
- /**
- * Determine if a result field type is of variable size.
- *
- * @return true for variable size field types, false for fixed
- * size field types
- **/
- static bool IsVariableSize(ResType t) { return (t >= RES_STRING); }
-
-
- /**
- * Determine if a pair of result field types are binary
- * compatible. A pair of types are binary compatible if the packed
- * representation is identical.
- *
- * @return true if the given types are binary compatible.
- * @param a enum value of a result field type.
- * @param b enum value of a result field type.
- **/
- static bool IsBinaryCompatible(ResType a, ResType b)
- {
- if (a == b) {
- return true;
- }
- switch (a) {
- case RES_BYTE:
- case RES_BOOL:
- return (b == RES_BYTE || b == RES_BOOL);
- case RES_STRING:
- case RES_DATA:
- return (b == RES_STRING || b == RES_DATA);
- case RES_LONG_STRING:
- case RES_LONG_DATA:
- case RES_FEATUREDATA:
- case RES_JSONSTRING:
- return (b == RES_LONG_STRING || b == RES_LONG_DATA ||
- b == RES_FEATUREDATA || b == RES_JSONSTRING);
- default:
- return false;
- }
- return false;
- }
-
-
- /**
- * Determine if a pair of result field types are runtime
- * compatible. A pair of types are runtime compatible if the
- * unpacked (@ref ResEntry) representation is identical.
- *
- * @return true if the given types are runtime compatible.
- * @param a enum value of a result field type.
- * @param b enum value of a result field type.
- **/
- static bool IsRuntimeCompatible(ResType a, ResType b)
- {
- switch (a) {
- case RES_INT:
- case RES_SHORT:
- case RES_BYTE:
- case RES_BOOL:
- return (b == RES_INT || b == RES_SHORT || b == RES_BYTE || b == RES_BOOL);
- case RES_FLOAT:
- case RES_DOUBLE:
- return (b == RES_FLOAT || b == RES_DOUBLE);
- case RES_INT64:
- return b == RES_INT64;
- case RES_STRING:
- case RES_LONG_STRING:
- case RES_JSONSTRING:
- return (b == RES_STRING || b == RES_LONG_STRING || b == RES_JSONSTRING);
- case RES_DATA:
- case RES_LONG_DATA:
- return (b == RES_DATA || b == RES_LONG_DATA);
- case RES_TENSOR:
- return (b == RES_TENSOR);
- case RES_FEATUREDATA:
- return (b == RES_FEATUREDATA);
- }
- return false;
- }
+ static bool IsVariableSize(ResType t) { return ResTypeUtils::IsVariableSize(t); }
+ static bool IsBinaryCompatible(ResType a, ResType b) { return ResTypeUtils::IsBinaryCompatible(a, b); }
+ static bool IsRuntimeCompatible(ResType a, ResType b) { return ResTypeUtils::IsRuntimeCompatible(a, b); }
// whether last config seen wanted useV8geoPositions = true
static bool wantedV8geoPositions();
@@ -182,7 +107,7 @@ public:
* @return the name of the given result field type.
* @param resType enum value of a result field type.
**/
- static const char *GetResTypeName(ResType type);
+ static const char *GetResTypeName(ResType type) { return ResTypeUtils::GetResTypeName(type); }
/**
* Discard the current configuration and start over. After this
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.cpp b/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.cpp
index 4cf36785f69..66b15c1c8a9 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.cpp
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "resultpacker.h"
+#include "resultconfig.h"
#include <vespa/searchcommon/common/undefinedvalues.h>
#include <vespa/vespalib/util/size_literals.h>
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.h b/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.h
index f2460f3d3c3..816433652b8 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/resultpacker.h
@@ -2,9 +2,14 @@
#pragma once
-#include "resultconfig.h"
+#include "res_type_utils.h"
+#include "resultclass.h"
+#include <vespa/searchlib/util/rawbuf.h>
namespace search::docsummary {
+
+class ResultConfig;
+
/**
* An Object of this class may be used to create docsum blobs. A
* single blob is created by first indicating what result class the
@@ -27,11 +32,8 @@ private:
const ResConfigEntry *_cfgEntry; // current field of current blob
bool _error; // error flag for current blob
- static const char *GetResTypeName(ResType type)
- { return ResultConfig::GetResTypeName(type); }
-
- static bool IsBinaryCompatible(ResType a, ResType b)
- { return ResultConfig::IsBinaryCompatible(a, b); }
+ static const char *GetResTypeName(ResType type) { return ResTypeUtils::GetResTypeName(type); }
+ static bool IsBinaryCompatible(ResType a, ResType b) { return ResTypeUtils::IsBinaryCompatible(a, b); }
void WarnType(ResType type) const;
void SetFormatError(ResType type);
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.cpp
new file mode 100644
index 00000000000..8cc872378bd
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.cpp
@@ -0,0 +1,13 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "simple_dfw.h"
+
+namespace search::docsummary {
+
+void
+SimpleDFW::insertField(uint32_t docid, GeneralResult *, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target)
+{
+ insertField(docid, state, type, target);
+}
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.h b/searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.h
new file mode 100644
index 00000000000..abebb7de2b8
--- /dev/null
+++ b/searchsummary/src/vespa/searchsummary/docsummary/simple_dfw.h
@@ -0,0 +1,20 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "docsum_field_writer.h"
+
+namespace search::docsummary {
+
+/*
+ * Abstract class for writing document summaries that don't need
+ * access to a document retrieved from IDocsumStore.
+ */
+class SimpleDFW : public DocsumFieldWriter
+{
+public:
+ virtual void insertField(uint32_t docid, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target) = 0;
+ void insertField(uint32_t docid, GeneralResult *, GetDocsumsState *state, ResType type, vespalib::slime::Inserter &target) override;
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/struct_map_attribute_combiner_dfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/struct_map_attribute_combiner_dfw.cpp
index aec55977546..38a9cc8c50b 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/struct_map_attribute_combiner_dfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/struct_map_attribute_combiner_dfw.cpp
@@ -9,6 +9,7 @@
#include <vespa/searchlib/common/matching_elements.h>
#include <vespa/searchlib/common/matching_elements_fields.h>
#include <vespa/vespalib/data/slime/cursor.h>
+#include <vespa/vespalib/data/slime/inserter.h>
#include <vespa/vespalib/util/stash.h>
#include <algorithm>
#include <cassert>
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.cpp
index f920b7be0cd..c28e986612c 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.cpp
@@ -3,6 +3,7 @@
#include "summaryfeaturesdfw.h"
#include "docsumstate.h"
#include <vespa/vespalib/data/slime/cursor.h>
+#include <vespa/vespalib/data/slime/inserter.h>
#include <vespa/log/log.h>
LOG_SETUP(".searchlib.docsummary.summaryfeaturesdfw");
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.h b/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.h
index 6c4084b0221..d12feb69182 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/summaryfeaturesdfw.h
@@ -2,13 +2,13 @@
#pragma once
-#include "docsumfieldwriter.h"
+#include "simple_dfw.h"
namespace search::docsummary {
class IDocsumEnvironment;
-class SummaryFeaturesDFW : public ISimpleDFW
+class SummaryFeaturesDFW : public SimpleDFW
{
private:
IDocsumEnvironment * _env;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp b/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp
index 6082c82e863..1fcd0213ce2 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.cpp
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "summaryfieldconverter.h"
+#include "check_undefined_value_visitor.h"
#include "linguisticsannotation.h"
#include "resultconfig.h"
#include "searchdatatype.h"
@@ -594,10 +595,14 @@ SummaryFieldConverter::convert_field_with_filter(bool markup,
}
void
-SummaryFieldConverter::insert_summary_field(bool markup, const FieldValue& value, vespalib::slime::Inserter& inserter)
+SummaryFieldConverter::insert_summary_field(const FieldValue& value, vespalib::slime::Inserter& inserter)
{
- SlimeFiller visitor(inserter, markup);
- value.accept(visitor);
+ CheckUndefinedValueVisitor check_undefined;
+ value.accept(check_undefined);
+ if (!check_undefined.is_undefined()) {
+ SlimeFiller visitor(inserter, false);
+ value.accept(visitor);
+ }
}
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.h b/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.h
index 4367dbcd109..23d20b23c1f 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/summaryfieldconverter.h
@@ -25,7 +25,7 @@ public:
const document::FieldValue& value,
const std::vector<uint32_t>& matching_elems);
- static void insert_summary_field(bool markup, const document::FieldValue& value, vespalib::slime::Inserter& inserter);
+ static void insert_summary_field(const document::FieldValue& value, vespalib::slime::Inserter& inserter);
};
}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.cpp
index c85a4fb5788..dc6d9524ee4 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.cpp
@@ -1,8 +1,11 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "textextractordfw.h"
-#include "tokenizer.h"
#include "docsumstate.h"
+#include "general_result.h"
+#include "tokenizer.h"
+#include "resultconfig.h"
+#include <vespa/vespalib/data/slime/inserter.h>
#include <vespa/log/log.h>
LOG_SETUP(".searchlib.docsummary.textextractordfw");
@@ -31,7 +34,7 @@ TextExtractorDFW::insertField(uint32_t, GeneralResult *gres, GetDocsumsState *,
vespalib::slime::Inserter &target)
{
vespalib::string extracted;
- ResEntry * entry = gres->GetEntryFromEnumValue(_inputFieldEnum);
+ ResEntry * entry = gres->GetPresentEntryFromEnumValue(_inputFieldEnum);
if (entry != nullptr) {
const char * buf = nullptr;
uint32_t buflen = 0;
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.h b/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.h
index 10764e5c21d..3bce2ae5cd7 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.h
+++ b/searchsummary/src/vespa/searchsummary/docsummary/textextractordfw.h
@@ -2,14 +2,16 @@
#pragma once
-#include "docsumfieldwriter.h"
+#include "docsum_field_writer.h"
namespace search::docsummary {
+class ResultConfig;
+
/**
* This is the docsum field writer used to extract the original text from a disk summary on the juniper format.
**/
-class TextExtractorDFW : public IDocsumFieldWriter
+class TextExtractorDFW : public DocsumFieldWriter
{
private:
TextExtractorDFW(const TextExtractorDFW &);
@@ -19,7 +21,7 @@ private:
public:
TextExtractorDFW();
- ~TextExtractorDFW() {}
+ ~TextExtractorDFW() override = default;
bool init(const vespalib::string & fieldName, const vespalib::string & inputField, const ResultConfig & config);
bool IsGenerated() const override { return false; }
void insertField(uint32_t docid, GeneralResult *gres, GetDocsumsState *state,
diff --git a/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp b/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp
index 7e0b96b1d82..fcb56c4a553 100644
--- a/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp
+++ b/storage/src/tests/persistence/common/persistenceproviderwrapper.cpp
@@ -106,11 +106,11 @@ PersistenceProviderWrapper::putAsync(const spi::Bucket& bucket, spi::Timestamp t
}
void
-PersistenceProviderWrapper::removeAsync(const spi::Bucket& bucket, std::vector<TimeStampAndDocumentId> ids,
+PersistenceProviderWrapper::removeAsync(const spi::Bucket& bucket, std::vector<spi::IdAndTimestamp> ids,
spi::OperationComplete::UP onComplete)
{
- for (const TimeStampAndDocumentId & stampedId : ids) {
- LOG_SPI("remove(" << bucket << ", " << stampedId.first << ", " << stampedId.second << ")");
+ for (const auto & stampedId : ids) {
+ LOG_SPI("remove(" << bucket << ", " << stampedId.timestamp << ", " << stampedId.id << ")");
}
CHECK_ERROR_ASYNC(spi::RemoveResult, FAIL_REMOVE, onComplete);
_spi.removeAsync(bucket, std::move(ids), std::move(onComplete));
diff --git a/storage/src/tests/persistence/common/persistenceproviderwrapper.h b/storage/src/tests/persistence/common/persistenceproviderwrapper.h
index 3c93bc91d85..ec7ca70d7c7 100644
--- a/storage/src/tests/persistence/common/persistenceproviderwrapper.h
+++ b/storage/src/tests/persistence/common/persistenceproviderwrapper.h
@@ -106,7 +106,7 @@ public:
spi::BucketIdListResult listBuckets(BucketSpace bucketSpace) const override;
spi::BucketInfoResult getBucketInfo(const spi::Bucket&) const override;
void putAsync(const spi::Bucket&, spi::Timestamp, spi::DocumentSP, spi::OperationComplete::UP) override;
- void removeAsync(const spi::Bucket&, std::vector<TimeStampAndDocumentId> ids, spi::OperationComplete::UP) override;
+ void removeAsync(const spi::Bucket&, std::vector<spi::IdAndTimestamp> ids, spi::OperationComplete::UP) override;
void removeIfFoundAsync(const spi::Bucket&, spi::Timestamp, const spi::DocumentId&, spi::OperationComplete::UP) override;
void updateAsync(const spi::Bucket&, spi::Timestamp, spi::DocumentUpdateSP, spi::OperationComplete::UP) override;
spi::GetResult get(const spi::Bucket&, const document::FieldSet&, const spi::DocumentId&, spi::Context&) const override;
diff --git a/storage/src/vespa/storage/config/stor-communicationmanager.def b/storage/src/vespa/storage/config/stor-communicationmanager.def
index 75a3344b618..3083fb37081 100644
--- a/storage/src/vespa/storage/config/stor-communicationmanager.def
+++ b/storage/src/vespa/storage/config/stor-communicationmanager.def
@@ -38,20 +38,17 @@ mbus.tcp_no_delay bool default=true restart
## Number of threads for network.
mbus.num_network_threads int default=1 restart
-## Number of workers threads for messagebus
-## Any value below 1 will be 1.
-mbus.num_threads int default=4 restart
-
## The number of events in the queue of a network (FNET) thread before it is woken up.
mbus.events_before_wakeup int default=1 restart
## Enable to use above thread pool for encoding replies
## False will use network(fnet) thread
+## Deprecated and void
mbus.dispatch_on_encode bool default=true restart
## Enable to use above thread pool for decoding replies
## False will use network(fnet) thread
-## Todo: Change default once verified in large scale deployment.
+## Deprecated and void
mbus.dispatch_on_decode bool default=true restart
## The number of network (FNET) threads used by the shared rpc resource.
diff --git a/storage/src/vespa/storage/persistence/asynchandler.cpp b/storage/src/vespa/storage/persistence/asynchandler.cpp
index f5d29fb32a7..d5bf733a30c 100644
--- a/storage/src/vespa/storage/persistence/asynchandler.cpp
+++ b/storage/src/vespa/storage/persistence/asynchandler.cpp
@@ -114,13 +114,13 @@ bucketStatesAreSemanticallyEqual(const api::BucketInfo& a, const api::BucketInfo
class UnrevertableRemoveEntryProcessor : public BucketProcessor::EntryProcessor {
public:
- using DocumentIdsAndTimeStamps = std::vector<std::pair<spi::Timestamp, spi::DocumentId>>;
+ using DocumentIdsAndTimeStamps = std::vector<spi::IdAndTimestamp>;
UnrevertableRemoveEntryProcessor(DocumentIdsAndTimeStamps & to_remove)
: _to_remove(to_remove)
{}
void process(spi::DocEntry& entry) override {
- _to_remove.emplace_back(entry.getTimestamp(), *entry.getDocumentId());
+ _to_remove.emplace_back(*entry.getDocumentId(), entry.getTimestamp());
}
private:
DocumentIdsAndTimeStamps & _to_remove;
diff --git a/storage/src/vespa/storage/persistence/mergehandler.cpp b/storage/src/vespa/storage/persistence/mergehandler.cpp
index 012d5c2619d..ae68a694c90 100644
--- a/storage/src/vespa/storage/persistence/mergehandler.cpp
+++ b/storage/src/vespa/storage/persistence/mergehandler.cpp
@@ -522,9 +522,9 @@ MergeHandler::applyDiffEntry(std::shared_ptr<ApplyBucketDiffState> async_results
_clock, _env._metrics.merge_handler_metrics.put_latency);
_spi.putAsync(bucket, timestamp, std::move(doc), std::move(complete));
} else {
- std::vector<spi::PersistenceProvider::TimeStampAndDocumentId> ids;
- ids.emplace_back(timestamp, e._docName);
- auto complete = std::make_unique<ApplyBucketDiffEntryComplete>(std::move(async_results), ids[0].second,
+ std::vector<spi::IdAndTimestamp> ids;
+ ids.emplace_back(document::DocumentId(e._docName), timestamp);
+ auto complete = std::make_unique<ApplyBucketDiffEntryComplete>(std::move(async_results), ids[0].id,
std::move(throttle_token), "remove",
_clock, _env._metrics.merge_handler_metrics.remove_latency);
_spi.removeAsync(bucket, std::move(ids), std::move(complete));
diff --git a/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp b/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp
index 1be9679c641..9e55c0f9088 100644
--- a/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp
+++ b/storage/src/vespa/storage/persistence/provider_error_wrapper.cpp
@@ -152,7 +152,7 @@ ProviderErrorWrapper::putAsync(const spi::Bucket &bucket, spi::Timestamp ts, spi
}
void
-ProviderErrorWrapper::removeAsync(const spi::Bucket &bucket, std::vector<TimeStampAndDocumentId> ids,
+ProviderErrorWrapper::removeAsync(const spi::Bucket &bucket, std::vector<spi::IdAndTimestamp> ids,
spi::OperationComplete::UP onComplete)
{
onComplete->addResultHandler(this);
diff --git a/storage/src/vespa/storage/persistence/provider_error_wrapper.h b/storage/src/vespa/storage/persistence/provider_error_wrapper.h
index 7bd406a8758..82447fe4549 100644
--- a/storage/src/vespa/storage/persistence/provider_error_wrapper.h
+++ b/storage/src/vespa/storage/persistence/provider_error_wrapper.h
@@ -58,7 +58,7 @@ public:
void register_error_listener(std::shared_ptr<ProviderErrorListener> listener);
void putAsync(const spi::Bucket &, spi::Timestamp, spi::DocumentSP, spi::OperationComplete::UP) override;
- void removeAsync(const spi::Bucket&, std::vector<TimeStampAndDocumentId>, spi::OperationComplete::UP) override;
+ void removeAsync(const spi::Bucket&, std::vector<spi::IdAndTimestamp>, spi::OperationComplete::UP) override;
void removeIfFoundAsync(const spi::Bucket&, spi::Timestamp, const document::DocumentId&, spi::OperationComplete::UP) override;
void updateAsync(const spi::Bucket &, spi::Timestamp, spi::DocumentUpdateSP, spi::OperationComplete::UP) override;
void setActiveStateAsync(const spi::Bucket& b, spi::BucketInfo::ActiveState newState, spi::OperationComplete::UP onComplete) override;
diff --git a/storage/src/vespa/storage/storageserver/communicationmanager.cpp b/storage/src/vespa/storage/storageserver/communicationmanager.cpp
index a88c339052d..7fb6685a8b5 100644
--- a/storage/src/vespa/storage/storageserver/communicationmanager.cpp
+++ b/storage/src/vespa/storage/storageserver/communicationmanager.cpp
@@ -352,12 +352,9 @@ void CommunicationManager::configure(std::unique_ptr<CommunicationManagerConfig>
LOG(debug, "setting up slobrok config from id: '%s", _configUri.getConfigId().c_str());
mbus::RPCNetworkParams params(_configUri);
params.setConnectionExpireSecs(config->mbus.rpctargetcache.ttl);
- params.setNumThreads(std::max(1, config->mbus.numThreads));
params.setNumNetworkThreads(std::max(1, config->mbus.numNetworkThreads));
params.setNumRpcTargets(std::max(1, config->mbus.numRpcTargets));
params.events_before_wakeup(std::max(1, config->mbus.eventsBeforeWakeup));
- params.setDispatchOnDecode(config->mbus.dispatchOnDecode);
- params.setDispatchOnEncode(config->mbus.dispatchOnEncode);
params.setTcpNoDelay(config->mbus.tcpNoDelay);
params.setIdentity(mbus::Identity(_component.getIdentity()));
diff --git a/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.cpp b/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.cpp
index ce0706a03fb..fac801550d4 100644
--- a/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.cpp
+++ b/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.cpp
@@ -1,14 +1,16 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vsm/vsm/docsumconfig.h>
-#include <vespa/searchsummary/docsummary/docsumfieldwriter.h>
+#include <vespa/searchsummary/docsummary/copy_dfw.h>
+#include <vespa/searchsummary/docsummary/empty_dfw.h>
#include <vespa/searchsummary/docsummary/matched_elements_filter_dfw.h>
+#include <vespa/searchsummary/docsummary/resultconfig.h>
#include <vespa/searchlib/common/matching_elements_fields.h>
#include <vespa/vsm/config/config-vsmfields.h>
#include <vespa/vsm/config/config-vsmsummary.h>
using search::MatchingElementsFields;
-using search::docsummary::IDocsumFieldWriter;
+using search::docsummary::DocsumFieldWriter;
using search::docsummary::CopyDFW;
using search::docsummary::EmptyDFW;
using search::docsummary::MatchedElementsFilterDFW;
@@ -41,10 +43,10 @@ DynamicDocsumConfig::DynamicDocsumConfig(search::docsummary::IDocsumEnvironment*
{
}
-IDocsumFieldWriter::UP
+std::unique_ptr<DocsumFieldWriter>
DynamicDocsumConfig::createFieldWriter(const string & fieldName, const string & overrideName, const string & argument, bool & rc, std::shared_ptr<search::MatchingElementsFields> matching_elems_fields)
{
- IDocsumFieldWriter::UP fieldWriter;
+ std::unique_ptr<DocsumFieldWriter> fieldWriter;
if ((overrideName == "staticrank") ||
(overrideName == "ranklog") ||
(overrideName == "label") ||
diff --git a/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.h b/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.h
index 11010c04e90..a660c544d7d 100644
--- a/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.h
+++ b/streamingvisitors/src/vespa/vsm/vsm/docsumconfig.h
@@ -20,7 +20,7 @@ private:
public:
DynamicDocsumConfig(search::docsummary::IDocsumEnvironment* env, search::docsummary::DynamicDocsumWriter* writer, std::shared_ptr<VsmfieldsConfig> vsm_fields_config);
private:
- std::unique_ptr<search::docsummary::IDocsumFieldWriter>
+ std::unique_ptr<search::docsummary::DocsumFieldWriter>
createFieldWriter(const string & fieldName, const string & overrideName,
const string & cf, bool & rc, std::shared_ptr<search::MatchingElementsFields> matching_elems_fields) override;
};
diff --git a/streamingvisitors/src/vespa/vsm/vsm/docsumfilter.cpp b/streamingvisitors/src/vespa/vsm/vsm/docsumfilter.cpp
index a5fe08da605..20083b9160e 100644
--- a/streamingvisitors/src/vespa/vsm/vsm/docsumfilter.cpp
+++ b/streamingvisitors/src/vespa/vsm/vsm/docsumfilter.cpp
@@ -134,6 +134,7 @@ public:
DocsumStoreVsmDocument(const document::Document* document);
~DocsumStoreVsmDocument() override;
std::unique_ptr<document::FieldValue> get_field_value(const vespalib::string& field_name) const override;
+ void insert_summary_field(const vespalib::string& field_name, vespalib::slime::Inserter& inserter) const override;
};
DocsumStoreVsmDocument::DocsumStoreVsmDocument(const document::Document* document)
@@ -158,6 +159,15 @@ DocsumStoreVsmDocument::get_field_value(const vespalib::string& field_name) cons
return {};
}
+void
+DocsumStoreVsmDocument::insert_summary_field(const vespalib::string& field_name, vespalib::slime::Inserter& inserter) const
+{
+ auto field_value = get_field_value(field_name);
+ if (field_value) {
+ SummaryFieldConverter::insert_summary_field(*field_value, inserter);
+ }
+}
+
}
FieldPath
diff --git a/streamingvisitors/src/vespa/vsm/vsm/vsm-adapter.cpp b/streamingvisitors/src/vespa/vsm/vsm/vsm-adapter.cpp
index 14f44527887..add52effac2 100644
--- a/streamingvisitors/src/vespa/vsm/vsm/vsm-adapter.cpp
+++ b/streamingvisitors/src/vespa/vsm/vsm/vsm-adapter.cpp
@@ -138,6 +138,12 @@ VSMAdapter::configure(const VSMConfigSnapshot & snapshot)
// init result config
DocsumBlobEntryFilter docsum_blob_entry_filter;
docsum_blob_entry_filter.add_skip(search::docsummary::RES_INT);
+ docsum_blob_entry_filter.add_skip(search::docsummary::RES_SHORT);
+ docsum_blob_entry_filter.add_skip(search::docsummary::RES_BYTE);
+ docsum_blob_entry_filter.add_skip(search::docsummary::RES_FLOAT);
+ docsum_blob_entry_filter.add_skip(search::docsummary::RES_DOUBLE);
+ docsum_blob_entry_filter.add_skip(search::docsummary::RES_INT64);
+ docsum_blob_entry_filter.add_skip(search::docsummary::RES_TENSOR);
std::unique_ptr<ResultConfig> resCfg(new ResultConfig(docsum_blob_entry_filter));
if ( ! resCfg->ReadConfig(*summary.get(), _configId.c_str())) {
throw std::runtime_error("(re-)configuration of VSM (docsum tools) failed due to bad summary config");
diff --git a/vdslib/src/test/java/com/yahoo/vdslib/distribution/DistributionTestFactory.java b/vdslib/src/test/java/com/yahoo/vdslib/distribution/DistributionTestFactory.java
index 084d0e9185d..915f8dd67d5 100644
--- a/vdslib/src/test/java/com/yahoo/vdslib/distribution/DistributionTestFactory.java
+++ b/vdslib/src/test/java/com/yahoo/vdslib/distribution/DistributionTestFactory.java
@@ -108,6 +108,7 @@ public class DistributionTestFactory extends CrossPlatformTestFactory {
}
}
+ @SuppressWarnings("deprecation")
private static StorDistributionConfig.Builder deserializeConfig(String s) {
return new StorDistributionConfig.Builder(
new ConfigGetter<>(StorDistributionConfig.class).getConfig("raw:" + s));
diff --git a/vespa-hadoop/pom.xml b/vespa-hadoop/pom.xml
index 8060a2d67a1..4dd0ade7499 100644
--- a/vespa-hadoop/pom.xml
+++ b/vespa-hadoop/pom.xml
@@ -220,19 +220,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-source-plugin</artifactId>
- <executions>
- <execution>
- <id>attach-sources</id>
- <goals>
- <goal>jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>${vespaClients.jdk.releaseVersion}</release>
diff --git a/vespa-osgi-testrunner/pom.xml b/vespa-osgi-testrunner/pom.xml
index ebb1240a198..0032fe33de0 100644
--- a/vespa-osgi-testrunner/pom.xml
+++ b/vespa-osgi-testrunner/pom.xml
@@ -80,6 +80,11 @@
<artifactId>org.apache.felix.framework</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java
index 31dbd939991..ef4b402d33b 100644
--- a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java
+++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java
@@ -1,6 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.testrunner;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.yahoo.component.annotation.Inject;
import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.container.jdisc.EmptyResponse;
@@ -9,10 +11,6 @@ import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
import com.yahoo.exception.ExceptionUtils;
import com.yahoo.restapi.MessageResponse;
-import com.yahoo.restapi.SlimeJsonResponse;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Slime;
-import com.yahoo.vespa.testrunner.TestReport.ContainerNode;
import com.yahoo.vespa.testrunner.TestReport.FailureNode;
import com.yahoo.vespa.testrunner.TestReport.NamedNode;
import com.yahoo.vespa.testrunner.TestReport.Node;
@@ -22,6 +20,7 @@ import com.yahoo.yolean.Exceptions;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintStream;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
@@ -43,6 +42,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
public class TestRunnerHandler extends ThreadedHttpRequestHandler {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
+ private static final JsonFactory factory = new JsonFactory();
private final TestRunner testRunner;
@@ -80,15 +80,13 @@ public class TestRunnerHandler extends ThreadedHttpRequestHandler {
long fetchRecordsAfter = Optional.ofNullable(request.getProperty("after"))
.map(Long::parseLong)
.orElse(-1L);
- return new SlimeJsonResponse(logToSlime(testRunner.getLog(fetchRecordsAfter)));
+ return new CustomJsonResponse(out -> render(out, testRunner.getLog(fetchRecordsAfter)));
case "/tester/v1/status":
return new MessageResponse(testRunner.getStatus().name());
case "/tester/v1/report":
TestReport report = testRunner.getReport();
- if (report == null)
- return new EmptyResponse(200);
-
- return new SlimeJsonResponse(toSlime(report));
+ if (report == null) return new EmptyResponse(204);
+ else return new CustomJsonResponse(out -> render(out, report));
}
return new MessageResponse(Status.NOT_FOUND, "Not found: " + request.getUri().getPath());
}
@@ -114,31 +112,30 @@ public class TestRunnerHandler extends ThreadedHttpRequestHandler {
return path.substring(lastSlash + 1);
}
- static Slime logToSlime(Collection<LogRecord> log) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- Cursor recordArray = root.setArray("logRecords");
- logArrayToSlime(recordArray, log);
- return slime;
- }
-
- static void logArrayToSlime(Cursor recordArray, Collection<LogRecord> log) {
- log.forEach(record -> {
- Cursor recordObject = recordArray.addObject();
- recordObject.setLong("id", record.getSequenceNumber());
- recordObject.setLong("at", record.getMillis());
- recordObject.setString("type", typeOf(record.getLevel()));
+ private static void render(OutputStream out, Collection<LogRecord> log) throws IOException {
+ var json = factory.createGenerator(out);
+ json.writeStartObject();
+ json.writeArrayFieldStart("logRecords");
+ for (LogRecord record : log) {
String message = record.getMessage() == null ? "" : record.getMessage();
if (record.getThrown() != null) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
record.getThrown().printStackTrace(new PrintStream(buffer));
message += (message.isEmpty() ? "" : "\n") + buffer;
}
- recordObject.setString("message", message);
- });
+ json.writeStartObject();
+ json.writeNumberField("id", record.getSequenceNumber());
+ json.writeNumberField("at", record.getMillis());
+ json.writeStringField("type", typeOf(record.getLevel()));
+ json.writeStringField("message", message);
+ json.writeEndObject();
+ }
+ json.writeEndArray();
+ json.writeEndObject();
+ json.close();
}
- public static String typeOf(Level level) {
+ private static String typeOf(Level level) {
return level.getName().equals("html") ? "html"
: level.intValue() < Level.INFO.intValue() ? "debug"
: level.intValue() < Level.WARNING.intValue() ? "info"
@@ -146,72 +143,105 @@ public class TestRunnerHandler extends ThreadedHttpRequestHandler {
: "error";
}
- private static Slime toSlime(TestReport report) {
- var slime = new Slime();
- var root = slime.setObject();
+ private static void render(OutputStream out, TestReport report) throws IOException {
+ JsonGenerator json = factory.createGenerator(out);
+ json.writeStartObject();
+
+ json.writeFieldName("report");
+ render(json, (Node) report.root());
+
+ // TODO jonmv: remove
+ json.writeObjectFieldStart("summary");
+
+ renderSummary(json, report);
- toSlime(root.setObject("report"), (Node) report.root());
+ json.writeArrayFieldStart("failures");
+ renderFailures(json, report.root());
+ json.writeEndArray();
+
+ json.writeEndObject();
// TODO jonmv: remove
- Map<TestReport.Status, Long> tally = report.root().tally();
- var summary = root.setObject("summary");
- summary.setLong("success", tally.getOrDefault(TestReport.Status.successful, 0L));
- summary.setLong("failed", tally.getOrDefault(TestReport.Status.failed, 0L) + tally.getOrDefault(TestReport.Status.error, 0L));
- summary.setLong("ignored", tally.getOrDefault(TestReport.Status.skipped, 0L));
- summary.setLong("aborted", tally.getOrDefault(TestReport.Status.aborted, 0L));
- summary.setLong("inconclusive", tally.getOrDefault(TestReport.Status.inconclusive, 0L));
- toSlime(summary.setArray("failures"), root.setArray("output"), report.root());
+ json.writeArrayFieldStart("output");
+ renderOutput(json, report.root());
+ json.writeEndArray();
- return slime;
+ json.writeEndObject();
+ json.close();
}
- static void toSlime(Cursor failuresArray, Cursor outputArray, Node node) {
- for (Node child : node.children())
- TestRunnerHandler.toSlime(failuresArray, outputArray, child);
+ private static void renderSummary(JsonGenerator json, TestReport report) throws IOException {
+ Map<TestReport.Status, Long> tally = report.root().tally();
+ json.writeNumberField("success", tally.getOrDefault(TestReport.Status.successful, 0L));
+ json.writeNumberField("failed", tally.getOrDefault(TestReport.Status.failed, 0L) + tally.getOrDefault(TestReport.Status.error, 0L));
+ json.writeNumberField("ignored", tally.getOrDefault(TestReport.Status.skipped, 0L));
+ json.writeNumberField("aborted", tally.getOrDefault(TestReport.Status.aborted, 0L));
+ json.writeNumberField("inconclusive", tally.getOrDefault(TestReport.Status.inconclusive, 0L));
+ }
+ private static void renderFailures(JsonGenerator json, Node node) throws IOException {
if (node instanceof FailureNode) {
- Cursor failureObject = failuresArray.addObject();
- failureObject.setString("testName", node.parent.name());
- failureObject.setString("testError", ((FailureNode) node).thrown().getMessage());
- failureObject.setString("exception", ExceptionUtils.getStackTraceAsString(((FailureNode) node).thrown()));
+ json.writeStartObject();
+ json.writeStringField("testName", node.parent.name());
+ json.writeStringField("testError", ((FailureNode) node).thrown().getMessage());
+ json.writeStringField("exception", ExceptionUtils.getStackTraceAsString(((FailureNode) node).thrown()));
+ json.writeEndObject();
}
- if (node instanceof OutputNode)
+ else {
+ for (Node child : node.children())
+ renderFailures(json, child);
+ }
+ }
+
+ private static void renderOutput(JsonGenerator json, Node node) throws IOException {
+ if (node instanceof OutputNode) {
for (LogRecord record : ((OutputNode) node).log())
if (record.getMessage() != null)
- outputArray.addString(formatter.format(record.getInstant().atOffset(ZoneOffset.UTC)) + " " + record.getMessage());
+ json.writeString(formatter.format(record.getInstant().atOffset(ZoneOffset.UTC)) + " " + record.getMessage());
+ }
+ else {
+ for (Node child : node.children())
+ renderOutput(json, child);
+ }
}
- static void toSlime(Cursor nodeObject, Node node) {
- if (node instanceof NamedNode) toSlime(nodeObject, (NamedNode) node);
- if (node instanceof OutputNode) toSlime(nodeObject, (OutputNode) node);
+ private static void render(JsonGenerator json, Node node) throws IOException {
+ json.writeStartObject();
+ if (node instanceof NamedNode) render(json, (NamedNode) node);
+ if (node instanceof OutputNode) render(json, (OutputNode) node);
if ( ! node.children().isEmpty()) {
- Cursor childrenArray = nodeObject.setArray("children");
- for (Node child : node.children)
- toSlime(childrenArray.addObject(), child);
+ json.writeArrayFieldStart("children");
+ for (Node child : node.children) {
+ render(json, child);
+ }
+ json.writeEndArray();
}
+ json.writeEndObject();
}
- static void toSlime(Cursor nodeObject, NamedNode node) {
+ private static void render(JsonGenerator json, NamedNode node) throws IOException {
String type = node instanceof FailureNode ? "failure" : node instanceof TestNode ? "test" : "container";
- nodeObject.setString("type", type);
- nodeObject.setString("name", node.name());
- nodeObject.setString("status", node.status().name());
- nodeObject.setLong("start", node.start().toEpochMilli());
- nodeObject.setLong("duration", node.duration().toMillis());
+ json.writeStringField("type", type);
+ json.writeStringField("name", node.name());
+ json.writeStringField("status", node.status().name());
+ json.writeNumberField("start", node.start().toEpochMilli());
+ json.writeNumberField("duration", node.duration().toMillis());
}
- static void toSlime(Cursor nodeObject, OutputNode node) {
- nodeObject.setString("type", "output");
- Cursor childrenArray = nodeObject.setArray("children");
+ private static void render(JsonGenerator json, OutputNode node) throws IOException {
+ json.writeStringField("type", "output");
+ json.writeArrayFieldStart("children");
for (LogRecord record : node.log()) {
- Cursor recordObject = childrenArray.addObject();
- recordObject.setString("message", (record.getLoggerName() == null ? "" : record.getLoggerName() + ": ") +
- (record.getMessage() != null ? record.getMessage() : "") +
- (record.getThrown() != null ? (record.getMessage() != null ? "\n" : "") + traceToString(record.getThrown()) : ""));
- recordObject.setLong("at", record.getInstant().toEpochMilli());
- recordObject.setString("level", typeOf(record.getLevel()));
+ json.writeStartObject();
+ json.writeStringField("message", (record.getLoggerName() == null ? "" : record.getLoggerName() + ": ") +
+ (record.getMessage() != null ? record.getMessage() : "") +
+ (record.getThrown() != null ? (record.getMessage() != null ? "\n" : "") + traceToString(record.getThrown()) : ""));
+ json.writeNumberField("at", record.getInstant().toEpochMilli());
+ json.writeStringField("level", typeOf(record.getLevel()));
+ json.writeEndObject();
}
+ json.writeEndArray();
}
private static String traceToString(Throwable thrown) {
@@ -220,4 +250,36 @@ public class TestRunnerHandler extends ThreadedHttpRequestHandler {
return buffer.toString(UTF_8);
}
+ private interface Renderer {
+
+ void render(OutputStream out) throws IOException;
+
+ }
+
+ private static class CustomJsonResponse extends HttpResponse {
+
+ private final Renderer renderer;
+
+ private CustomJsonResponse(Renderer renderer) {
+ super(200);
+ this.renderer = renderer;
+ }
+
+ @Override
+ public void render(OutputStream outputStream) throws IOException {
+ renderer.render(outputStream);
+ }
+
+ @Override
+ public String getContentType() {
+ return "application/json";
+ }
+
+ @Override
+ public long maxPendingBytes() {
+ return 1 << 25; // 32MB
+ }
+
+ }
+
}
diff --git a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/test/samples/SampleTest.java b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/test/samples/SampleTest.java
index bc878353d4b..b0e5119c06e 100644
--- a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/test/samples/SampleTest.java
+++ b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/test/samples/SampleTest.java
@@ -71,7 +71,7 @@ public class SampleTest {
@Test
void successful() {
log.log(new Level("html", INFO.intValue()) { }, "<body />");
- log.log(INFO, "Very informative");
+ log.log(INFO, "Very informative: \"\\n\": \n");
log.log(WARNING, "Oh no", new IllegalArgumentException("error", new RuntimeException("wrapped")));
}
diff --git a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java
index 5ce737d7649..6d6fbbf2cf1 100644
--- a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java
+++ b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java
@@ -27,7 +27,6 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
-import java.util.logging.Level;
import java.util.logging.LogRecord;
import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
@@ -136,14 +135,6 @@ class TestRunnerHandlerTest {
}
}
- /* Creates a LogRecord that has a known instant and sequence number to get predictable serialization results. */
- private static LogRecord logRecord(String logMessage) {
- LogRecord logRecord = new LogRecord(Level.INFO, logMessage);
- logRecord.setInstant(testInstant);
- logRecord.setSequenceNumber(0);
- return logRecord;
- }
-
private static class MockRunner implements TestRunner {
private final TestRunner.Status status;
diff --git a/vespa-osgi-testrunner/src/test/resources/output.json b/vespa-osgi-testrunner/src/test/resources/output.json
index 847ae5800e9..2b4aa9e5599 100644
--- a/vespa-osgi-testrunner/src/test/resources/output.json
+++ b/vespa-osgi-testrunner/src/test/resources/output.json
@@ -1,133 +1,133 @@
{
"logRecords": [
{
- "id": 18,
+ "id": 2,
"at": 0,
"type": "info",
"message": "spam"
},
{
- "id": 21,
+ "id": 5,
"at": 0,
"type": "info",
"message": "spam"
},
{
- "id": 22,
+ "id": 6,
"at": 0,
"type": "error",
"message": "java.lang.NoClassDefFoundError\n\tat com.yahoo.vespa.test.samples.SampleTest.error(SampleTest.java:87)\n"
},
{
- "id": 25,
+ "id": 9,
"at": 0,
"type": "info",
"message": "spam"
},
{
- "id": 26,
+ "id": 10,
"at": 0,
"type": "info",
"message": "I have a bad feeling about this"
},
{
- "id": 27,
+ "id": 11,
"at": 0,
"type": "error",
"message": "org.opentest4j.AssertionFailedError: baz ==> expected: <foo> but was: <bar>\n\tat org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)\n\tat org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)\n\tat org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)\n\tat org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1152)\n\tat com.yahoo.vespa.test.samples.SampleTest.failing(SampleTest.java:81)\n"
},
{
- "id": 31,
+ "id": 15,
"at": 0,
"type": "info",
"message": "spam"
},
{
- "id": 32,
+ "id": 16,
"at": 0,
"type": "info",
"message": "I'm here with Erwin today; Erwin, what can you tell us about your cat?"
},
{
- "id": 33,
+ "id": 17,
"at": 0,
"type": "warning",
"message": "ai.vespa.hosted.cd.InconclusiveTestException: the cat is both dead _and_ alive\n\tat com.yahoo.vespa.test.samples.SampleTest.inconclusive(SampleTest.java:93)\n"
},
{
- "id": 36,
+ "id": 20,
"at": 0,
"type": "info",
"message": "spam"
},
{
- "id": 37,
+ "id": 21,
"at": 0,
"type": "info",
"message": "<body />"
},
{
- "id": 38,
+ "id": 22,
"at": 0,
"type": "info",
- "message": "Very informative"
+ "message": "Very informative: \"\\n\": \n"
},
{
- "id": 39,
+ "id": 23,
"at": 0,
"type": "warning",
"message": "Oh no\njava.lang.IllegalArgumentException: error\n\tat com.yahoo.vespa.test.samples.SampleTest.successful(SampleTest.java:75)\nCaused by: java.lang.RuntimeException: wrapped\n\t... 1 more\n"
},
{
- "id": 43,
+ "id": 27,
"at": 0,
"type": "info",
"message": "spam"
},
{
- "id": 46,
+ "id": 30,
"at": 0,
"type": "info",
"message": "Catch me if you can!"
},
{
- "id": 50,
+ "id": 34,
"at": 0,
"type": "error",
"message": "org.opentest4j.AssertionFailedError: no charm\n\tat org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:39)\n\tat org.junit.jupiter.api.Assertions.fail(Assertions.java:134)\n\tat com.yahoo.vespa.test.samples.SampleTest$Inner.lambda$others$1(SampleTest.java:105)\n"
},
{
- "id": 54,
+ "id": 38,
"at": 0,
"type": "info",
"message": "spam"
},
{
- "id": 2,
+ "id": 67,
"at": 0,
"type": "error",
"message": "org.opentest4j.AssertionFailedError\n\tat org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:35)\n\tat org.junit.jupiter.api.Assertions.fail(Assertions.java:115)\n\tat com.yahoo.vespa.test.samples.FailingTestAndBothAftersTest.test(FailingTestAndBothAftersTest.java:19)\n\tSuppressed: java.lang.RuntimeException\n\t\tat com.yahoo.vespa.test.samples.FailingTestAndBothAftersTest.moreFail(FailingTestAndBothAftersTest.java:16)\n"
},
{
- "id": 4,
+ "id": 69,
"at": 0,
"type": "error",
"message": "java.lang.RuntimeException\n\tat com.yahoo.vespa.test.samples.FailingTestAndBothAftersTest.fail(FailingTestAndBothAftersTest.java:13)\n"
},
{
- "id": 7,
+ "id": 72,
"at": 0,
"type": "error",
"message": "org.junit.platform.commons.JUnitException: @BeforeAll method 'void com.yahoo.vespa.test.samples.WrongBeforeAllTest.wrong()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).\n"
},
{
- "id": 11,
+ "id": 76,
"at": 0,
"type": "error",
"message": "java.lang.NullPointerException\n\tat com.yahoo.vespa.test.samples.FailingExtensionTest$FailingExtension.<init>(FailingExtensionTest.java:19)\n"
},
{
- "id": 15,
+ "id": 80,
"at": 12000,
"type": "error",
"message": "java.lang.ClassNotFoundException: School's out all summer!\n"
diff --git a/vespa-osgi-testrunner/src/test/resources/report.json b/vespa-osgi-testrunner/src/test/resources/report.json
index fa76c222f93..443694e2e0c 100644
--- a/vespa-osgi-testrunner/src/test/resources/report.json
+++ b/vespa-osgi-testrunner/src/test/resources/report.json
@@ -191,7 +191,7 @@
"level": "info"
},
{
- "message": "com.yahoo.vespa.test.samples.SampleTest: Very informative",
+ "message": "com.yahoo.vespa.test.samples.SampleTest: Very informative: \"\\n\": \n",
"at": 0,
"level": "info"
},
@@ -546,7 +546,7 @@
"00:00:00.000 I'm here with Erwin today; Erwin, what can you tell us about your cat?",
"00:00:00.000 spam",
"00:00:00.000 <body />",
- "00:00:00.000 Very informative",
+ "00:00:00.000 Very informative: \"\\n\": \n",
"00:00:00.000 Oh no",
"00:00:00.000 spam",
"00:00:00.000 Catch me if you can!",
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java
index f3ea8fb5a80..2fc34112517 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java
@@ -25,7 +25,7 @@ public class FeedHandlerTest {
FeedHandler handler = new FeedHandler(
new RejectingContainerThreadpool(),
new CollectingMetric(),
- new DocumentTypeManager(new DocumentmanagerConfig.Builder().enablecompression(true).build()),
+ new DocumentTypeManager(new DocumentmanagerConfig.Builder().build()),
null /* session cache */,
MetricReceiver.nullImplementation);
var responseHandler = new RequestHandlerTestDriver.MockResponseHandler();
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java
index dcabc1f338e..5b8b5b1827f 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java
@@ -108,7 +108,7 @@ public class FeedHandlerV3Test {
}
private FeedHandlerV3 setupFeederHandler(Executor threadPool) {
- DocumentTypeManager docMan = new DocumentTypeManager(new DocumentmanagerConfig.Builder().enablecompression(true).build());
+ DocumentTypeManager docMan = new DocumentTypeManager(new DocumentmanagerConfig.Builder().build());
FeedHandlerV3 feedHandlerV3 = new FeedHandlerV3(
threadPool,
metric,
diff --git a/vespaclient-core/src/main/java/com/yahoo/vespaclient/ClusterList.java b/vespaclient-core/src/main/java/com/yahoo/vespaclient/ClusterList.java
index d60463df581..c42d48b4821 100644
--- a/vespaclient-core/src/main/java/com/yahoo/vespaclient/ClusterList.java
+++ b/vespaclient-core/src/main/java/com/yahoo/vespaclient/ClusterList.java
@@ -20,6 +20,7 @@ public class ClusterList {
this.contentClusters = List.copyOf(contentClusters);
}
+ @SuppressWarnings("deprecation")
public ClusterList(String configId) {
this(new ConfigGetter<>(ClusterListConfig.class).getConfig(configId));
}