aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--application-model/src/main/java/com/yahoo/vespa/applicationmodel/ApplicationInstance.java28
-rw-r--r--config-model-api/abi-spec.json3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java1
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java5
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java8
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java2
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java34
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java22
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java24
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java37
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java69
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java3
-rw-r--r--config-model/src/main/resources/schema/admin.rnc14
-rw-r--r--config-model/src/test/derived/fieldset2/index-info.cfg4
-rw-r--r--config-model/src/test/derived/indexschema/index-info.cfg2
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java13
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java23
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java5
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/TelegrafTest.java12
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java101
-rw-r--r--config-model/src/test/schema-test-files/services.xml9
-rw-r--r--config-provisioning/abi-spec.json3
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java14
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java31
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java5
-rw-r--r--config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java25
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java10
-rw-r--r--configserver/src/main/resources/configserver-app/services.xml3
-rw-r--r--container-disc/src/main/ssl/jdisc_container.keydb11
-rw-r--r--container-search/abi-spec.json4
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/Index.java6
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/IndexModel.java1
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java18
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java1
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java33
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/parser/SimpleParser.java52
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/parser/StructuredParser.java17
-rw-r--r--container-search/src/main/java/com/yahoo/search/Query.java4
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcClient.java10
-rw-r--r--container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java14
-rw-r--r--container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/yql/YqlParser.java10
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java9
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParseTestCase.java357
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/querytransform/test/CJKSearcherTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/querytransform/test/LiteralBoostSearcherTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/searcher/test/FieldCollapsingSearcherTestCase.java264
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/semantics/test/SegmentSubstitutionTestCase.java8
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java6
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java30
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchers/test/InputCheckingSearcherTestCase.java50
-rw-r--r--container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java57
-rw-r--r--container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java3
-rw-r--r--container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/DeploymentData.java63
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java16
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingEndpoint.java1
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGenerator.java1
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGeneratorMock.java22
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java96
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java166
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterCost.java95
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterInfo.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilization.java63
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentCost.java61
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java64
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java77
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java53
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java34
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java31
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java35
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializer.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java91
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java35
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java178
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java34
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java112
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java24
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java89
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterCostTest.java35
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilizationTest.java29
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentCostTest.java38
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java35
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java112
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java65
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java42
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java61
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java90
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java11
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java31
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json29
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json17
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json82
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json82
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json22
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json42
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json42
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java26
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java30
-rw-r--r--dist/vespa.spec3
-rw-r--r--docker-api/CMakeLists.txt4
-rw-r--r--eval/src/tests/ann/bruteforce-nns.h4
-rw-r--r--eval/src/tests/ann/gist_benchmark.cpp1
-rw-r--r--eval/src/tests/ann/sift_benchmark.cpp1
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java23
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java44
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java9
-rw-r--r--metrics-proxy/src/main/resources/configdefinitions/telegraf.def1
-rw-r--r--node-admin/CMakeLists.txt4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java17
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java15
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java9
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java9
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java12
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java22
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java7
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java22
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java16
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java4
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/InstanceLookupService.java20
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java95
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorUtil.java23
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/ServiceMonitorInstanceLookupService.java45
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiFactory.java6
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImpl.java14
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/HostedVespaPolicy.java6
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java5
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/resources/InstanceResource.java19
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ApplicationLock.java36
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/HostInfosServiceImpl.java128
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java38
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java26
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkApplicationLock.java114
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkStatusService.java237
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java418
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyServiceMonitor.java (renamed from orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyInstanceLookupService.java)41
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java92
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorUtilTest.java4
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/ClusterControllerClientFactoryMock.java6
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/model/ModelTestUtils.java17
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionResourceTest.java4
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java44
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZkStatusService2Test.java (renamed from orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService2Test.java)12
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZkStatusServiceTest.java (renamed from orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusServiceTest.java)69
-rw-r--r--parent/pom.xml2
-rw-r--r--searchcore/src/vespa/searchcore/config/proton.def4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/threading_service_config.cpp20
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/threading_service_config.h12
-rw-r--r--searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp2
-rw-r--r--searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp2
-rw-r--r--searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_benchmark.cpp8
-rw-r--r--searchlib/src/tests/query/query_visitor_test.cpp2
-rw-r--r--searchlib/src/tests/query/querybuilder_test.cpp7
-rw-r--r--searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp35
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.cpp10
-rw-r--r--searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.h8
-rw-r--r--searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp29
-rw-r--r--searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.h24
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/querynode.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/querybuilder.h12
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/queryreplicator.h3
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/simplequery.h6
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h25
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/termnodes.h11
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp11
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h4
-rw-r--r--searchlib/src/vespa/searchlib/tensor/CMakeLists.txt1
-rw-r--r--searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.cpp15
-rw-r--r--searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.h35
-rw-r--r--searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp4
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java102
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java14
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java3
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java84
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java32
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelCache.java13
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java73
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceMonitorImpl.java49
-rw-r--r--service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceMonitor.java32
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelManagerTest.java2
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java70
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java5
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelCacheTest.java20
-rw-r--r--service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelProviderTest.java4
-rw-r--r--staging_vespalib/CMakeLists.txt1
-rw-r--r--staging_vespalib/src/tests/singleexecutor/CMakeLists.txt8
-rw-r--r--staging_vespalib/src/tests/singleexecutor/singleexecutor_test.cpp80
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt1
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/singleexecutor.cpp131
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/singleexecutor.h55
-rw-r--r--standalone-container/CMakeLists.txt1
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java3
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java20
-rw-r--r--vespajlib/src/main/java/com/yahoo/collections/AbstractFilteringList.java10
-rw-r--r--vespalib/src/vespa/vespalib/util/executor.h2
255 files changed, 3898 insertions, 3141 deletions
diff --git a/application-model/src/main/java/com/yahoo/vespa/applicationmodel/ApplicationInstance.java b/application-model/src/main/java/com/yahoo/vespa/applicationmodel/ApplicationInstance.java
index 3363ddb040f..1d230d6cb47 100644
--- a/application-model/src/main/java/com/yahoo/vespa/applicationmodel/ApplicationInstance.java
+++ b/application-model/src/main/java/com/yahoo/vespa/applicationmodel/ApplicationInstance.java
@@ -11,24 +11,28 @@ import java.util.Set;
*/
public class ApplicationInstance {
- private final TenantId tenantId;
- private final ApplicationInstanceId applicationInstanceId;
+ private final ApplicationInstanceReference reference;
private final Set<ServiceCluster> serviceClusters;
- public ApplicationInstance(TenantId tenantId, ApplicationInstanceId applicationInstanceId, Set<ServiceCluster> serviceClusters) {
- this.tenantId = tenantId;
- this.applicationInstanceId = applicationInstanceId;
+ public ApplicationInstance(TenantId tenantId,
+ ApplicationInstanceId applicationInstanceId,
+ Set<ServiceCluster> serviceClusters) {
+ this(new ApplicationInstanceReference(tenantId, applicationInstanceId), serviceClusters);
+ }
+
+ public ApplicationInstance(ApplicationInstanceReference reference, Set<ServiceCluster> serviceClusters) {
+ this.reference = reference;
this.serviceClusters = serviceClusters;
}
@JsonProperty("tenantId")
public TenantId tenantId() {
- return tenantId;
+ return reference.tenantId();
}
@JsonProperty("applicationInstanceId")
public ApplicationInstanceId applicationInstanceId() {
- return applicationInstanceId;
+ return reference.applicationInstanceId();
}
@JsonProperty("serviceClusters")
@@ -38,7 +42,7 @@ public class ApplicationInstance {
@JsonProperty("reference")
public ApplicationInstanceReference reference() {
- return new ApplicationInstanceReference(tenantId, applicationInstanceId);
+ return reference;
}
@Override
@@ -46,21 +50,19 @@ public class ApplicationInstance {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ApplicationInstance that = (ApplicationInstance) o;
- return Objects.equals(tenantId, that.tenantId) &&
- Objects.equals(applicationInstanceId, that.applicationInstanceId) &&
+ return Objects.equals(reference, that.reference) &&
Objects.equals(serviceClusters, that.serviceClusters);
}
@Override
public int hashCode() {
- return Objects.hash(tenantId, applicationInstanceId, serviceClusters);
+ return Objects.hash(reference, serviceClusters);
}
@Override
public String toString() {
return "ApplicationInstance{" +
- "tenantId=" + tenantId +
- ", applicationInstanceId=" + applicationInstanceId +
+ "reference=" + reference +
", serviceClusters=" + serviceClusters +
'}';
}
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json
index 90132d6924a..40a62026253 100644
--- a/config-model-api/abi-spec.json
+++ b/config-model-api/abi-spec.json
@@ -880,7 +880,8 @@
"public java.util.Optional endpointCertificateSecrets()",
"public abstract double defaultTermwiseLimit()",
"public abstract boolean useBucketSpaceMetric()",
- "public boolean useNewAthenzFilter()"
+ "public boolean useNewAthenzFilter()",
+ "public boolean usePhraseSegmenting()"
],
"fields": []
},
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 2410de55f86..9aad6361b9a 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
@@ -60,6 +60,7 @@ public interface ModelContext {
double defaultTermwiseLimit();
boolean useBucketSpaceMetric();
default boolean useNewAthenzFilter() { return false; }
+ default boolean usePhraseSegmenting() { return false; }
}
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java b/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
index a9677d4b34c..4cd0c1815dd 100644
--- a/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
+++ b/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
@@ -133,6 +133,11 @@ public final class XmlHelper {
return Optional.ofNullable(element.getAttribute(name)).filter(s -> !s.isEmpty());
}
+ public static Optional<Element> getOptionalChild(Element parent, String childName) {
+ return Optional.ofNullable(XML.getChild(parent, childName));
+
+ }
+
public static Optional<String> getOptionalChildValue(Element parent, String childName) {
Element child = XML.getChild(parent, childName);
if (child == null) return Optional.empty();
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
index 0b4562ecd5c..2e4cdd706f7 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
@@ -42,7 +42,7 @@ public class TestProperties implements ModelContext.Properties {
private double defaultTermwiseLimit = 1.0;
private Optional<EndpointCertificateSecrets> endpointCertificateSecrets = Optional.empty();
private boolean useNewAthenzFilter = false;
-
+ private boolean usePhraseSegmenting = false;
@Override public boolean multitenant() { return multitenant; }
@Override public ApplicationId applicationId() { return applicationId; }
@@ -63,6 +63,7 @@ public class TestProperties implements ModelContext.Properties {
@Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; }
@Override public boolean useBucketSpaceMetric() { return true; }
@Override public boolean useNewAthenzFilter() { return useNewAthenzFilter; }
+ @Override public boolean usePhraseSegmenting() { return usePhraseSegmenting; }
public TestProperties setDefaultTermwiseLimit(double limit) {
defaultTermwiseLimit = limit;
@@ -114,6 +115,11 @@ public class TestProperties implements ModelContext.Properties {
return this;
}
+ public TestProperties setUsePhraseSegmenting(boolean phraseSegmenting) {
+ this.usePhraseSegmenting = phraseSegmenting;
+ return this;
+ }
+
public static class Spec implements ConfigServerSpec {
private final String hostName;
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
index fc8710fa1a1..b16f8c0e5bb 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java
@@ -87,7 +87,7 @@ public class DerivedConfiguration {
juniperrc = new Juniperrc(search);
rankProfileList = new RankProfileList(search, search.rankingConstants(), attributeFields, rankProfileRegistry, queryProfiles, importedModels, deployProperties);
indexingScript = new IndexingScript(search);
- indexInfo = new IndexInfo(search);
+ indexInfo = new IndexInfo(search, deployProperties);
indexSchema = new IndexSchema(search);
importedFields = new ImportedFields(search);
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
index 032f7f58e2a..9ae72badf1c 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/IndexInfo.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.derived;
+import com.yahoo.config.model.api.ModelContext;
import com.yahoo.document.*;
import com.yahoo.searchdefinition.Index;
import com.yahoo.searchdefinition.Search;
@@ -11,6 +12,7 @@ import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.search.config.IndexInfoConfig;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -36,13 +38,16 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
private static final String CMD_FAST_SEARCH = "fast-search";
private static final String CMD_PREDICATE_BOUNDS = "predicate-bounds";
private static final String CMD_NUMERICAL = "numerical";
+ private static final String CMD_PHRASE_SEGMENTING = "phrase-segmenting";
private Set<IndexCommand> commands = new java.util.LinkedHashSet<>();
private Map<String, String> aliases = new java.util.LinkedHashMap<>();
private Map<String, FieldSet> fieldSets;
private Search search;
+ private final boolean phraseSegmenting;
- public IndexInfo(Search search) {
+ public IndexInfo(Search search, ModelContext.Properties deployProperties) {
this.fieldSets = search.fieldSets().userFieldSets();
+ this.phraseSegmenting = deployProperties.usePhraseSegmenting();
addIndexCommand("sddocname", CMD_INDEX);
addIndexCommand("sddocname", CMD_WORD);
derive(search);
@@ -153,6 +158,10 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
addIndexCommand(field, CMD_NUMERICAL);
}
+ if (phraseSegmenting) {
+ addIndexCommand(field, CMD_PHRASE_SEGMENTING);
+ }
+
// Explicit commands
for (String command : field.getQueryCommands()) {
addIndexCommand(field, command);
@@ -293,6 +302,7 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
boolean anyLowerCasing = false;
boolean anyStemming = false;
boolean anyNormalizing = false;
+ String phraseSegmentingCommand = null;
String stemmingCommand = null;
Matching fieldSetMatching = fieldSet.getMatching(); // null if no explicit matching
// First a pass over the fields to read some params to decide field settings implicitly:
@@ -313,8 +323,13 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
if (field.getNormalizing().doRemoveAccents()) {
anyNormalizing = true;
}
- if (fieldSetMatching == null && field.getMatching().getType() != Matching.defaultType)
+ if (fieldSetMatching == null && field.getMatching().getType() != Matching.defaultType) {
fieldSetMatching = field.getMatching();
+ }
+ Optional<String> explicitPhraseSegmentingCommand = field.getQueryCommands().stream().filter(c -> c.startsWith(CMD_PHRASE_SEGMENTING)).findFirst();
+ if (explicitPhraseSegmentingCommand.isPresent()) {
+ phraseSegmentingCommand = explicitPhraseSegmentingCommand.get();
+ }
}
if (anyIndexing && anyAttributing && fieldSet.getMatching() == null) {
// We have both attributes and indexes and no explicit match setting ->
@@ -357,6 +372,11 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
new IndexInfoConfig.Indexinfo.Command.Builder()
.indexname(fieldSet.getName())
.command(CMD_NORMALIZE));
+ if (phraseSegmentingCommand != null)
+ iiB.command(
+ new IndexInfoConfig.Indexinfo.Command.Builder()
+ .indexname(fieldSet.getName())
+ .command(phraseSegmentingCommand));
}
} else {
// Assume only attribute fields
@@ -392,9 +412,15 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer {
} else if (fieldSetMatching.getType().equals(Matching.Type.TEXT)) {
}
-
}
-
+
+ if (phraseSegmentingCommand == null
+ && fieldSet.queryCommands().stream().noneMatch(c -> c.startsWith(CMD_PHRASE_SEGMENTING))) { // use default
+ if (phraseSegmenting)
+ iiB.command(new IndexInfoConfig.Indexinfo.Command.Builder()
+ .indexname(fieldSet.getName())
+ .command(CMD_PHRASE_SEGMENTING));
+ }
}
private boolean hasMultiValueField(FieldSet fieldSet) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
index 9c68b3eb28a..a1ec308c808 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
@@ -190,7 +190,9 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC
cloudWatch.hostedAuth().ifPresent(hostedAuth -> cloudWatchBuilder
.accessKeyName(hostedAuth.accessKeyName)
.secretKeyName(hostedAuth.secretKeyName));
- cloudWatch.profile().ifPresent(cloudWatchBuilder::profile);
+ cloudWatch.sharedCredentials().ifPresent(sharedCredentials -> cloudWatchBuilder
+ .profile(sharedCredentials.profile)
+ .file(sharedCredentials.file));
builder.cloudWatch(cloudWatchBuilder);
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java
index fd290409ea5..0f3543d3c36 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/CloudWatch.java
@@ -13,7 +13,7 @@ public class CloudWatch {
private final MetricsConsumer consumer;
private HostedAuth hostedAuth;
- private String profile;
+ private SharedCredentials sharedCredentials;
public CloudWatch(String region, String namespace, MetricsConsumer consumer) {
this.region = region;
@@ -26,14 +26,14 @@ public class CloudWatch {
public String consumer() { return consumer.getId(); }
public Optional<HostedAuth> hostedAuth() {return Optional.ofNullable(hostedAuth); }
- public Optional<String> profile() { return Optional.ofNullable(profile); }
+ public Optional<SharedCredentials> sharedCredentials() {return Optional.ofNullable(sharedCredentials); }
- public void setHostedAuth(HostedAuth hostedAuth) {
- this.hostedAuth = hostedAuth;
+ public void setHostedAuth(String accessKeyName, String secretKeyName) {
+ hostedAuth = new HostedAuth(accessKeyName, secretKeyName);
}
- public void setProfile(String profile) {
- this.profile = profile;
+ public void setSharedCredentials(String profile, String file) {
+ sharedCredentials = new SharedCredentials(profile, file);
}
public static class HostedAuth {
@@ -46,4 +46,14 @@ public class CloudWatch {
}
}
+ public static class SharedCredentials {
+ public final String profile;
+ public final String file;
+
+ public SharedCredentials(String profile, String file) {
+ this.profile = profile;
+ this.file = file;
+ }
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
index e308be00faf..bd16d83d157 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
@@ -608,6 +608,8 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.idealstate.garbage_collection.done_ok.rate"));
metrics.add(new Metric("vds.idealstate.garbage_collection.done_failed.rate"));
metrics.add(new Metric("vds.idealstate.garbage_collection.pending.average"));
+ metrics.add(new Metric("vds.idealstate.garbage_collection.documents_removed.count"));
+ metrics.add(new Metric("vds.idealstate.garbage_collection.documents_removed.rate"));
metrics.add(new Metric("vds.distributor.puts.sum.latency.max"));
metrics.add(new Metric("vds.distributor.puts.sum.latency.sum"));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java
index 4b9d5542aa9..5ce941d6638 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/builder/xml/CloudWatchBuilder.java
@@ -5,7 +5,7 @@ import com.yahoo.vespa.model.admin.monitoring.CloudWatch.HostedAuth;
import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;
import org.w3c.dom.Element;
-import static com.yahoo.config.model.builder.xml.XmlHelper.getOptionalChildValue;
+import static com.yahoo.config.model.builder.xml.XmlHelper.getOptionalChild;
/**
* @author gjoranv
@@ -14,22 +14,26 @@ public class CloudWatchBuilder {
private static final String REGION_ATTRIBUTE = "region";
private static final String NAMESPACE_ATTRIBUTE = "namespace";
- private static final String ACCESS_KEY_ELEMENT = "access-key-name";
- private static final String SECRET_KEY_ELEMENT = "secret-key-name";
- private static final String PROFILE_ELEMENT = "profile";
+ private static final String CREDENTIALS_ELEMENT = "credentials";
+ private static final String ACCESS_KEY_ATTRIBUTE = "access-key-name";
+ private static final String SECRET_KEY_ATTRIBUTE = "secret-key-name";
+ private static final String SHARED_CREDENTIALS_ELEMENT = "shared-credentials";
+ private static final String PROFILE_ATTRIBUTE = "profile";
+ private static final String FILE_ATTRIBUTE = "file";
public static CloudWatch buildCloudWatch(Element cloudwatchElement, MetricsConsumer consumer) {
CloudWatch cloudWatch = new CloudWatch(cloudwatchElement.getAttribute(REGION_ATTRIBUTE),
cloudwatchElement.getAttribute(NAMESPACE_ATTRIBUTE),
consumer);
- getOptionalChildValue(cloudwatchElement, PROFILE_ELEMENT).ifPresent(cloudWatch::setProfile);
+ getOptionalChild(cloudwatchElement, CREDENTIALS_ELEMENT)
+ .ifPresent(elem -> cloudWatch.setHostedAuth(elem.getAttribute(ACCESS_KEY_ATTRIBUTE),
+ elem.getAttribute(SECRET_KEY_ATTRIBUTE)));
+
+ getOptionalChild(cloudwatchElement, SHARED_CREDENTIALS_ELEMENT)
+ .ifPresent(elem -> cloudWatch.setSharedCredentials(elem.getAttribute(PROFILE_ATTRIBUTE),
+ elem.getAttribute(FILE_ATTRIBUTE)));
- getOptionalChildValue(cloudwatchElement, ACCESS_KEY_ELEMENT)
- .ifPresent(accessKey -> cloudWatch.setHostedAuth(
- new HostedAuth(accessKey,
- getOptionalChildValue(cloudwatchElement, SECRET_KEY_ELEMENT)
- .orElseThrow(() -> new IllegalArgumentException("Access key given without a secret key.")))));
return cloudWatch;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java
new file mode 100644
index 00000000000..462ac39fa84
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudWatchValidator.java
@@ -0,0 +1,37 @@
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.config.model.ConfigModelContext;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * @author gjoranv
+ */
+public class CloudWatchValidator extends Validator {
+
+ @Override
+ public void validate(VespaModel model, DeployState deployState) {
+ if (!deployState.isHosted()) return;
+ if (deployState.zone().system().isPublic()) return;
+ if (model.getAdmin().getApplicationType() != ConfigModelContext.ApplicationType.DEFAULT) return;
+
+ var offendingConsumers = model.getAdmin().getUserMetrics().getConsumers().values().stream()
+ .filter(consumer -> !consumer.cloudWatches().isEmpty())
+ .collect(toList());
+
+ if (! offendingConsumers.isEmpty()) {
+ throw new IllegalArgumentException("CloudWatch cannot be set up for non-public hosted Vespa and must " +
+ "be removed for consumers: " + consumerIds(offendingConsumers));
+ }
+ }
+
+ private List<String> consumerIds(List<MetricsConsumer> offendingConsumers) {
+ return offendingConsumers.stream().map(MetricsConsumer::getId).collect(toList());
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
index 1e4a45428b8..b03917dec3b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
@@ -59,6 +59,7 @@ public class Validation {
new SecretStoreValidator().validate(model, deployState);
new EndpointCertificateSecretsValidator().validate(model, deployState);
new AccessControlFilterValidator().validate(model, deployState);
+ new CloudWatchValidator().validate(model, deployState);
List<ConfigChangeAction> result = Collections.emptyList();
if (deployState.getProperties().isFirstTimeDeployment()) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
index 2c88f965f1f..43f4637798a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
@@ -15,9 +15,9 @@ import com.yahoo.vespa.model.container.xml.ContainerModelBuilder;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
/**
* A common utility class to represent a requirement for nodes during model building.
@@ -46,18 +46,19 @@ public class NodesSpecification {
private final boolean exclusive;
- /** Whether this requires running container and content processes co-located on the same node. */
- private final boolean combined;
-
/** The resources each node should have, or empty to use the default */
private final Optional<NodeResources> resources;
/** The identifier of the custom docker image layer to use (not supported yet) */
private final Optional<String> dockerImage;
+ /** The ID of the cluster referencing this node specification, if any */
+ private final Optional<String> combinedId;
+
private NodesSpecification(boolean dedicated, int count, int groups, Version version,
- boolean required, boolean canFail, boolean exclusive, boolean combined,
- Optional<NodeResources> resources, Optional<String> dockerImage) {
+ boolean required, boolean canFail, boolean exclusive,
+ Optional<NodeResources> resources, Optional<String> dockerImage,
+ Optional<String> combinedId) {
this.dedicated = dedicated;
this.count = count;
this.groups = groups;
@@ -67,10 +68,11 @@ public class NodesSpecification {
this.exclusive = exclusive;
this.resources = resources;
this.dockerImage = dockerImage;
- this.combined = combined;
+ this.combinedId = combinedId;
}
- private NodesSpecification(boolean dedicated, boolean canFail, boolean combined, Version version, ModelElement nodesElement) {
+ private NodesSpecification(boolean dedicated, boolean canFail, Version version, ModelElement nodesElement,
+ Optional<String> combinedId) {
this(dedicated,
nodesElement.integerAttribute("count", 1),
nodesElement.integerAttribute("groups", 1),
@@ -78,15 +80,25 @@ public class NodesSpecification {
nodesElement.booleanAttribute("required", false),
canFail,
nodesElement.booleanAttribute("exclusive", false),
- combined,
getResources(nodesElement),
- Optional.ofNullable(nodesElement.stringAttribute("docker-image")));
+ Optional.ofNullable(nodesElement.stringAttribute("docker-image")),
+ combinedId);
+ }
+
+ /** Returns the ID of the cluster referencing this node specification, if any */
+ private static Optional<String> findCombinedId(ModelElement nodesElement, ModelElement resolvedElement) {
+ if (resolvedElement != nodesElement) {
+ // Specification for a container cluster referencing nodes in a content cluster
+ return containerIdOf(nodesElement);
+ }
+ // Specification for a content cluster that is referenced by a container cluster
+ return containerIdReferencing(nodesElement);
}
private static NodesSpecification create(boolean dedicated, boolean canFail, Version version, ModelElement nodesElement) {
var resolvedElement = resolveElement(nodesElement);
- boolean combined = resolvedElement != nodesElement || isReferencedByOtherElement(nodesElement);
- return new NodesSpecification(dedicated, canFail, combined, version, resolvedElement);
+ var combinedId = findCombinedId(nodesElement, resolvedElement);
+ return new NodesSpecification(dedicated, canFail, version, resolvedElement, combinedId);
}
/** Returns a requirement for dedicated nodes taken from the given <code>nodes</code> element */
@@ -133,7 +145,7 @@ public class NodesSpecification {
false,
! context.getDeployState().getProperties().isBootstrap(),
false,
- false,
+ Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -147,7 +159,7 @@ public class NodesSpecification {
false,
! context.getDeployState().getProperties().isBootstrap(),
false,
- false,
+ Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -175,9 +187,9 @@ public class NodesSpecification {
ClusterSpec.Type clusterType,
ClusterSpec.Id clusterId,
DeployLogger logger) {
- if (combined)
+ if (combinedId.isPresent())
clusterType = ClusterSpec.Type.combined;
- ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId, version, exclusive);
+ ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId, version, exclusive, combinedId.map(ClusterSpec.Id::from));
return hostSystem.allocateHosts(cluster, Capacity.fromCount(count, resources, required, canFail), groups, logger);
}
@@ -280,24 +292,35 @@ public class NodesSpecification {
return new ModelElement(referencedNodesElement);
}
- /** Returns whether the given nodesElement is referenced by any other nodes element */
- private static boolean isReferencedByOtherElement(ModelElement nodesElement) {
+ /** Returns the ID of the parent container element of nodesElement, if any */
+ private static Optional<String> containerIdOf(ModelElement nodesElement) {
+ var element = nodesElement.getXml();
+ for (var containerTag : List.of("container", "jdisc")) {
+ var container = findParentByTag(containerTag, element);
+ if (container.isEmpty()) continue;
+ return container.map(el -> el.getAttribute("id"));
+ }
+ return Optional.empty();
+ }
+
+ /** Returns the ID of the container element referencing nodesElement, if any */
+ private static Optional<String> containerIdReferencing(ModelElement nodesElement) {
var element = nodesElement.getXml();
var services = findParentByTag("services", element);
- if (services.isEmpty()) return false;
+ if (services.isEmpty()) return Optional.empty();
var content = findParentByTag("content", element);
- if (content.isEmpty()) return false;
+ if (content.isEmpty()) return Optional.empty();
var contentClusterId = content.get().getAttribute("id");
- if (contentClusterId.isEmpty()) return false;
+ if (contentClusterId.isEmpty()) return Optional.empty();
for (var rootChild : XML.getChildren(services.get())) {
if ( ! ContainerModelBuilder.isContainerTag(rootChild)) continue;
var nodes = XML.getChild(rootChild, "nodes");
if (nodes == null) continue;
if (!contentClusterId.equals(nodes.getAttribute("of"))) continue;
- return true;
+ return Optional.of(rootChild.getAttribute("id"));
}
- return false;
+ return Optional.empty();
}
private static Optional<Element> findChildById(Element parent, String id) {
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 9d7274f1bbf..747f8801137 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
@@ -629,7 +629,8 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from(cluster.getName()),
deployState.getWantedNodeVespaVersion(),
- false);
+ false,
+ Optional.empty());
Capacity capacity = Capacity.fromCount(1,
Optional.empty(),
false,
@@ -655,7 +656,8 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from(cluster.getName()),
context.getDeployState().getWantedNodeVespaVersion(),
- false);
+ false,
+ Optional.empty());
Map<HostResource, ClusterMembership> hosts =
cluster.getRoot().hostSystem().allocateHosts(clusterSpec,
Capacity.fromRequiredNodeType(type), 1, log);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
index 1b4c03a2182..445c93ba66e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/search/IndexedSearchCluster.java
@@ -202,7 +202,8 @@ public class IndexedSearchCluster extends SearchCluster
com.yahoo.searchdefinition.Search search = spec.getSearchDefinition().getSearch();
if ( ! (search instanceof DocumentOnlySearch)) {
DocumentDatabase db = new DocumentDatabase(this, search.getName(),
- new DerivedConfiguration(search, deployState.getDeployLogger(),
+ new DerivedConfiguration(search,
+ deployState.getDeployLogger(),
deployState.getProperties(),
deployState.rankProfileRegistry(),
deployState.getQueryProfiles().getRegistry(),
diff --git a/config-model/src/main/resources/schema/admin.rnc b/config-model/src/main/resources/schema/admin.rnc
index e3ba7dc500d..f4585b3cf3f 100644
--- a/config-model/src/main/resources/schema/admin.rnc
+++ b/config-model/src/main/resources/schema/admin.rnc
@@ -91,14 +91,16 @@ Cloudwatch = element cloudwatch {
attribute region { xsd:Name } &
attribute namespace { xsd:string { pattern = "[\w_\-/#:\.]+" } } &
(
- (
- element access-key-name { xsd:Name } &
- element secret-key-name { xsd:Name }
- )
+ element credentials {
+ attribute access-key-name { xsd:Name } &
+ attribute secret-key-name { xsd:Name }
+ }
|
- element profile { xsd:Name }
+ element shared-credentials {
+ attribute profile { xsd:Name } &
+ attribute file { string }?
+ }
)?
-
}
ClusterControllers = element cluster-controllers {
diff --git a/config-model/src/test/derived/fieldset2/index-info.cfg b/config-model/src/test/derived/fieldset2/index-info.cfg
index 7c3c1c448db..56cc53b4628 100644
--- a/config-model/src/test/derived/fieldset2/index-info.cfg
+++ b/config-model/src/test/derived/fieldset2/index-info.cfg
@@ -13,6 +13,8 @@ indexinfo[].command[].indexname "field1"
indexinfo[].command[].command "normalize"
indexinfo[].command[].indexname "field1"
indexinfo[].command[].command "plain-tokens"
+indexinfo[].command[].indexname "field1"
+indexinfo[].command[].command "phrase-segmenting"
indexinfo[].command[].indexname "field2"
indexinfo[].command[].command "index"
indexinfo[].command[].indexname "field2"
@@ -23,6 +25,8 @@ indexinfo[].command[].indexname "field2"
indexinfo[].command[].command "normalize"
indexinfo[].command[].indexname "field2"
indexinfo[].command[].command "plain-tokens"
+indexinfo[].command[].indexname "field2"
+indexinfo[].command[].command "phrase-segmenting"
indexinfo[].command[].indexname "default"
indexinfo[].command[].command "phrase-segmenting false"
indexinfo[].command[].indexname "default"
diff --git a/config-model/src/test/derived/indexschema/index-info.cfg b/config-model/src/test/derived/indexschema/index-info.cfg
index 65818e088f5..388b212689a 100644
--- a/config-model/src/test/derived/indexschema/index-info.cfg
+++ b/config-model/src/test/derived/indexschema/index-info.cfg
@@ -334,6 +334,8 @@ indexinfo[].command[].command "stem:BEST"
indexinfo[].command[].indexname "gram"
indexinfo[].command[].command "normalize"
indexinfo[].command[].indexname "gram"
+indexinfo[].command[].command "phrase-segmenting false"
+indexinfo[].command[].indexname "gram"
indexinfo[].command[].command "ngram 2"
indexinfo[].command[].indexname "nostem1"
indexinfo[].command[].command "lowercase"
diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
index c010b23e207..d215fdbb7a0 100644
--- a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
@@ -264,7 +264,7 @@ public class ModelProvisioningTest {
assertEquals("Heap size is lowered with combined clusters",
17, physicalMemoryPercentage(model.getContainerClusters().get("container1")));
assertProvisioned(0, ClusterSpec.Id.from("container1"), ClusterSpec.Type.container, model);
- assertProvisioned(2, ClusterSpec.Id.from("content1"), ClusterSpec.Type.combined, model);
+ assertProvisioned(2, ClusterSpec.Id.from("content1"), ClusterSpec.Id.from("container1"), ClusterSpec.Type.combined, model);
}
}
@@ -1805,12 +1805,17 @@ public class ModelProvisioningTest {
assertTrue(logdConfig.logserver().use());
}
- private static void assertProvisioned(int nodeCount, ClusterSpec.Id id, ClusterSpec.Type type, VespaModel model) {
- assertEquals("Nodes in cluster " + id + " with type " + type, nodeCount,
+ private static void assertProvisioned(int nodeCount, ClusterSpec.Id id, ClusterSpec.Id combinedId,
+ ClusterSpec.Type type, VespaModel model) {
+ assertEquals("Nodes in cluster " + id + " with type " + type + (combinedId != null ? ", combinedId " + combinedId : ""), nodeCount,
model.hostSystem().getHosts().stream()
.map(h -> h.spec().membership().get().cluster())
- .filter(spec -> spec.id().equals(id) && spec.type().equals(type))
+ .filter(spec -> spec.id().equals(id) && spec.type().equals(type) && spec.combinedId().equals(Optional.ofNullable(combinedId)))
.count());
}
+ private static void assertProvisioned(int nodeCount, ClusterSpec.Id id, ClusterSpec.Type type, VespaModel model) {
+ assertProvisioned(nodeCount, id, null, type, model);
+ }
+
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java
index 8ea53172200..7e0eb01f611 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java
@@ -1,6 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.derived;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
+import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.document.DocumenttypesConfig;
import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.searchdefinition.Search;
@@ -10,8 +12,6 @@ import com.yahoo.searchdefinition.parser.ParseException;
import ai.vespa.rankingexpression.importer.configmodelview.ImportedMlModels;
import com.yahoo.vespa.configmodel.producers.DocumentManager;
import com.yahoo.vespa.configmodel.producers.DocumentTypes;
-import com.yahoo.vespa.model.container.search.QueryProfiles;
-import com.yahoo.vespa.model.test.utils.DeployLoggerStub;
import java.io.File;
import java.io.IOException;
@@ -26,17 +26,22 @@ public abstract class AbstractExportingTestCase extends SearchDefinitionTestCase
private static final String tempDir = "temp/";
private static final String searchDefRoot = "src/test/derived/";
- private DerivedConfiguration derive(String dirName, String searchDefinitionName) throws IOException, ParseException {
+ private DerivedConfiguration derive(String dirName, String searchDefinitionName, TestProperties properties) throws IOException, ParseException {
File toDir = new File(tempDir + dirName);
toDir.mkdirs();
deleteContent(toDir);
SearchBuilder builder = SearchBuilder.createFromDirectory(searchDefRoot + dirName + "/");
- return derive(dirName, searchDefinitionName, builder);
+ return derive(dirName, searchDefinitionName, properties, builder);
}
- private DerivedConfiguration derive(String dirName, String searchDefinitionName, SearchBuilder builder) throws IOException {
+ private DerivedConfiguration derive(String dirName,
+ String searchDefinitionName,
+ TestProperties properties,
+ SearchBuilder builder) throws IOException {
DerivedConfiguration config = new DerivedConfiguration(builder.getSearch(searchDefinitionName),
+ new BaseDeployLogger(),
+ properties,
builder.getRankProfileRegistry(),
builder.getQueryProfileRegistry(),
new ImportedMlModels());
@@ -79,7 +84,11 @@ public abstract class AbstractExportingTestCase extends SearchDefinitionTestCase
}
protected DerivedConfiguration assertCorrectDeriving(String dirName, String searchDefinitionName) throws IOException, ParseException {
- DerivedConfiguration derived = derive(dirName, searchDefinitionName);
+ return assertCorrectDeriving(dirName, searchDefinitionName, new TestProperties());
+ }
+
+ protected DerivedConfiguration assertCorrectDeriving(String dirName, String searchDefinitionName, TestProperties properties) throws IOException, ParseException {
+ DerivedConfiguration derived = derive(dirName, searchDefinitionName, properties);
assertCorrectConfigFiles(dirName);
return derived;
}
@@ -90,7 +99,7 @@ public abstract class AbstractExportingTestCase extends SearchDefinitionTestCase
*/
protected DerivedConfiguration assertCorrectDeriving(SearchBuilder builder, String dirName) throws IOException {
builder.build();
- DerivedConfiguration derived = derive(dirName, null, builder);
+ DerivedConfiguration derived = derive(dirName, null, new TestProperties(), builder);
assertCorrectConfigFiles(dirName);
return derived;
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
index e785792839d..08d915c35ca 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ExportingTestCase.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.derived;
+import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.searchdefinition.SearchBuilder;
import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;
@@ -121,7 +122,9 @@ public class ExportingTestCase extends AbstractExportingTestCase {
@Test
public void testFieldSet2() throws IOException, ParseException {
- assertCorrectDeriving("fieldset2");
+ TestProperties properties = new TestProperties();
+ properties.setUsePhraseSegmenting(true);
+ assertCorrectDeriving("fieldset2", null, properties);
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/TelegrafTest.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/TelegrafTest.java
index dbcbad7bf49..9be94e4198e 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/TelegrafTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/TelegrafTest.java
@@ -14,6 +14,7 @@ import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
/**
* @author gjoranv
@@ -53,6 +54,7 @@ public class TelegrafTest {
String services = servicesWithCloudwatch();
VespaModel hostedModel = getModel(services, hosted);
TelegrafConfig config = hostedModel.getConfig(TelegrafConfig.class, CLUSTER_CONFIG_ID);
+ assertTrue(config.isHostedVespa());
var cloudWatch0 = config.cloudWatch(0);
assertEquals("cloudwatch-consumer", cloudWatch0.consumer());
@@ -72,8 +74,8 @@ public class TelegrafTest {
" <consumer id='cloudwatch-consumer'>",
" <metric id='my-metric'/>",
" <cloudwatch region='us-east-1' namespace='my-namespace' >",
- " <access-key-name>my-access-key</access-key-name>",
- " <secret-key-name>my-secret-key</secret-key-name>",
+ " <credentials access-key-name='my-access-key' ",
+ " secret-key-name='my-secret-key' />",
" </cloudwatch>",
" </consumer>",
" </metrics>",
@@ -92,11 +94,11 @@ public class TelegrafTest {
" <consumer id='cloudwatch-consumer'>",
" <metric id='my-metric'/>",
" <cloudwatch region='us-east-1' namespace='namespace-1' >",
- " <access-key-name>access-key-1</access-key-name>",
- " <secret-key-name>secret-key-1</secret-key-name>",
+ " <credentials access-key-name='access-key-1' ",
+ " secret-key-name='secret-key-1' />",
" </cloudwatch>",
" <cloudwatch region='us-east-1' namespace='namespace-2' >",
- " <profile>profile-2</profile>",
+ " <shared-credentials profile='profile-2' />",
" </cloudwatch>",
" </consumer>",
" </metrics>",
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java
new file mode 100644
index 00000000000..40b8223479d
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudWatchValidatorTest.java
@@ -0,0 +1,101 @@
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.model.NullConfigModelRegistry;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.deploy.TestProperties;
+import com.yahoo.config.model.test.MockApplicationPackage;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.model.VespaModel;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+
+import static com.yahoo.config.provision.Environment.prod;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author gjoranv
+ */
+public class CloudWatchValidatorTest {
+
+ @Rule
+ public final ExpectedException exceptionRule = ExpectedException.none();
+
+ @Test
+ public void cloudwatch_in_public_zones_passes_validation() throws IOException, SAXException {
+ DeployState deployState = deployState(servicesWithCloudwatch(), true, true);
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+
+ new CloudWatchValidator().validate(model, deployState);
+ }
+
+ @Test
+ public void cloudwatch_passes_validation_for_self_hosted_vespa() throws IOException, SAXException {
+ DeployState deployState = deployState(servicesWithCloudwatch(), false, false);
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+
+ new CloudWatchValidator().validate(model, deployState);
+ }
+
+ @Test
+ public void cloudwatch_in_non_public_zones_fails_validation() throws IOException, SAXException {
+ exceptionRule.expect(IllegalArgumentException.class);
+ exceptionRule.expectMessage(
+ "CloudWatch cannot be set up for non-public hosted Vespa and must be removed for consumers: [cloudwatch-consumer]");
+
+ DeployState deployState = deployState(servicesWithCloudwatch(), true, false);
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+
+ new CloudWatchValidator().validate(model, deployState);
+ }
+
+ private static DeployState deployState(String servicesXml, boolean isHosted, boolean isPublic) {
+ ApplicationPackage app = new MockApplicationPackage.Builder()
+ .withServices(servicesXml)
+ .build();
+
+ DeployState.Builder builder = new DeployState.Builder()
+ .applicationPackage(app)
+ .properties(new TestProperties().setHostedVespa(isHosted));
+ if (isHosted) {
+ var system = isPublic ? SystemName.Public : SystemName.main;
+ builder.zone(new Zone(system, Environment.prod, RegionName.from("foo")));
+ }
+ final DeployState deployState = builder.build();
+
+ if (isHosted) {
+ assertTrue("Test must emulate a hosted deployment.", deployState.isHosted());
+ assertEquals("Test must emulate a prod environment.", prod, deployState.zone().environment());
+ }
+ return deployState;
+ }
+
+ private String servicesWithCloudwatch() {
+ return String.join("\n",
+ "<services>",
+ " <admin version='2.0'>",
+ " <adminserver hostalias='node1'/>",
+ " <metrics>",
+ " <consumer id='cloudwatch-consumer'>",
+ " <metric id='my-metric'/>",
+ " <cloudwatch region='us-east-1' namespace='my-namespace' >",
+ " <credentials access-key-name='my-access-key' ",
+ " secret-key-name='my-secret-key' />",
+ " </cloudwatch>",
+ " </consumer>",
+ " </metrics>",
+ " </admin>",
+ "</services>"
+ );
+ }
+
+}
diff --git a/config-model/src/test/schema-test-files/services.xml b/config-model/src/test/schema-test-files/services.xml
index 604e2abbbae..0c395fedb96 100644
--- a/config-model/src/test/schema-test-files/services.xml
+++ b/config-model/src/test/schema-test-files/services.xml
@@ -15,26 +15,29 @@
<slobrok hostalias="rtc-1" />
</slobroks>
<metrics>
+
<consumer id="cloudwatch-hosted">
<metric-set id="my-set" />
<metric id="my-metric"/>
<metric id="my-metric2" display-name="my-metric3"/>
<metric display-name="my-metric4" id="my-metric4.avg"/>
<cloudwatch region="us-east1" namespace="my-namespace">
- <access-key-name>my-access-key</access-key-name>
- <secret-key-name>my-secret-key</secret-key-name>
+ <credentials access-key-name="my-access-key" secret-key-name="my-secret-key" />
</cloudwatch>
</consumer>
+
<consumer id="cloudwatch-self-hosted-with-default-auth">
<metric-set id="public" />
<cloudwatch region="us-east1" namespace="namespace_legal.chars:/#1" />
</consumer>
+
<consumer id="cloudwatch-self-hosted-with-profile">
<metric id="my-custom-metric" />
<cloudwatch region="us-east1" namespace="another-namespace">
- <profile>profile-in-credentials-file</profile>
+ <shared-credentials profile="profile-in-credentials-file" file="/user/.aws/credentials"/>
</cloudwatch>
</consumer>
+
</metrics>
<logforwarding>
<splunk deployment-server="foo:8989" client-name="foobar" splunk-home="/opt/splunk" phone-home-interval="900"/>
diff --git a/config-provisioning/abi-spec.json b/config-provisioning/abi-spec.json
index 9a091f1161c..fa7773a54f1 100644
--- a/config-provisioning/abi-spec.json
+++ b/config-provisioning/abi-spec.json
@@ -271,11 +271,14 @@
"public com.yahoo.config.provision.ClusterSpec$Id id()",
"public com.yahoo.component.Version vespaVersion()",
"public java.util.Optional group()",
+ "public java.util.Optional combinedId()",
"public boolean isExclusive()",
"public com.yahoo.config.provision.ClusterSpec with(java.util.Optional)",
"public com.yahoo.config.provision.ClusterSpec exclusive(boolean)",
"public static com.yahoo.config.provision.ClusterSpec request(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.component.Version, boolean)",
+ "public static com.yahoo.config.provision.ClusterSpec request(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.component.Version, boolean, java.util.Optional)",
"public static com.yahoo.config.provision.ClusterSpec from(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.config.provision.ClusterSpec$Group, com.yahoo.component.Version, boolean)",
+ "public static com.yahoo.config.provision.ClusterSpec from(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.config.provision.ClusterSpec$Group, com.yahoo.component.Version, boolean, java.util.Optional)",
"public java.lang.String toString()",
"public int hashCode()",
"public boolean equals(java.lang.Object)",
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java
index f041823bf04..0fb78b59aaf 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java
@@ -3,9 +3,11 @@ package com.yahoo.config.provision;
import com.yahoo.component.Version;
+import java.util.Optional;
+
/**
* A node's membership in a cluster. This is a value object.
- * The format is "clusterType/clusterId/groupId/index[/exclusive][/retired]"
+ * The format is "clusterType/clusterId/groupId/index[/exclusive][/retired][/combinedId]"
*
* @author bratseth
*/
@@ -22,21 +24,24 @@ public class ClusterMembership {
String[] components = stringValue.split("/");
if (components.length < 4)
throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " +
- "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive]'");
+ "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive][/combinedId]'");
boolean exclusive = false;
+ var combinedId = Optional.<String>empty();
if (components.length > 4) {
for (int i = 4; i < components.length; i++) {
String component = components[i];
switch (component) {
case "exclusive": exclusive = true; break;
case "retired": retired = true; break;
+ default: combinedId = Optional.of(component); break;
}
}
}
this.cluster = ClusterSpec.from(ClusterSpec.Type.valueOf(components[0]), ClusterSpec.Id.from(components[1]),
- ClusterSpec.Group.from(Integer.valueOf(components[2])), vespaVersion, exclusive);
+ ClusterSpec.Group.from(Integer.parseInt(components[2])), vespaVersion,
+ exclusive, combinedId.map(ClusterSpec.Id::from));
this.index = Integer.parseInt(components[3]);
this.stringValue = toStringValue();
}
@@ -54,7 +59,8 @@ public class ClusterMembership {
(cluster.group().isPresent() ? "/" + cluster.group().get().index() : "") +
"/" + index +
( cluster.isExclusive() ? "/exclusive" : "") +
- ( retired ? "/retired" : "");
+ ( retired ? "/retired" : "") +
+ ( cluster.combinedId().isPresent() ? "/" + cluster.combinedId().get().value() : "");
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
index 5aed5d8e2e7..e1a28e3f8d7 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
@@ -21,13 +21,19 @@ public final class ClusterSpec {
private final Optional<Group> groupId;
private final Version vespaVersion;
private boolean exclusive;
+ private final Optional<Id> combinedId;
- private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive) {
+ private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive, Optional<Id> combinedId) {
this.type = type;
this.id = id;
this.groupId = groupId;
this.vespaVersion = vespaVersion;
this.exclusive = exclusive;
+ // TODO(mpolden): Require combinedId to always be present for type combined after April 2020
+ if (type != Type.combined && combinedId.isPresent()) {
+ throw new IllegalArgumentException("combinedId must be empty for cluster of type " + type);
+ }
+ this.combinedId = combinedId;
}
/** Returns the cluster type */
@@ -42,6 +48,11 @@ public final class ClusterSpec {
/** Returns the group within the cluster this specifies, or empty to specify the whole cluster */
public Optional<Group> group() { return groupId; }
+ /** Returns the ID of the container cluster that is combined with this. This is only present for combined clusters */
+ public Optional<Id> combinedId() {
+ return combinedId;
+ }
+
/**
* Returns whether the physical hosts running the nodes of this application can
* also run nodes of other applications. Using exclusive nodes for containers increases security
@@ -50,19 +61,29 @@ public final class ClusterSpec {
public boolean isExclusive() { return exclusive; }
public ClusterSpec with(Optional<Group> newGroup) {
- return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive);
+ return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, combinedId);
}
public ClusterSpec exclusive(boolean exclusive) {
- return new ClusterSpec(type, id, groupId, vespaVersion, exclusive);
+ return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId);
}
+ // TODO(mpolden): Remove after April 2020
public static ClusterSpec request(Type type, Id id, Version vespaVersion, boolean exclusive) {
- return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive);
+ return request(type, id, vespaVersion, exclusive, Optional.empty());
}
+ public static ClusterSpec request(Type type, Id id, Version vespaVersion, boolean exclusive, Optional<Id> combinedId) {
+ return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive, combinedId);
+ }
+
+ // TODO(mpolden): Remove after April 2020
public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion, boolean exclusive) {
- return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive);
+ return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive, Optional.empty());
+ }
+
+ public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion, boolean exclusive, Optional<Id> combinedId) {
+ return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive, combinedId);
}
@Override
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java b/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java
index 02da2ab98e2..5c3d9d97bdd 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java
@@ -22,4 +22,9 @@ public enum RoutingMethod {
return this == exclusive || this == sharedLayer4;
}
+ /** Returns whether this method routes requests through a shared routing layer */
+ public boolean isShared() {
+ return this == shared || this == sharedLayer4;
+ }
+
}
diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java
index 3a36afcfdce..aa22747c165 100644
--- a/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java
+++ b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java
@@ -5,6 +5,8 @@ import com.yahoo.component.Version;
import com.yahoo.component.Vtag;
import org.junit.Test;
+import java.util.Optional;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -16,28 +18,41 @@ public class ClusterMembershipTest {
@Test
public void testContainerServiceInstance() {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false, Optional.empty());
assertContainerService(ClusterMembership.from(cluster, 3));
}
@Test
- public void testContainerInstanceWithOptionalParts() {
+ public void testSerializationWithOptionalParts() {
{
ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive/retired", Vtag.currentVersion);
+ ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion);
+ assertEquals(instance, serialized);
assertTrue(instance.retired());
assertTrue(instance.cluster().isExclusive());
+ assertFalse(instance.cluster().combinedId().isPresent());
}
-
{
ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive", Vtag.currentVersion);
+ ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion);
+ assertEquals(instance, serialized);
+ assertFalse(instance.retired());
+ assertTrue(instance.cluster().isExclusive());
+ assertFalse(instance.cluster().combinedId().isPresent());
+ }
+ {
+ ClusterMembership instance = ClusterMembership.from("combined/id1/4/37/exclusive/containerId1", Vtag.currentVersion);
+ ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion);
+ assertEquals(instance, serialized);
assertFalse(instance.retired());
assertTrue(instance.cluster().isExclusive());
+ assertEquals(ClusterSpec.Id.from("containerId1"), instance.cluster().combinedId().get());
}
}
@Test
public void testServiceInstance() {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false, Optional.empty());
assertContentService(ClusterMembership.from(cluster, 37));
}
@@ -55,7 +70,7 @@ public class ClusterMembershipTest {
@Test
public void testServiceInstanceWithRetire() {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false, Optional.empty());
assertContentServiceWithRetire(ClusterMembership.retiredFrom(cluster, 37));
}
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 829a59a9598..a15b570a55d 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
@@ -135,6 +135,7 @@ public class ModelContextImpl implements ModelContext {
private final double defaultTermwiseLimit;
private final boolean useBucketSpaceMetric;
private final boolean useNewAthenzFilter;
+ private final boolean usePhraseSegmenting;
public Properties(ApplicationId applicationId,
boolean multitenantFromConfig,
@@ -169,6 +170,8 @@ public class ModelContextImpl implements ModelContext {
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
this.useNewAthenzFilter = Flags.USE_NEW_ATHENZ_FILTER.bindTo(flagSource)
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
+ this.usePhraseSegmenting = Flags.PHRASE_SEGMENTING.bindTo(flagSource)
+ .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
}
@Override
@@ -223,7 +226,12 @@ public class ModelContextImpl implements ModelContext {
@Override
public boolean useBucketSpaceMetric() { return useBucketSpaceMetric; }
- @Override public boolean useNewAthenzFilter() { return useNewAthenzFilter; }
+ @Override
+ public boolean useNewAthenzFilter() { return useNewAthenzFilter; }
+
+ @Override
+ public boolean usePhraseSegmenting() { return usePhraseSegmenting; }
+
}
}
diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml
index 970dd49e865..0cd283ca62b 100644
--- a/configserver/src/main/resources/configserver-app/services.xml
+++ b/configserver/src/main/resources/configserver-app/services.xml
@@ -63,8 +63,7 @@
<component id="com.yahoo.vespa.service.manager.UnionMonitorManager" bundle="service-monitor" />
<component id="com.yahoo.vespa.service.model.ServiceMonitorImpl" bundle="service-monitor" />
<component id="com.yahoo.vespa.service.duper.DuperModelManager" bundle="service-monitor" />
- <component id="com.yahoo.vespa.orchestrator.ServiceMonitorInstanceLookupService" bundle="orchestrator" />
- <component id="com.yahoo.vespa.orchestrator.status.ZookeeperStatusService" bundle="orchestrator" />
+ <component id="com.yahoo.vespa.orchestrator.status.ZkStatusService" bundle="orchestrator" />
<component id="com.yahoo.vespa.orchestrator.controller.RetryingClusterControllerClientFactory" bundle="orchestrator" />
<component id="com.yahoo.vespa.orchestrator.OrchestratorImpl" bundle="orchestrator" />
diff --git a/container-disc/src/main/ssl/jdisc_container.keydb b/container-disc/src/main/ssl/jdisc_container.keydb
deleted file mode 100644
index 23479a83442..00000000000
--- a/container-disc/src/main/ssl/jdisc_container.keydb
+++ /dev/null
@@ -1,11 +0,0 @@
-<keydb>
- <keygroup name="jdisc_container" id="0">
- <keyname name="jdisc_key" usage="all" type="transient">
- <key version="0"
- value = "2eMsYNKWBOWpsah8d57B65xvmFVZGiPAGv3pbI1mlZU-" current = "true"
- timestamp = "20130320234320"
- expiry = "20160319234320">
- </key>
- </keyname>
- </keygroup>
-</keydb>
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json
index 82d3223c8fe..51fee99a743 100644
--- a/container-search/abi-spec.json
+++ b/container-search/abi-spec.json
@@ -858,8 +858,12 @@
"public void <init>(java.lang.String, java.lang.String)",
"public int getTargetNumHits()",
"public java.lang.String getIndexName()",
+ "public int getHnswExploreAdditionalHits()",
+ "public boolean getAllowApproximate()",
"public java.lang.String getQueryTensorName()",
"public void setTargetNumHits(int)",
+ "public void setHnswExploreAdditionalHits(int)",
+ "public void setAllowApproximate(boolean)",
"public void setIndexName(java.lang.String)",
"public com.yahoo.prelude.query.Item$ItemType getItemType()",
"public java.lang.String getName()",
diff --git a/container-search/src/main/java/com/yahoo/prelude/Index.java b/container-search/src/main/java/com/yahoo/prelude/Index.java
index 365ee299ca4..5e7fddd7fe7 100644
--- a/container-search/src/main/java/com/yahoo/prelude/Index.java
+++ b/container-search/src/main/java/com/yahoo/prelude/Index.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.prelude;
-
import com.yahoo.language.process.StemMode;
import java.util.ArrayList;
@@ -10,7 +9,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
-
/**
* Information about configured settings of a field or field collection (an actual index or not) in a search definition.
* There are two types of settings:
@@ -74,8 +72,8 @@ public class Index {
private boolean isNGram = false;
private int gramSize = 2;
- /** Whether implicit phrases should lead to a phrase item or an and item */
- private boolean phraseSegmenting = true;
+ /** Whether implicit phrases should lead to a phrase item or an and item. */
+ private Boolean phraseSegmenting = false;
/** The string terminating an exact token in this index, or null to use the default (space) */
private String exactTerminator = null;
diff --git a/container-search/src/main/java/com/yahoo/prelude/IndexModel.java b/container-search/src/main/java/com/yahoo/prelude/IndexModel.java
index 062a514056b..00935392683 100644
--- a/container-search/src/main/java/com/yahoo/prelude/IndexModel.java
+++ b/container-search/src/main/java/com/yahoo/prelude/IndexModel.java
@@ -109,7 +109,6 @@ public final class IndexModel {
return searchDefinitions;
}
- @SuppressWarnings("deprecation")
private SearchDefinition unionOf(Collection<SearchDefinition> searchDefinitions) {
SearchDefinition union = new SearchDefinition(IndexFacts.unionName);
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java b/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java
index 35b87ec0190..836107138d0 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/NearestNeighborItem.java
@@ -20,6 +20,8 @@ import java.nio.ByteBuffer;
public class NearestNeighborItem extends SimpleTaggableItem {
private int targetNumHits = 0;
+ private int hnswExploreAdditionalHits = 0;
+ private boolean approximate = true;
private String field;
private String queryTensorName;
@@ -34,12 +36,24 @@ public class NearestNeighborItem extends SimpleTaggableItem {
/** Returns the field name */
public String getIndexName() { return field; }
+ /** Returns the number of extra hits to explore in HNSW algorithm */
+ public int getHnswExploreAdditionalHits() { return hnswExploreAdditionalHits; }
+
+ /** Returns whether approximation is allowed */
+ public boolean getAllowApproximate() { return approximate; }
+
/** Returns the name of the query tensor */
public String getQueryTensorName() { return queryTensorName; }
/** Set the K number of hits to produce */
public void setTargetNumHits(int target) { this.targetNumHits = target; }
+ /** Set the number of extra hits to explore in HNSW algorithm */
+ public void setHnswExploreAdditionalHits(int num) { this.hnswExploreAdditionalHits = num; }
+
+ /** Set whether approximation is allowed */
+ public void setAllowApproximate(boolean value) { this.approximate = value; }
+
@Override
public void setIndexName(String index) { this.field = index; }
@@ -58,6 +72,8 @@ public class NearestNeighborItem extends SimpleTaggableItem {
putString(field, buffer);
putString(queryTensorName, buffer);
IntegerCompressor.putCompressedPositiveNumber(targetNumHits, buffer);
+ IntegerCompressor.putCompressedPositiveNumber((approximate ? 1 : 0), buffer);
+ IntegerCompressor.putCompressedPositiveNumber(hnswExploreAdditionalHits, buffer);
return 1; // number of encoded stack dump items
}
@@ -65,6 +81,8 @@ public class NearestNeighborItem extends SimpleTaggableItem {
protected void appendBodyString(StringBuilder buffer) {
buffer.append("{field=").append(field);
buffer.append(",queryTensorName=").append(queryTensorName);
+ buffer.append(",hnsw.exploreAdditionalHits=").append(hnswExploreAdditionalHits);
+ buffer.append(",approximate=").append(String.valueOf(approximate));
buffer.append(",targetNumHits=").append(targetNumHits).append("}");
}
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java
index d9b969757c2..49bdba2c90f 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java
@@ -30,6 +30,7 @@ public class AllParser extends SimpleParser {
super(environment);
}
+ @Override
protected Item parseItems() {
int position = tokens.getPosition();
try {
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java
index dd836e9c8e1..b714a1d8b34 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/AnyParser.java
@@ -35,21 +35,12 @@ public class AnyParser extends SimpleParser {
return anyItems(true);
}
- Item parseFilter(String filter, Language queryLanguage, Set<String> searchDefinitions) {
- return parseFilter(filter, queryLanguage, environment.getIndexFacts().newSession(searchDefinitions, Collections.emptySet()));
- }
-
Item parseFilter(String filter, Language queryLanguage, IndexFacts.Session indexFacts) {
- Item filterRoot;
-
setState(queryLanguage, indexFacts);
tokenize(filter, null, indexFacts, queryLanguage);
- filterRoot = anyItems(true);
-
- if (filterRoot == null) {
- return null;
- }
+ Item filterRoot = anyItems(true);
+ if (filterRoot == null) return null;
markAllTermsAsFilters(filterRoot);
return filterRoot;
@@ -61,18 +52,10 @@ public class AnyParser extends SimpleParser {
try {
tokens.skipMultiple(PLUS);
+ if ( ! tokens.skipMultiple(MINUS)) return null;
+ if (tokens.currentIsNoIgnore(SPACE)) return null;
- if (!tokens.skipMultiple(MINUS)) {
- return null;
- }
-
- if (tokens.currentIsNoIgnore(SPACE)) {
- return null;
- }
-
- if (item == null) {
- item = indexableItem();
- }
+ item = indexableItem();
if (item == null) {
item = compositeItem();
@@ -88,13 +71,13 @@ public class AnyParser extends SimpleParser {
}
}
}
- if (item!=null)
+ if (item != null)
item.setProtected(true);
+
return item;
} finally {
- if (item == null) {
+ if (item == null)
tokens.setPosition(position);
- }
}
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/SimpleParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/SimpleParser.java
index 9ddfea6dffb..3d244312b2f 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/parser/SimpleParser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/SimpleParser.java
@@ -50,32 +50,28 @@ abstract class SimpleParser extends StructuredParser {
private Item anyItemsBody(boolean topLevel) {
Item topLevelItem = null;
NotItem not = null;
- Item item;
+ Item item = null;
do {
- item = null;
-
- if (item == null) {
- item = positiveItem();
- if (item != null) {
- if (not == null) {
- not = new NotItem();
- not.addPositiveItem(item);
- topLevelItem = combineItems(topLevelItem, not);
- } else {
- not.addPositiveItem(item);
- }
+ item = positiveItem();
+ if (item != null) {
+ if (not == null) {
+ not = new NotItem();
+ not.addPositiveItem(item);
+ topLevelItem = combineItems(topLevelItem, not);
+ } else {
+ not.addPositiveItem(item);
}
}
if (item == null) {
item = negativeItem();
if (item != null) {
- if (not == null && item != null) {
+ if (not == null) {
not = new NotItem();
not.addNegativeItem(item);
topLevelItem = combineItems(topLevelItem, not);
- } else if (item != null) {
+ } else {
not.addNegativeItem(item);
}
}
@@ -97,9 +93,8 @@ abstract class SimpleParser extends StructuredParser {
if (item != null) {
if (topLevelItem == null) {
topLevelItem = item;
- } else if (needNewTopLevel(topLevelItem, item)) {
+ } else if (needNewORTopLevel(topLevelItem, item)) {
CompositeItem newTop = new OrItem();
-
newTop.addItem(topLevelItem);
newTop.addItem(item);
topLevelItem = newTop;
@@ -131,6 +126,7 @@ abstract class SimpleParser extends StructuredParser {
if (topLevelItem != null && topLevelItem != not) {
// => neutral rank items becomes implicit positives
+ System.out.println("Extracting positive item from " + topLevelItem);
not.addPositiveItem(getItemAsPositiveItem(topLevelItem, not));
return not;
} else { // Only negatives - ignore them
@@ -144,21 +140,13 @@ abstract class SimpleParser extends StructuredParser {
}
}
-
- /** Says whether we need a new top level item given the new item */
- private boolean needNewTopLevel(Item topLevelItem, Item item) {
- if (item == null) {
- return false;
- }
- if (topLevelItem instanceof TermItem) {
- return true;
- }
- if (topLevelItem instanceof PhraseItem) {
- return true;
- }
- if (topLevelItem instanceof BlockItem) {
- return true;
- }
+ /** Says whether we need a new top level OR item given the new item */
+ private boolean needNewORTopLevel(Item topLevelItem, Item item) {
+ if (item == null) return false;
+ if (topLevelItem instanceof TermItem) return true;
+ if (topLevelItem instanceof PhraseItem) return true;
+ if (topLevelItem instanceof BlockItem) return true;
+ if ( topLevelItem instanceof AndItem) return true;
return false;
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/StructuredParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/StructuredParser.java
index 5e292a06b0f..9ba6c1a8101 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/parser/StructuredParser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/StructuredParser.java
@@ -442,9 +442,9 @@ abstract class StructuredParser extends AbstractParser {
Item item = null;
try {
- if (!tokens.currentIs(WORD)
- && ((!tokens.currentIs(NUMBER) && !tokens.currentIs(MINUS)
- && !tokens.currentIs(UNDERSCORE)) || (!submodes.url && !submodes.site))) {
+ if ( ! tokens.currentIs(WORD)
+ && ((!tokens.currentIs(NUMBER) && !tokens.currentIs(MINUS)
+ && !tokens.currentIs(UNDERSCORE)) || (!submodes.url && !submodes.site))) {
return null;
}
Token word = tokens.next();
@@ -557,6 +557,7 @@ abstract class StructuredParser extends AbstractParser {
if (composite != null) {
composite.addItem(word);
+ connectLastTermsIn(composite);
} else if (firstWord != null) {
if (submodes.site || submodes.url) {
UriItem uriItem = new UriItem();
@@ -584,6 +585,7 @@ abstract class StructuredParser extends AbstractParser {
}
composite.addItem(firstWord);
composite.addItem(word);
+ connectLastTermsIn(composite);
} else if (word instanceof PhraseItem) {
composite = (PhraseItem)word;
} else {
@@ -654,6 +656,15 @@ abstract class StructuredParser extends AbstractParser {
}
}
+ private void connectLastTermsIn(CompositeItem composite) {
+ int items = composite.items().size();
+ if (items < 2) return;
+ Item nextToLast = composite.items().get(items - 2);
+ Item last = composite.items().get(items - 1);
+ if ( ! (nextToLast instanceof TermItem)) return;
+ ((TermItem)nextToLast).setConnectivity(last, 1);
+ }
+
private boolean addStartMarking() {
if (submodes.explicitAnchoring() && tokens.currentIs(HAT)) {
tokens.skip();
diff --git a/container-search/src/main/java/com/yahoo/search/Query.java b/container-search/src/main/java/com/yahoo/search/Query.java
index 1e3f11f4f78..dc8e2b70740 100644
--- a/container-search/src/main/java/com/yahoo/search/Query.java
+++ b/container-search/src/main/java/com/yahoo/search/Query.java
@@ -663,7 +663,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
// getQueryTree isn't exception safe
try {
queryTree = model.getQueryTree().toString();
- } catch (Exception e) {
+ } catch (Exception | StackOverflowError e) {
queryTree = "[Could not parse user input: " + model.getQueryString() + "]";
}
return "query '" + queryTree + "'";
@@ -675,7 +675,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
// getQueryTree isn't exception safe
try {
queryTree = model.getQueryTree().toString();
- } catch (Exception e) {
+ } catch (Exception | StackOverflowError e) {
queryTree = "Could not parse user input: " + model.getQueryString();
}
return "query=[" + queryTree + "]" + " offset=" + getOffset() + " hits=" + getHits() + "]";
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcClient.java b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcClient.java
index a2821892358..52cb2b4c061 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcClient.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcClient.java
@@ -44,13 +44,14 @@ class RpcClient implements Client {
// The current shared connection. This will be recycled when it becomes invalid.
// All access to this must be synchronized
- private Target target = null;
+ private Target target;
public RpcNodeConnection(String hostname, int port, Supervisor supervisor) {
this.supervisor = supervisor;
this.hostname = hostname;
this.port = port;
description = "rpc node connection to " + hostname + ":" + port;
+ target = supervisor.connect(new Spec(hostname, port));
}
@Override
@@ -79,17 +80,16 @@ class RpcClient implements Client {
private void invokeAsync(Request req, double timeout, RequestWaiter waiter) {
// TODO: Consider replacing this by a watcher on the target
synchronized(this) { // ensure we have exactly 1 valid connection across threads
- if (target == null || ! target.isValid())
+ if (! target.isValid()) {
target = supervisor.connect(new Spec(hostname, port));
+ }
}
target.invokeAsync(req, timeout, waiter);
}
@Override
public void close() {
- if (target != null) {
- target.close();
- }
+ target.close();
}
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java b/container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java
index d39a488626b..e346a766738 100644
--- a/container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java
@@ -97,11 +97,10 @@ public class DocumentSourceSearcher extends Searcher {
public Result search(Query query, Execution execution) {
queryCount++;
Result r = unFilledResults.get(getQueryKeyClone(query));
- if (r == null) {
+ if (r == null)
r = defaultFilledResult.clone();
- } else {
+ else
r = r.clone();
- }
r.setQuery(query);
r.hits().trim(query.getOffset(), query.getHits());
@@ -182,11 +181,8 @@ public class DocumentSourceSearcher extends Searcher {
* reset. For testing - not reliable if multiple threads makes
* queries simultaneously
*/
- public int getQueryCount() {
- return queryCount;
- }
+ public int getQueryCount() { return queryCount; }
+
+ public void resetQueryCount() { queryCount = 0; }
- public void resetQueryCount() {
- queryCount=0;
- }
}
diff --git a/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java b/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java
index 6eef1252998..dd52b9e19b8 100644
--- a/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java
+++ b/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java
@@ -701,7 +701,18 @@ public class VespaSerializer {
destination.append(leafAnnotations(item));
comma(destination, initLen);
int targetNumHits = item.getTargetNumHits();
- destination.append("\"targetNumHits\": ").append(targetNumHits);
+ annotationKey(destination, "targetNumHits").append(targetNumHits);
+ int explore = item.getHnswExploreAdditionalHits();
+ if (explore != 0) {
+ comma(destination, initLen);
+ String key = YqlParser.HNSW_EXPLORE_ADDITIONAL_HITS;
+ annotationKey(destination, key).append(explore);
+ }
+ boolean allow_approx = item.getAllowApproximate();
+ if (! allow_approx) {
+ comma(destination, initLen);
+ annotationKey(destination, "approximate").append(allow_approx);
+ }
destination.append("}]");
destination.append(NEAREST_NEIGHBOR).append('(');
destination.append(item.getIndexName()).append(", ");
@@ -1347,6 +1358,11 @@ public class VespaSerializer {
}
}
+ private static StringBuilder annotationKey(StringBuilder annotation, String val) {
+ annotation.append("\"").append(val).append("\": ");
+ return annotation;
+ }
+
private static void comma(StringBuilder annotation, int initLen) {
if (annotation.length() > initLen) {
annotation.append(", ");
diff --git a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
index 8d013e501e8..f4560806dd2 100644
--- a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
+++ b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
@@ -137,6 +137,7 @@ public class YqlParser implements Parser {
static final String ACCENT_DROP = "accentDrop";
static final String ALTERNATIVES = "alternatives";
static final String AND_SEGMENTING = "andSegmenting";
+ static final String APPROXIMATE = "approximate";
static final String BOUNDS = "bounds";
static final String BOUNDS_LEFT_OPEN = "leftOpen";
static final String BOUNDS_OPEN = "open";
@@ -149,6 +150,7 @@ public class YqlParser implements Parser {
static final String EQUIV = "equiv";
static final String FILTER = "filter";
static final String HIT_LIMIT = "hitLimit";
+ static final String HNSW_EXPLORE_ADDITIONAL_HITS = "hnsw.exploreAdditionalHits";
static final String IMPLICIT_TRANSFORMS = "implicitTransforms";
static final String LABEL = "label";
static final String NEAR = "near";
@@ -421,6 +423,14 @@ public class YqlParser implements Parser {
if (targetNumHits != null) {
item.setTargetNumHits(targetNumHits);
}
+ Integer hnswExploreAdditionalHits = getAnnotation(ast, HNSW_EXPLORE_ADDITIONAL_HITS,
+ Integer.class, null, "number of extra hits to explore for HNSW algorithm");
+ if (hnswExploreAdditionalHits != null) {
+ item.setHnswExploreAdditionalHits(hnswExploreAdditionalHits);
+ }
+ Boolean allowApproximate = getAnnotation(ast, APPROXIMATE,
+ Boolean.class, Boolean.TRUE, "allow approximate nearest neighbor search");
+ item.setAllowApproximate(allowApproximate);
String label = getAnnotation(ast, LABEL, String.class, null, "item label");
if (label != null) {
item.setLabel(label);
diff --git a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java
index 5cae40bd10d..df35d8dbdea 100644
--- a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java
@@ -11,6 +11,7 @@ import org.junit.Test;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
@@ -34,7 +35,7 @@ public class ExactMatchAndDefaultIndexTestCase {
q.getModel().setExecution(new Execution(new Execution.Context(null, facts, null, null, null)));
assertEquals("AND testexact:a/b testexact:foo.com", q.getModel().getQueryTree().getRoot().toString());
q = new Query("?query=" + enc("a/b foo.com"));
- assertEquals("AND \"a b\" \"foo com\"", q.getModel().getQueryTree().getRoot().toString());
+ assertEquals("AND a b foo com", q.getModel().getQueryTree().getRoot().toString());
}
@Test
@@ -44,11 +45,7 @@ public class ExactMatchAndDefaultIndexTestCase {
}
private String enc(String s) {
- try {
- return URLEncoder.encode(s, "utf-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e);
- }
+ return URLEncoder.encode(s, StandardCharsets.UTF_8);
}
}
diff --git a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParseTestCase.java b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParseTestCase.java
index 0fdad1a1f9c..c1db7d73561 100644
--- a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParseTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ParseTestCase.java
@@ -7,6 +7,7 @@ import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.IndexModel;
import com.yahoo.prelude.SearchDefinition;
import com.yahoo.prelude.query.AndItem;
+import com.yahoo.prelude.query.AndSegmentItem;
import com.yahoo.prelude.query.CompositeItem;
import com.yahoo.prelude.query.IntItem;
import com.yahoo.prelude.query.Item;
@@ -48,7 +49,9 @@ public class ParseTestCase {
@Test
public void testTermWithIndexPrefix() {
- tester.assertParsed("url:foobar", "url:foobar", Query.Type.ANY);
+ tester.assertParsed("url:foobar",
+ "url:foobar",
+ Query.Type.ANY);
}
@Test
@@ -59,104 +62,98 @@ public class ParseTestCase {
@Test
public void testMultipleTermsWithUTF8EncodingOred() {
tester.assertParsed("OR l\u00e5gen delta M\u00dcNICH M\u00fcnchen",
- "l\u00e5gen delta M\u00dcNICH M\u00fcnchen", Query.Type.ANY);
+ "l\u00e5gen delta M\u00dcNICH M\u00fcnchen",
+ Query.Type.ANY);
}
@Test
public void testMultipleTermsWithMultiplePrefixes() {
tester.assertParsed("RANK (+bar -normal.title:foo -baz) url:foobar",
- "url:foobar +bar -normal.title:foo -baz", Query.Type.ANY);
+ "url:foobar +bar -normal.title:foo -baz", Query.Type.ANY);
}
@Test
public void testSimpleQueryDefaultOr() {
- tester.assertParsed("OR foobar foo bar baz", "foobar foo bar baz",
- Query.Type.ANY);
+ tester.assertParsed("OR foobar foo bar baz", "foobar foo bar baz", Query.Type.ANY);
}
@Test
public void testOrAndNot() {
tester.assertParsed("RANK (+(AND baz bar) -xyzzy -foobaz) foobar foo",
- "foobar +baz foo -xyzzy -foobaz +bar", Query.Type.ANY);
+ "foobar +baz foo -xyzzy -foobaz +bar", Query.Type.ANY);
}
@Test
public void testSimpleOrNestedAnd() {
tester.assertParsed("RANK (OR foo bar baz) foobar xyzzy",
- "foobar +(foo bar baz) xyzzy", Query.Type.ANY);
+ "foobar +(foo bar baz) xyzzy", Query.Type.ANY);
}
@Test
public void testSimpleOrNestedNot() {
tester.assertParsed("+(OR foobar xyzzy) -(AND foo bar baz)",
- "foobar -(foo bar baz) xyzzy", Query.Type.ANY);
+ "foobar -(foo bar baz) xyzzy", Query.Type.ANY);
}
@Test
public void testOrNotNestedAnd() {
- tester.assertParsed(
- "RANK (+(AND baz (OR foo bar baz) bar) -xyzzy -foobaz) foobar foo",
- "foobar +baz foo -xyzzy +(foo bar baz) -foobaz +bar",
- Query.Type.ANY);
+ tester.assertParsed("RANK (+(AND baz (OR foo bar baz) bar) -xyzzy -foobaz) foobar foo",
+ "foobar +baz foo -xyzzy +(foo bar baz) -foobaz +bar",
+ Query.Type.ANY);
}
@Test
public void testOrAndNotNestedNot() {
- tester.assertParsed(
- "RANK (+(AND baz bar) -xyzzy -(AND foo bar baz) -foobaz) foobar foo",
- "foobar +baz foo -xyzzy -(foo bar baz) -foobaz +bar",
- Query.Type.ANY);
+ tester.assertParsed("RANK (+(AND baz bar) -xyzzy -(AND foo bar baz) -foobaz) foobar foo",
+ "foobar +baz foo -xyzzy -(foo bar baz) -foobaz +bar",
+ Query.Type.ANY);
}
@Test
public void testOrMultipleNestedAnd() {
- tester.assertParsed(
- "RANK (AND (OR fo ba foba) (OR foz baraz)) foobar foo bar baz",
- "foobar +(fo ba foba) foo bar +(foz baraz) baz", Query.Type.ANY);
+ tester.assertParsed("RANK (AND (OR fo ba foba) (OR foz baraz)) foobar foo bar baz",
+ "foobar +(fo ba foba) foo bar +(foz baraz) baz",
+ Query.Type.ANY);
}
@Test
public void testOrMultipleNestedNot() {
- tester.assertParsed(
- "+(OR foobar foo bar baz) -(AND fo ba foba) -(AND foz baraz)",
- "foobar -(fo ba foba) foo bar -(foz baraz) baz", Query.Type.ANY);
+ tester.assertParsed("+(OR foobar foo bar baz) -(AND fo ba foba) -(AND foz baraz)",
+ "foobar -(fo ba foba) foo bar -(foz baraz) baz",
+ Query.Type.ANY);
}
@Test
public void testOrAndNotMultipleNestedAnd() {
- tester.assertParsed(
- "RANK (+(AND baz (OR foo bar baz) (OR foz bazaz) bar) -xyzzy -foobaz) foobar foo",
- "foobar +baz foo -xyzzy +(foo bar baz) -foobaz +(foz bazaz) +bar",
- Query.Type.ANY);
+ tester.assertParsed("RANK (+(AND baz (OR foo bar baz) (OR foz bazaz) bar) -xyzzy -foobaz) foobar foo",
+ "foobar +baz foo -xyzzy +(foo bar baz) -foobaz +(foz bazaz) +bar",
+ Query.Type.ANY);
}
@Test
public void testOrAndNotMultipleNestedNot() {
- tester.assertParsed(
- "RANK (+(AND baz bar) -xyzzy -(AND foo bar baz) -foobaz -(AND foz bazaz)) foobar foo",
- "foobar +baz foo -xyzzy -(foo bar baz) -foobaz -(foz bazaz) +bar",
- Query.Type.ANY);
+ tester.assertParsed("RANK (+(AND baz bar) -xyzzy -(AND foo bar baz) -foobaz -(AND foz bazaz)) foobar foo",
+ "foobar +baz foo -xyzzy -(foo bar baz) -foobaz -(foz bazaz) +bar",
+ Query.Type.ANY);
}
@Test
public void testOrMultipleNestedAndNot() {
- tester.assertParsed(
- "RANK (+(AND (OR ffoooo bbaarr) (OR oof rab raboof)) -(AND fo ba foba) -(AND foz baraz)) foobar foo bar baz",
- "foobar -(fo ba foba) foo +(ffoooo bbaarr) bar +(oof rab raboof) -(foz baraz) baz",
- Query.Type.ANY);
+ tester.assertParsed("RANK (+(AND (OR ffoooo bbaarr) (OR oof rab raboof)) -(AND fo ba foba) -(AND foz baraz)) foobar foo bar baz",
+ "foobar -(fo ba foba) foo +(ffoooo bbaarr) bar +(oof rab raboof) -(foz baraz) baz",
+ Query.Type.ANY);
}
@Test
public void testOrAndNotMultipleNestedAndNot() {
- tester.assertParsed(
- "RANK (+(AND (OR ffoooo bbaarr) (OR oof rab raboof) baz xyxyzzy) -(AND fo ba foba) -foo -bar -(AND foz baraz)) foobar",
- "foobar -(fo ba foba) -foo +(ffoooo bbaarr) -bar +(oof rab raboof) -(foz baraz) +baz +xyxyzzy",
- Query.Type.ANY);
+ tester.assertParsed("RANK (+(AND (OR ffoooo bbaarr) (OR oof rab raboof) baz xyxyzzy) -(AND fo ba foba) -foo -bar -(AND foz baraz)) foobar",
+ "foobar -(fo ba foba) -foo +(ffoooo bbaarr) -bar +(oof rab raboof) -(foz baraz) +baz +xyxyzzy",
+ Query.Type.ANY);
}
@Test
public void testExplicitPhrase() {
- Item root=tester.assertParsed("\"foo bar foobar\"", "\"foo bar foobar\"", Query.Type.ANY);
+ Item root = tester.assertParsed("\"foo bar foobar\"", "\"foo bar foobar\"", Query.Type.ANY);
assertTrue(root instanceof PhraseItem);
assertTrue(((PhraseItem)root).isExplicit());
}
@@ -164,21 +161,20 @@ public class ParseTestCase {
@Test
public void testPhraseWithIndex() {
tester.assertParsed("normal.title:\"foo bar foobar\"",
- "normal.title:\"foo bar foobar\"", Query.Type.ANY);
+ "normal.title:\"foo bar foobar\"", Query.Type.ANY);
}
@Test
public void testPhrasesAndTerms() {
tester.assertParsed("OR \"foo bar foobar\" xyzzy \"baz gaz faz\"",
- "\"foo bar foobar\" xyzzy \"baz gaz faz\"", Query.Type.ANY);
+ "\"foo bar foobar\" xyzzy \"baz gaz faz\"", Query.Type.ANY);
}
@Test
public void testPhrasesAndTermsWithOperators() {
- tester.assertParsed(
- "RANK (+(AND \"baz gaz faz\" bazar) -\"foo bar foobar\") foofoo xyzzy",
- "foofoo -\"foo bar foobar\" xyzzy +\"baz gaz faz\" +bazar",
- Query.Type.ANY);
+ tester.assertParsed("RANK (+(AND \"baz gaz faz\" bazar) -\"foo bar foobar\") foofoo xyzzy",
+ "foofoo -\"foo bar foobar\" xyzzy +\"baz gaz faz\" +bazar",
+ Query.Type.ANY);
}
@Test
@@ -188,38 +184,40 @@ public class ParseTestCase {
@Test
public void testTermWithCatalogAndIndexPrefixDefaultAnd() {
- tester.assertParsed("normal.title:foobar", "normal.title:foobar",
- Query.Type.ALL);
+ tester.assertParsed("normal.title:foobar", "normal.title:foobar", Query.Type.ALL);
}
@Test
public void testMultipleTermsWithMultiplePrefixesDefaultAnd() {
tester.assertParsed("+(AND url:foobar bar) -normal.title:foo -baz",
- "url:foobar +bar -normal.title:foo -baz", Query.Type.ALL);
+ "url:foobar +bar -normal.title:foo -baz",
+ Query.Type.ALL);
}
@Test
public void testSimpleQueryDefaultAnd() {
- tester.assertParsed("AND foobar foo bar baz", "foobar foo bar baz",
- Query.Type.ALL);
+ tester.assertParsed("AND foobar foo bar baz", "foobar foo bar baz", Query.Type.ALL);
}
@Test
public void testNotDefaultAnd() {
- tester.assertParsed(
- "+(AND foobar (OR foo bar baz) xyzzy) -(AND foz baraz bazar)",
- "foobar +(foo bar baz) xyzzy -(foz baraz bazar)", Query.Type.ALL);
+ tester.assertParsed("+(AND foobar (OR foo bar baz) xyzzy) -(AND foz baraz bazar)",
+ "foobar +(foo bar baz) xyzzy -(foz baraz bazar)",
+ Query.Type.ALL);
}
@Test
public void testSimpleTermQueryDefaultPhrase() {
- tester.assertParsed("foobar", "foobar", Query.Type.PHRASE);
+ tester.assertParsed("foobar",
+ "foobar",
+ Query.Type.PHRASE);
}
@Test
public void testSimpleQueryDefaultPhrase() {
- Item root=tester.assertParsed("\"foobar foo bar baz\"", "foobar foo bar baz",
- Query.Type.PHRASE);
+ Item root = tester.assertParsed("\"foobar foo bar baz\"",
+ "foobar foo bar baz",
+ Query.Type.PHRASE);
assertTrue(root instanceof PhraseItem);
assertFalse(((PhraseItem)root).isExplicit());
}
@@ -227,23 +225,25 @@ public class ParseTestCase {
@Test
public void testMultipleTermsWithMultiplePrefixesDefaultPhrase() {
tester.assertParsed("\"url foobar bar normal title foo baz\"",
- "url:foobar +bar -normal.title:foo -baz", Query.Type.PHRASE);
+ "url:foobar +bar -normal.title:foo -baz",
+ Query.Type.PHRASE);
}
@Test
public void testOdd1() {
- tester.assertParsed("AND \"window print\" error", "+window.print() +error",Query.Type.ALL);
+ tester.assertParsed("AND window print error", "+window.print() +error",
+ Query.Type.ALL);
}
@Test
public void testOdd2() {
- tester.assertParsed("normal.title:kaboom", "normal.title:\"kaboom\"",Query.Type.ALL);
+ tester.assertParsed("normal.title:kaboom", "normal.title:\"kaboom\"",
+ Query.Type.ALL);
}
@Test
public void testOdd2Uppercase() {
- tester.assertParsed("normal.title:KABOOM", "NORMAL.TITLE:\"KABOOM\"",
- Query.Type.ALL);
+ tester.assertParsed("normal.title:KABOOM", "NORMAL.TITLE:\"KABOOM\"", Query.Type.ALL);
}
@Test
@@ -280,19 +280,19 @@ public class ParseTestCase {
@Test
public void testNestedCompositesDefaultOr() {
tester.assertParsed("RANK (OR foobar bar baz) foo xyzzy",
- "foo +(foobar +(bar baz)) xyzzy", Query.Type.ANY);
+ "foo +(foobar +(bar baz)) xyzzy", Query.Type.ANY);
}
@Test
public void testNestedCompositesDefaultAnd() {
tester.assertParsed("AND foo (OR foobar bar baz) xyzzy",
- "foo +(foobar +(bar baz)) xyzzy", Query.Type.ALL);
+ "foo +(foobar +(bar baz)) xyzzy", Query.Type.ALL);
}
@Test
public void testNestedCompositesPhraseDefault() {
tester.assertParsed("\"foo foobar bar baz xyzzy\"",
- "foo +(foobar +(bar baz)) xyzzy", Query.Type.PHRASE);
+ "foo +(foobar +(bar baz)) xyzzy", Query.Type.PHRASE);
}
@Test
@@ -349,8 +349,7 @@ public class ParseTestCase {
@Test
public void testNumericWithIndex() {
- tester.assertParsed("document.size:[34;454]", "document.size:[34;454]",
- Query.Type.ANY);
+ tester.assertParsed("document.size:[34;454]", "document.size:[34;454]", Query.Type.ANY);
}
@Test
@@ -361,14 +360,14 @@ public class ParseTestCase {
@Test
public void testMultipleIntegerWithIndex() {
tester.assertParsed("OR document.size:[34;454] date:>1234567890",
- "document.size:[34;454] date:>1234567890", Query.Type.ANY);
+ "document.size:[34;454] date:>1234567890", Query.Type.ANY);
}
@Test
public void testMixedNumericAndOtherTerms() {
tester.assertParsed("RANK (AND document.size:<1024 xyzzy) foo date:>123456890",
- "foo +document.size:<1024 +xyzzy date:>123456890",
- Query.Type.ANY);
+ "foo +document.size:<1024 +xyzzy date:>123456890",
+ Query.Type.ANY);
}
@Test
@@ -378,20 +377,18 @@ public class ParseTestCase {
@Test
public void testItemPhraseEmptyPhrase() {
- tester.assertParsed("RANK to \"or not to be\"", "+to\"or not to be\"\"\"",
- Query.Type.ANY);
+ tester.assertParsed("RANK to \"or not to be\"", "+to\"or not to be\"\"\"", Query.Type.ANY);
}
@Test
public void testSimpleQuery() {
- tester.assertParsed("OR if am \"f g 4 2\" maybe", "if am \" f g 4 2\"\" maybe",
- Query.Type.ANY);
+ tester.assertParsed("OR if am \"f g 4 2\" maybe", "if am \" f g 4 2\"\" maybe", Query.Type.ANY);
}
@Test
public void testExcessivePluses() {
tester.assertParsed("+(AND other is nothing) -test",
- "++other +++++is ++++++nothing -test", Query.Type.ANY);
+ "++other +++++is ++++++nothing -test", Query.Type.ANY);
}
@Test
@@ -401,39 +398,38 @@ public class ParseTestCase {
@Test
public void testPlusesAndMinuses() {
- Item root=tester.assertParsed("\"a b c d d\"", "a+b+c+d--d", Query.Type.ANY);
- assertTrue(root instanceof PhraseItem);
- assertFalse(((PhraseItem)root).isExplicit());
+ tester.assertParsed("AND a b c d d", "a+b+c+d--d", Query.Type.ANY);
}
@Test
public void testNumbers() {
- tester.assertParsed("\"123 2132odfd 934032 32423\"",
- "123+2132odfd.934032,,32423", Query.Type.ANY);
+ tester.assertParsed("AND 123 2132odfd 934032 32423", "123+2132odfd.934032,,32423", Query.Type.ANY);
}
@Test
public void testOtherSignsInQuote() {
- tester.assertParsed("\"0032 4 320 24329043\"", "0032+4\\320.24329043",
- Query.Type.ANY);
+ tester.assertParsed("AND 0032 4 320 24329043", "0032+4\\320.24329043", Query.Type.ANY);
}
@Test
public void testGribberish() {
tester.assertParsed("1349832840234l3040roer\u00e6lf12",
- ",1349832840234l3040roer\u00e6lf12", Query.Type.ANY);
+ ",1349832840234l3040roer\u00e6lf12",
+ Query.Type.ANY);
}
@Test
public void testUrl() {
- tester.assertParsed("www:\"www hotelaiguablava com\"",
- "+www:www.hotelaiguablava:com", Query.Type.ANY);
+ tester.assertParsed("AND www:www www:hotelaiguablava www:com",
+ "+www:www.hotelaiguablava:com",
+ Query.Type.ANY);
}
@Test
public void testUrlGribberish() {
- tester.assertParsed("OR \"3 16\" fast.type:lycosoffensive",
- "[ 3:16 fast.type:lycosoffensive", Query.Type.ANY);
+ tester.assertParsed("OR (AND 3 16) fast.type:lycosoffensive",
+ "[ 3:16 fast.type:lycosoffensive",
+ Query.Type.ANY);
}
@Test
@@ -475,8 +471,7 @@ public class ParseTestCase {
@Test
public void testPrefixWithDotAdvanced() {
- tester.assertParsed("normal.title:foobar", "normal.title:foobar",
- Query.Type.ADVANCED);
+ tester.assertParsed("normal.title:foobar", "normal.title:foobar", Query.Type.ADVANCED);
}
@Test
@@ -486,20 +481,21 @@ public class ParseTestCase {
@Test
public void testSimplePhraseAdvanced() {
- tester.assertParsed("\"foo bar foobar\"", "\"foo bar foobar\"",
- Query.Type.ADVANCED);
+ tester.assertParsed("\"foo bar foobar\"", "\"foo bar foobar\"", Query.Type.ADVANCED);
}
@Test
public void testSimplePhraseWithIndexAdvanced() {
tester.assertParsed("normal.title:\"foo bar foobar\"",
- "normal.title:\"foo bar foobar\"", Query.Type.ADVANCED);
+ "normal.title:\"foo bar foobar\"",
+ Query.Type.ADVANCED);
}
@Test
public void testMultiplePhrasesAdvanced() {
tester.assertParsed("AND \"foo bar foobar\" \"baz gaz faz\"",
- "\"foo bar foobar\" and \"baz gaz faz\"", Query.Type.ADVANCED);
+ "\"foo bar foobar\" and \"baz gaz faz\"",
+ Query.Type.ADVANCED);
}
@Test
@@ -661,23 +657,23 @@ public class ParseTestCase {
@Test
public void testImplicitPhrase1Advanced() {
- tester.assertParsed("\"test if\"", "--test+-if", Query.Type.ADVANCED);
+ tester.assertParsed("AND test if", "--test+-if", Query.Type.ADVANCED);
}
@Test
public void testImplicitPhrase2Advanced() {
- tester.assertParsed("\"a b c d d\"", "a+b+c+d--d", Query.Type.ADVANCED);
+ tester.assertParsed("AND a b c d d", "a+b+c+d--d", Query.Type.ADVANCED);
}
@Test
public void testImplicitPhrase3Advanced() {
- tester.assertParsed("\"123 2132odfd 934032 32423\"",
+ tester.assertParsed("AND 123 2132odfd 934032 32423",
"123+2132odfd.934032,,32423", Query.Type.ADVANCED);
}
@Test
public void testImplicitPhrase4Advanced() {
- tester.assertParsed("\"0032 4 320 24329043\"", "0032+4\\320.24329043", Query.Type.ADVANCED);
+ tester.assertParsed("AND 0032 4 320 24329043", "0032+4\\320.24329043", Query.Type.ADVANCED);
}
@Test
@@ -730,7 +726,7 @@ public class ParseTestCase {
@Test
public void testSingleHyphen() {
- tester.assertParsed("\"a b\"", "a-b", Query.Type.ALL);
+ tester.assertParsed("AND a b", "a-b", Query.Type.ALL);
}
@Test
@@ -883,27 +879,27 @@ public class ParseTestCase {
@Test
public void testSimpleDotPhraseAny() {
- tester.assertParsed("OR a \"b c\" d", "a b.c d", Query.Type.ANY);
+ tester.assertParsed("OR a (AND b c) d", "a b.c d", Query.Type.ANY);
}
@Test
public void testSimpleHyphenPhraseAny() {
- tester.assertParsed("OR a \"b c\" d", "a b-c d", Query.Type.ANY);
+ tester.assertParsed("OR a (AND b c) d", "a b-c d", Query.Type.ANY);
}
@Test
public void testAnotherSimpleDotPhraseAny() {
- tester.assertParsed("OR \"a b\" c d", "a.b c d", Query.Type.ANY);
+ tester.assertParsed("OR (AND a b) c d", "a.b c d", Query.Type.ANY);
}
@Test
public void testYetAnotherSimpleDotPhraseAny() {
- tester.assertParsed("OR a b \"c d\"", "a b c.d", Query.Type.ANY);
+ tester.assertParsed("OR a b (AND c d)", "a b c.d", Query.Type.ANY);
}
@Test
public void testVariousSeparatorsPhraseAny() {
- tester.assertParsed("\"a b c d\"", "a-b.c%d", Query.Type.ANY);
+ tester.assertParsed("AND a b c d", "a-b.c%d", Query.Type.ANY);
}
@Test
@@ -918,45 +914,44 @@ public class ParseTestCase {
@Test
public void testIndexedDottedPhraseAny() {
- tester.assertParsed("OR a url:\"b c\" d", "a url:b.c d", Query.Type.ANY);
+ tester.assertParsed("OR a (AND url:b url:c) d", "a url:b.c d", Query.Type.ANY);
}
@Test
public void testIndexedPlusedPhraseAny() {
- tester.assertParsed("OR a normal.title:\"b c\" d", "a normal.title:b+c d",
- Query.Type.ANY);
+ tester.assertParsed("OR a (AND normal.title:b normal.title:c) d", "a normal.title:b+c d", Query.Type.ANY);
}
@Test
public void testNestedNotAny() {
tester.assertParsed(
- "RANK (+(OR normal.title:foobar url:\"www pvv org\") -foo) a",
+ "RANK (+(OR normal.title:foobar (AND url:www url:pvv url:org)) -foo) a",
"a +(normal.title:foobar url:www.pvv.org) -foo", Query.Type.ANY);
}
@Test
public void testDottedPhraseAdvanced() {
- tester.assertParsed("OR a \"b c\"", "a or b.c", Query.Type.ADVANCED);
+ tester.assertParsed("OR a (AND b c)", "a or b.c", Query.Type.ADVANCED);
}
@Test
public void testHyphenPhraseAdvanced() {
- tester.assertParsed("OR (AND a \"b c\") d", "a and b-c or d", Query.Type.ADVANCED);
+ tester.assertParsed("OR (AND a (AND b c)) d", "a and b-c or d", Query.Type.ADVANCED);
}
@Test
public void testAnotherDottedPhraseAdvanced() {
- tester.assertParsed("OR \"a b\" c", "a.b or c", Query.Type.ADVANCED);
+ tester.assertParsed("OR (AND a b) c", "a.b or c", Query.Type.ADVANCED);
}
@Test
public void testNottedDottedPhraseAdvanced() {
- tester.assertParsed("+a -\"c d\"", "a andnot c.d", Query.Type.ADVANCED);
+ tester.assertParsed("+a -(AND c d)", "a andnot c.d", Query.Type.ADVANCED);
}
@Test
public void testVariousSeparatorsPhraseAdvanced() {
- tester.assertParsed("\"a b c d\"", "a-b.c%d", Query.Type.ADVANCED);
+ tester.assertParsed("AND a b c d", "a-b.c%d", Query.Type.ADVANCED);
}
@Test
@@ -976,14 +971,14 @@ public class ParseTestCase {
@Test
public void testNestedPlussedPhraseAdvanced() {
- tester.assertParsed("AND (OR a normal.title:\"b c\") d",
+ tester.assertParsed("AND (OR a (AND normal.title:b normal.title:c)) d",
"a or normal.title:b+c and d", Query.Type.ADVANCED);
}
@Test
public void testNottedNestedDottedPhraseAdvanced() {
tester.assertParsed(
- "+(AND a (OR normal.title:foobar url:\"www pvv org\")) -foo",
+ "+(AND a (OR normal.title:foobar (AND url:www url:pvv url:org))) -foo",
"a and (normal.title:foobar or url:www.pvv.org) andnot foo",
Query.Type.ADVANCED);
}
@@ -995,7 +990,7 @@ public class ParseTestCase {
@Test
public void testPlusedTwiceThenQuotedPhraseAny() {
- tester.assertParsed("\"a b c d\"", "a+b+\"c d\"", Query.Type.ANY);
+ tester.assertParsed("AND a b c d", "a+b+\"c d\"", Query.Type.ANY);
}
@Test
@@ -1005,7 +1000,7 @@ public class ParseTestCase {
@Test
public void testPhrasesInBraces() {
- tester.assertParsed("url.domain:\"microsoft com\"",
+ tester.assertParsed("AND url.domain:microsoft url.domain:com",
"+(url.domain:microsoft.com)", Query.Type.ALL);
}
@@ -1053,17 +1048,17 @@ public class ParseTestCase {
@Test
public void testPhraseNotPrefix() {
- tester.assertParsed("OR foo \"prefix bar\"", "foo prefix*bar", Query.Type.ANY);
+ tester.assertParsed("OR foo (AND prefix bar)", "foo prefix*bar", Query.Type.ANY);
}
@Test
public void testPhraseNotSubstring() {
- tester.assertParsed("OR foo \"substring bar\"", "foo *substring*bar", Query.Type.ANY);
+ tester.assertParsed("OR foo (AND substring bar)", "foo *substring*bar", Query.Type.ANY);
}
@Test
public void testPhraseNotSuffix() {
- tester.assertParsed("OR \"foo suffix\" bar", "foo*suffix bar", Query.Type.ANY);
+ tester.assertParsed("OR (AND foo suffix) bar", "foo*suffix bar", Query.Type.ANY);
}
@Test
@@ -1086,20 +1081,17 @@ public class ParseTestCase {
@Test
public void testIndexedPhraseNotPrefix() {
- tester.assertParsed("foo.bar:\"prefix xyzzy\"", "foo.bar:prefix*xyzzy",
- Query.Type.ANY);
+ tester.assertParsed("AND foo.bar:prefix foo.bar:xyzzy", "foo.bar:prefix*xyzzy", Query.Type.ANY);
}
@Test
public void testIndexedPhraseNotSubstring() {
- tester.assertParsed("foo.bar:\"substring xyzzy\"", "foo.bar:*substring*xyzzy",
- Query.Type.ANY);
+ tester.assertParsed("AND foo.bar:substring foo.bar:xyzzy", "foo.bar:*substring*xyzzy", Query.Type.ANY);
}
@Test
public void testIndexedPhraseNotSuffix() {
- tester.assertParsed("foo.bar:\"xyzzy suffix\"", "foo.bar:xyzzy*suffix",
- Query.Type.ANY);
+ tester.assertParsed("AND foo.bar:xyzzy foo.bar:suffix", "foo.bar:xyzzy*suffix", Query.Type.ANY);
}
@Test
@@ -1120,20 +1112,20 @@ public class ParseTestCase {
assertTrue(root instanceof SuffixItem);
}
- /** Non existing index → phrase **/
+ /** Non existing index → and **/
@Test
public void testNonIndexPhraseNotPrefix() {
- tester.assertParsed("\"void prefix\"", "void:prefix*", Query.Type.ANY);
+ tester.assertParsed("AND void prefix", "void:prefix*", Query.Type.ANY);
}
@Test
public void testNonIndexPhraseNotSubstring() {
- tester.assertParsed("\"void substring\"", "void:*substring*", Query.Type.ANY);
+ tester.assertParsed("AND void substring", "void:*substring*", Query.Type.ANY);
}
@Test
public void testNonIndexPhraseNotSuffix() {
- tester.assertParsed("\"void suffix\"", "void:*suffix", Query.Type.ANY);
+ tester.assertParsed("AND void suffix", "void:*suffix", Query.Type.ANY);
}
/** Explicit phrase → remove '*' **/
@@ -1198,7 +1190,7 @@ public class ParseTestCase {
/** Extra spaces with index **/
@Test
public void testIndexPrefixExtraSpace() {
- tester.assertParsed("\"foo prefix\"", "foo:prefix *", Query.Type.ANY);
+ tester.assertParsed("AND foo prefix", "foo:prefix *", Query.Type.ANY);
}
@Test
@@ -1419,7 +1411,7 @@ public class ParseTestCase {
@Test
public void testMultipleDifferentPhraseSeparators() {
- tester.assertParsed("\"foo bar\"", "foo.-.bar", Query.Type.ANY);
+ tester.assertParsed("AND foo bar", "foo.-.bar", Query.Type.ANY);
}
@Test
@@ -1430,19 +1422,17 @@ public class ParseTestCase {
@Test
public void testReallyNoisyQuery1() {
- tester.assertParsed("AND word another", "&word\"()/&#)(/&another!\"",
- Query.Type.ALL);
+ tester.assertParsed("AND word another", "&word\"()/&#)(/&another!\"", Query.Type.ALL);
}
@Test
public void testReallyNoisyQuery2() {
- tester.assertParsed("\"\u03bc\u03bc hei\"", "&&&`\u00b5\u00b5=@hei", Query.Type.ALL);
+ tester.assertParsed("AND \u03bc\u03bc hei", "&&&`\u00b5\u00b5=@hei", Query.Type.ALL);
}
@Test
public void testReallyNoisyQuery3() {
- tester.assertParsed("AND \"hei hallo\" du der", "hei-hallo;du;der",
- Query.Type.ALL);
+ tester.assertParsed("AND hei hallo du der", "hei-hallo;du;der", Query.Type.ALL);
}
@Test
@@ -1478,7 +1468,7 @@ public class ParseTestCase {
@Test
public void testTheStupidSymbolsWhichAreNowWordCharactersInUnicode() {
- tester.assertParsed("\"yz a\"", "yz\u00A8\u00AA\u00AF", Query.Type.ANY);
+ tester.assertParsed("AND yz a", "yz\u00A8\u00AA\u00AF", Query.Type.ANY);
}
@Test
@@ -1498,7 +1488,7 @@ public class ParseTestCase {
@Test
public void testImplicitPhrasingWithIndex() {
- tester.assertParsed("a:\"b c\"", "a:/b/c", Query.Type.ANY);
+ tester.assertParsed("AND a:b a:c", "a:/b/c", Query.Type.ANY);
}
@Test
@@ -1508,7 +1498,7 @@ public class ParseTestCase {
@Test
public void testSingleNoisyPhraseWithIndex() {
- tester.assertParsed("mail:\"yahoo com\"", "mail:@yahoo.com", Query.Type.ANY);
+ tester.assertParsed("AND mail:yahoo mail:com", "mail:@yahoo.com", Query.Type.ANY);
}
@Test
@@ -1599,7 +1589,7 @@ public class ParseTestCase {
"url.all:http://www.newsadvance.com/servlet/Satellite?pagename=LNA/MGArticle/IMD_BasicArticle&c=MGArticle&cid=1031782787014&path=!mgnetwork!diversions",
Query.Type.ALL);
tester.assertParsed(
- "AND ull:\"http www neue oz de information pub Boulevard index html file a 3 s 4 file\" s:\"37 iptc bdt 20050607 294 dpa 9001170 txt\" s:\"3 dir\" s:\"26 opt DPA parsed boulevard\" s:\"7 bereich\" s:\"9 Boulevard\"",
+ "AND ull:http ull:www ull:neue ull:oz ull:de ull:information ull:pub ull:Boulevard ull:index ull:html ull:file ull:a ull:3 ull:s ull:4 ull:file s:\"37 iptc bdt 20050607 294 dpa 9001170 txt\" s:\"3 dir\" s:\"26 opt DPA parsed boulevard\" s:\"7 bereich\" s:\"9 Boulevard\"",
"ull:http://www.neue-oz.de/information/pub_Boulevard/index.html?file=a:3:{s:4:\"file\";s:37:\"iptc-bdt-20050607-294-dpa_9001170.txt\";s:3:\"dir\";s:26:\"/opt/DPA/parsed/boulevard/\";s:7:\"bereich\";s:9:\"Boulevard\";}",
Query.Type.ALL);
}
@@ -1640,7 +1630,7 @@ public class ParseTestCase {
@Test
public void testTooLongQueryTerms() {
- tester.assertParsed("AND \"545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof filter ew 545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof\"!1000 \"2b 2f 545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof\"",
+ tester.assertParsed("AND 545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof filter ew 545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof 2b 2f 545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof",
"+/545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof&filter=ew:545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof!1000 =.2b..2f.545558598787gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggcfffffffffffffffffffffffffffffffffffffffffffccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlkjhcxxdfffxdzzaqwwsxedcrfvtgbyhnujmikkiloolpppof",
Query.Type.ALL);
}
@@ -1648,22 +1638,22 @@ public class ParseTestCase {
@Test
public void testNonSpecialTokenParsing() {
ParsingTester customTester = new ParsingTester(new SpecialTokens("default"));
- customTester.assertParsed("OR c or c with \"tcp ip\"", "c# or c++ with tcp/ip", Query.Type.ANY);
+ customTester.assertParsed("OR c or c with (AND tcp ip)", "c# or c++ with tcp/ip", Query.Type.ANY);
}
@Test
public void testNonIndexWithColons1() {
- tester.assertParsed("OR this is \"notan iindex\"", "this is notan:iindex", Query.Type.ANY);
+ tester.assertParsed("OR this is (AND notan iindex)", "this is notan:iindex", Query.Type.ANY);
}
@Test
public void testNonIndexWithColons2() {
- tester.assertParsed("OR this is \"notan iindex either\"", "this is notan:iindex:either", Query.Type.ANY);
+ tester.assertParsed("OR this is (AND notan iindex either)", "this is notan:iindex:either", Query.Type.ANY);
}
@Test
public void testIndexThenUnderscoreTermBecomesIndex() {
- tester.assertParsed("name:\"batch article\"", "name:batch_article", Query.Type.ANY);
+ tester.assertParsed("AND name:batch name:article", "name:batch_article", Query.Type.ANY);
}
@Test
@@ -1671,17 +1661,17 @@ public class ParseTestCase {
// "first" "second" and "third" are segments in the test language
Item item = tester.parseQuery("name:firstsecondthird", null, Language.CHINESE_SIMPLIFIED, Query.Type.ANY, TestLinguistics.INSTANCE);
- assertTrue(item instanceof PhraseSegmentItem);
- PhraseSegmentItem phrase = (PhraseSegmentItem) item;
+ assertTrue(item instanceof AndSegmentItem);
+ AndSegmentItem segment = (AndSegmentItem) item;
- assertEquals(3, phrase.getItemCount());
- assertEquals("name:first", phrase.getItem(0).toString());
- assertEquals("name:second", phrase.getItem(1).toString());
- assertEquals("name:third", phrase.getItem(2).toString());
+ assertEquals(3, segment.getItemCount());
+ assertEquals("name:first", segment.getItem(0).toString());
+ assertEquals("name:second", segment.getItem(1).toString());
+ assertEquals("name:third", segment.getItem(2).toString());
- assertEquals("name", ((WordItem) phrase.getItem(0)).getIndexName());
- assertEquals("name", ((WordItem) phrase.getItem(1)).getIndexName());
- assertEquals("name", ((WordItem) phrase.getItem(2)).getIndexName());
+ assertEquals("name", ((WordItem) segment.getItem(0)).getIndexName());
+ assertEquals("name", ((WordItem) segment.getItem(1)).getIndexName());
+ assertEquals("name", ((WordItem) segment.getItem(2)).getIndexName());
}
@Test
@@ -1689,22 +1679,22 @@ public class ParseTestCase {
// "first" "second" and "third" are segments in the test language
Item item = tester.parseQuery("name:\"firstsecondthird\"", null, Language.CHINESE_SIMPLIFIED, Query.Type.ANY, TestLinguistics.INSTANCE);
- assertTrue(item instanceof PhraseSegmentItem);
- PhraseSegmentItem phrase = (PhraseSegmentItem) item;
+ assertTrue(item instanceof AndSegmentItem);
+ AndSegmentItem segment = (AndSegmentItem) item;
- assertEquals(3, phrase.getItemCount());
- assertEquals("name:first", phrase.getItem(0).toString());
- assertEquals("name:second", phrase.getItem(1).toString());
- assertEquals("name:third", phrase.getItem(2).toString());
+ assertEquals(3, segment.getItemCount());
+ assertEquals("name:first", segment.getItem(0).toString());
+ assertEquals("name:second", segment.getItem(1).toString());
+ assertEquals("name:third", segment.getItem(2).toString());
- assertEquals("name", ((WordItem) phrase.getItem(0)).getIndexName());
- assertEquals("name", ((WordItem) phrase.getItem(1)).getIndexName());
- assertEquals("name", ((WordItem)phrase.getItem(2)).getIndexName());
+ assertEquals("name", ((WordItem) segment.getItem(0)).getIndexName());
+ assertEquals("name", ((WordItem) segment.getItem(1)).getIndexName());
+ assertEquals("name", ((WordItem)segment.getItem(2)).getIndexName());
}
@Test
public void testAndItemAndImplicitPhrase() {
- tester.assertParsed("\"\u00d8 \u00d8 \u00d8 \u00d9\"",
+ tester.assertParsed("AND \u00d8 \u00d8 \u00d8 \u00d9",
"\u00d8\u00b9\u00d8\u00b1\u00d8\u00a8\u00d9", "",
Query.Type.ALL, Language.CHINESE_SIMPLIFIED);
}
@@ -1736,7 +1726,7 @@ public class ParseTestCase {
@Test
public void testFakeCJKSegmentingOfMultiplePhrases() {
Item item = tester.parseQuery("name:firstsecond.s", null, Language.CHINESE_SIMPLIFIED, Query.Type.ANY, TestLinguistics.INSTANCE);
- assertEquals("name:\"'first second' s\"", item.toString());
+ assertEquals("AND (SAND name:first name:second) name:s", item.toString());
}
@Test
@@ -1801,7 +1791,7 @@ public class ParseTestCase {
@Test
public void testCommaOnlyLeadsToImplicitPhrasing() {
- tester.assertParsed("\"A B C\"", "A,B,C", Query.Type.ALL);
+ tester.assertParsed("AND A B C", "A,B,C", Query.Type.ALL);
}
@Test
@@ -1873,8 +1863,8 @@ public class ParseTestCase {
@Test
public void testJPMobileExceptionQuery() {
- tester.assertParsed("OR concat and \"make string\" 1 47 or",
- "(concat \"and\" (make-string 1 47) \"or\")", Query.Type.ALL);
+ tester.assertParsed("OR concat and (AND make string) 1 47 or",
+ "(concat \"and\" (make-string 1 47) \"or\")", Query.Type.ALL);
}
@Test
@@ -1882,7 +1872,7 @@ public class ParseTestCase {
tester.assertParsed("b", "a: b", Query.Type.ALL);
tester.assertParsed("AND a b", "a : b", Query.Type.ALL);
tester.assertParsed("AND a b", "a :b", Query.Type.ALL);
- tester.assertParsed("\"a b\"", "a.:b", Query.Type.ALL);
+ tester.assertParsed("AND a b", "a.:b", Query.Type.ALL);
tester.assertParsed("a:b", "a:b", Query.Type.ALL);
}
@@ -1917,8 +1907,7 @@ public class ParseTestCase {
tester.assertParsed("AND ringtone AND (OR a:\"Delivery SMAF large max 150kB 063\" OR a:\"RealMusic Delivery\")",
"ringtone AND (a:\"Delivery SMAF large max.150kB (063)\" OR a:\"RealMusic Delivery\" )",
Query.Type.ALL);
- // The last one here is a little weird, but it's not a problem,
- // so I let it pass for now...
+ // The last one here is a little weird, but it's not a problem, so let it pass for now...
tester.assertParsed("OR (OR ringtone AND) (OR a:\"Delivery SMAF large max 150kB 063\" OR a:\"RealMusic Delivery\")",
"ringtone AND (a:\"Delivery SMAF large max.150kB (063)\" OR a:\"RealMusic Delivery\" )",
Query.Type.ANY);
@@ -1926,7 +1915,7 @@ public class ParseTestCase {
@Test
public void testMixedCaseIndexNames() {
- tester.assertParsed("AND mixedCase:a mixedCase:b \"notAnIndex c\" mixedCase:d",
+ tester.assertParsed("AND mixedCase:a mixedCase:b notAnIndex c mixedCase:d",
"mixedcase:a MIXEDCASE:b notAnIndex:c mixedCase:d",
Query.Type.ALL);
}
@@ -1934,7 +1923,7 @@ public class ParseTestCase {
/** CJK special tokens should be recognized also on non-boundaries */
@Test
public void testChineseSpecialTokens() {
- tester.assertParsed("AND \"cat tcp/ip zu\" \"foo dotnet bar dotnet dotnet c# c++ bar dotnet dotnet wiz\"",
+ tester.assertParsed("AND cat tcp/ip zu foo dotnet bar dotnet dotnet c# c++ bar dotnet dotnet wiz",
"cattcp/ipzu foo.netbar.net.netC#c++bar.net.netwiz","", Query.Type.ALL, Language.CHINESE_SIMPLIFIED);
}
@@ -1945,7 +1934,7 @@ public class ParseTestCase {
@Test
public void testChineseSpecialTokensWithMultiSegmentReplace() {
// special-token-fs is a special token, to be replaced by firstsecond, first and second are segments in test
- tester.assertParsed("AND \"tcp/ip firstsecond dotnet\" firstsecond 'first second'","tcp/ipspecial-token-fs.net special-token-fs firstsecond",
+ tester.assertParsed("AND tcp/ip firstsecond dotnet firstsecond (SAND first second)","tcp/ipspecial-token-fs.net special-token-fs firstsecond",
"", Query.Type.ALL, Language.CHINESE_SIMPLIFIED, TestLinguistics.INSTANCE);
}
@@ -2014,7 +2003,7 @@ public class ParseTestCase {
@Test
public void testVersionNumbers() {
- tester.assertParsed("\"1 0 9\"", "1.0.9", Query.Type.ALL);
+ tester.assertParsed("AND 1 0 9", "1.0.9", Query.Type.ALL);
}
@Test
@@ -2321,7 +2310,7 @@ public class ParseTestCase {
@Test
public void testOdd1Web() {
- tester.assertParsed("AND \"window print\" error", "+window.print() +error",Query.Type.WEB);
+ tester.assertParsed("AND window print error", "+window.print() +error",Query.Type.WEB);
}
@Test
@@ -2351,13 +2340,13 @@ public class ParseTestCase {
@Test
public void testDefaultWebIndices() {
- tester.assertParsed("\"notanindex b\"","notanindex:b",Query.Type.WEB);
- tester.assertParsed("site:\"b $\"","site:b",Query.Type.WEB);
- tester.assertParsed("hostname:b","hostname:b",Query.Type.WEB);
- tester.assertParsed("link:b","link:b",Query.Type.WEB);
- tester.assertParsed("url:b","url:b",Query.Type.WEB);
- tester.assertParsed("inurl:b","inurl:b",Query.Type.WEB);
- tester.assertParsed("intitle:b","intitle:b",Query.Type.WEB);
+ tester.assertParsed("AND notanindex b","notanindex:b", Query.Type.WEB);
+ tester.assertParsed("site:\"b $\"","site:b", Query.Type.WEB);
+ tester.assertParsed("hostname:b","hostname:b", Query.Type.WEB);
+ tester.assertParsed("link:b","link:b", Query.Type.WEB);
+ tester.assertParsed("url:b","url:b", Query.Type.WEB);
+ tester.assertParsed("inurl:b","inurl:b", Query.Type.WEB);
+ tester.assertParsed("intitle:b","intitle:b", Query.Type.WEB);
}
@Test
@@ -2527,7 +2516,7 @@ public class ParseTestCase {
@Test
public void testSiteAndSegmentPhrasesFollowedByText() {
- tester.assertParsed("AND host.all:\"www abc com x y-z $\" 'a b'",
+ tester.assertParsed("AND host.all:\"www abc com x y-z $\" (SAND a b)",
"host.all:www.abc.com/x'y-z a'b", "",
Query.Type.ALL, Language.ENGLISH);
}
@@ -2544,7 +2533,7 @@ public class ParseTestCase {
@Test
public void testNonAsciiNumber() {
- tester.assertParsed("title:\"199 119 201 149\"", "title:199.119.201.149", Query.Type.ALL);
+ tester.assertParsed("AND title:199 title:119 title:201 title:149", "title:199.119.201.149", Query.Type.ALL);
}
}
diff --git a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/CJKSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/CJKSearcherTestCase.java
index 91cf5015cba..0ca4b8aa615 100644
--- a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/CJKSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/CJKSearcherTestCase.java
@@ -45,7 +45,7 @@ public class CJKSearcherTestCase {
@Test
public void testCjkQueryWithOverlappingTokens() {
// The test language segmenter will segment "bcd" into the overlapping tokens "bc" "cd"
- assertTransformed("bcd", "'bc cd'", Query.Type.ALL, Language.CHINESE_SIMPLIFIED, Language.CHINESE_TRADITIONAL,
+ assertTransformed("bcd", "SAND bc cd", Query.Type.ALL, Language.CHINESE_SIMPLIFIED, Language.CHINESE_TRADITIONAL,
TestLinguistics.INSTANCE);
// While "efg" will be segmented into one of the standard options, "e" "fg"
diff --git a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/LiteralBoostSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/LiteralBoostSearcherTestCase.java
index 12e756a07ee..023cd3c2849 100644
--- a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/LiteralBoostSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/LiteralBoostSearcherTestCase.java
@@ -71,7 +71,7 @@ public class LiteralBoostSearcherTestCase {
@Test
public void testQueryWithoutBoost() {
- assertEquals("RANK (AND \"nonexistant a\" \"nonexistant b\") default_literal:nonexistant default_literal:a default_literal:nonexistant default_literal:b",
+ assertEquals("RANK (AND nonexistant a nonexistant b) default_literal:nonexistant default_literal:a default_literal:nonexistant default_literal:b",
transformQuery("?query=nonexistant:a nonexistant:b&source=cluster1&restrict=type1"));
}
diff --git a/container-search/src/test/java/com/yahoo/prelude/searcher/test/FieldCollapsingSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/searcher/test/FieldCollapsingSearcherTestCase.java
index 4875121a501..12619bf0a5e 100644
--- a/container-search/src/test/java/com/yahoo/prelude/searcher/test/FieldCollapsingSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/searcher/test/FieldCollapsingSearcherTestCase.java
@@ -40,34 +40,8 @@ import static org.junit.Assert.assertTrue;
*
* @author Steinar Knutsen
*/
-@SuppressWarnings("deprecation")
public class FieldCollapsingSearcherTestCase {
- private FastHit createHit(String uri,int relevancy,int mid) {
- FastHit hit = new FastHit(uri,relevancy);
- hit.setField("amid", String.valueOf(mid));
- return hit;
- }
-
- private void assertHit(String uri,int relevancy,int mid,Hit hit) {
- assertEquals(uri,hit.getId().toString());
- assertEquals(relevancy, ((int) hit.getRelevance().getScore()));
- assertEquals(mid,Integer.parseInt((String) hit.getField("amid")));
- }
-
- private static class ZeroHitsControl extends com.yahoo.search.Searcher {
- public int queryCount = 0;
- public com.yahoo.search.Result search(com.yahoo.search.Query query,
- com.yahoo.search.searchchain.Execution execution) {
- ++queryCount;
- if (query.getHits() == 0) {
- return new Result(query);
- } else {
- return new Result(query, ErrorMessage.createIllegalQuery("Did not request zero hits."));
- }
- }
- }
-
@Test
public void testFieldCollapsingWithoutHits() {
// Set up
@@ -116,14 +90,14 @@ public class FieldCollapsingSearcherTestCase {
// The searcher turns off collapsing further on in the chain
q.properties().set("collapse", "0");
Result r = new Result(q);
- r.hits().add(createHit("http://acme.org/a.html",10,0));
- r.hits().add(createHit("http://acme.org/b.html", 9,0));
- r.hits().add(createHit("http://acme.org/c.html", 9,1));
- r.hits().add(createHit("http://acme.org/d.html", 8,1));
- r.hits().add(createHit("http://acme.org/e.html", 8,2));
- r.hits().add(createHit("http://acme.org/f.html", 7,2));
- r.hits().add(createHit("http://acme.org/g.html", 7,3));
- r.hits().add(createHit("http://acme.org/h.html", 6,3));
+ r.hits().add(createHit("http://acme.org/a.html",10, 0));
+ r.hits().add(createHit("http://acme.org/b.html", 9, 0));
+ r.hits().add(createHit("http://acme.org/c.html", 9, 1));
+ r.hits().add(createHit("http://acme.org/d.html", 8, 1));
+ r.hits().add(createHit("http://acme.org/e.html", 8, 2));
+ r.hits().add(createHit("http://acme.org/f.html", 7, 2));
+ r.hits().add(createHit("http://acme.org/g.html", 7, 3));
+ r.hits().add(createHit("http://acme.org/h.html", 6, 3));
r.setTotalHitCount(8);
docsource.addResult(q, r);
@@ -133,10 +107,10 @@ public class FieldCollapsingSearcherTestCase {
assertEquals(4, r.getHitCount());
assertEquals(1, docsource.getQueryCount());
- assertHit("http://acme.org/a.html",10,0,r.hits().get(0));
- assertHit("http://acme.org/c.html", 9,1,r.hits().get(1));
- assertHit("http://acme.org/e.html", 8,2,r.hits().get(2));
- assertHit("http://acme.org/g.html", 7,3,r.hits().get(3));
+ assertHit("http://acme.org/a.html",10, 0, r.hits().get(0));
+ assertHit("http://acme.org/c.html", 9, 1, r.hits().get(1));
+ assertHit("http://acme.org/e.html", 8, 2, r.hits().get(2));
+ assertHit("http://acme.org/g.html", 7, 3, r.hits().get(3));
}
@Test
@@ -152,14 +126,14 @@ public class FieldCollapsingSearcherTestCase {
// The searcher turns off collapsing further on in the chain
q.properties().set("collapse", "0");
Result r = new Result(q);
- r.hits().add(createHit("http://acme.org/a.html",10,0));
- r.hits().add(createHit("http://acme.org/b.html", 9,0));
- r.hits().add(createHit("http://acme.org/c.html", 9,1));
- r.hits().add(createHit("http://acme.org/d.html", 8,1));
- r.hits().add(createHit("http://acme.org/e.html", 8,2));
- r.hits().add(createHit("http://acme.org/f.html", 7,2));
- r.hits().add(createHit("http://acme.org/g.html", 7,3));
- r.hits().add(createHit("http://acme.org/h.html", 6,3));
+ r.hits().add(createHit("http://acme.org/a.html",10, 0));
+ r.hits().add(createHit("http://acme.org/b.html", 9, 0));
+ r.hits().add(createHit("http://acme.org/c.html", 9, 1));
+ r.hits().add(createHit("http://acme.org/d.html", 8, 1));
+ r.hits().add(createHit("http://acme.org/e.html", 8, 2));
+ r.hits().add(createHit("http://acme.org/f.html", 7, 2));
+ r.hits().add(createHit("http://acme.org/g.html", 7, 3));
+ r.hits().add(createHit("http://acme.org/h.html", 6, 3));
r.setTotalHitCount(8);
docsource.addResult(q, r);
@@ -169,10 +143,10 @@ public class FieldCollapsingSearcherTestCase {
assertEquals(4, r.getHitCount());
assertEquals(1, docsource.getQueryCount());
- assertHit("http://acme.org/a.html",10,0,r.hits().get(0));
- assertHit("http://acme.org/c.html", 9,1,r.hits().get(1));
- assertHit("http://acme.org/e.html", 8,2,r.hits().get(2));
- assertHit("http://acme.org/g.html", 7,3,r.hits().get(3));
+ assertHit("http://acme.org/a.html",10,0, r.hits().get(0));
+ assertHit("http://acme.org/c.html", 9,1, r.hits().get(1));
+ assertHit("http://acme.org/e.html", 8,2, r.hits().get(2));
+ assertHit("http://acme.org/g.html", 7,3, r.hits().get(3));
}
@Test
@@ -185,14 +159,14 @@ public class FieldCollapsingSearcherTestCase {
Query q = new Query("?query=test_collapse");
Result r = new Result(q);
- r.hits().add(createHit("http://acme.org/a.html",10,0));
- r.hits().add(createHit("http://acme.org/b.html", 9,0));
- r.hits().add(createHit("http://acme.org/c.html", 9,1));
- r.hits().add(createHit("http://acme.org/d.html", 8,1));
- r.hits().add(createHit("http://acme.org/e.html", 8,2));
- r.hits().add(createHit("http://acme.org/f.html", 7,2));
- r.hits().add(createHit("http://acme.org/g.html", 7,3));
- r.hits().add(createHit("http://acme.org/h.html", 6,3));
+ r.hits().add(createHit("http://acme.org/a.html",10, 0));
+ r.hits().add(createHit("http://acme.org/b.html", 9, 0));
+ r.hits().add(createHit("http://acme.org/c.html", 9, 1));
+ r.hits().add(createHit("http://acme.org/d.html", 8, 1));
+ r.hits().add(createHit("http://acme.org/e.html", 8, 2));
+ r.hits().add(createHit("http://acme.org/f.html", 7, 2));
+ r.hits().add(createHit("http://acme.org/g.html", 7, 3));
+ r.hits().add(createHit("http://acme.org/h.html", 6, 3));
r.setTotalHitCount(8);
docsource.addResult(q, r);
@@ -220,16 +194,16 @@ public class FieldCollapsingSearcherTestCase {
// The searcher turns off collapsing further on in the chain
q.properties().set("collapse", "0");
Result r = new Result(q);
- r.hits().add(createHit("http://acme.org/a.html",10,0));
- r.hits().add(createHit("http://acme.org/b.html", 9,0));
- r.hits().add(createHit("http://acme.org/c.html", 9,0));
- r.hits().add(createHit("http://acme.org/d.html", 8,0));
- r.hits().add(createHit("http://acme.org/e.html", 8,0));
- r.hits().add(createHit("http://acme.org/f.html", 7,0));
- r.hits().add(createHit("http://acme.org/g.html", 7,0));
- r.hits().add(createHit("http://acme.org/h.html", 6,0));
- r.hits().add(createHit("http://acme.org/i.html", 5,1));
- r.hits().add(createHit("http://acme.org/j.html", 4,2));
+ r.hits().add(createHit("http://acme.org/a.html",10, 0));
+ r.hits().add(createHit("http://acme.org/b.html", 9, 0));
+ r.hits().add(createHit("http://acme.org/c.html", 9, 0));
+ r.hits().add(createHit("http://acme.org/d.html", 8, 0));
+ r.hits().add(createHit("http://acme.org/e.html", 8, 0));
+ r.hits().add(createHit("http://acme.org/f.html", 7, 0));
+ r.hits().add(createHit("http://acme.org/g.html", 7, 0));
+ r.hits().add(createHit("http://acme.org/h.html", 6, 0));
+ r.hits().add(createHit("http://acme.org/i.html", 5, 1));
+ r.hits().add(createHit("http://acme.org/j.html", 4, 2));
r.setTotalHitCount(10);
docsource.addResult(q, r);
@@ -239,15 +213,15 @@ public class FieldCollapsingSearcherTestCase {
assertEquals(2, r.getHitCount());
assertEquals(2, docsource.getQueryCount());
- assertHit("http://acme.org/a.html",10,0,r.hits().get(0));
- assertHit("http://acme.org/i.html", 5,1,r.hits().get(1));
+ assertHit("http://acme.org/a.html",10, 0, r.hits().get(0));
+ assertHit("http://acme.org/i.html", 5, 1, r.hits().get(1));
// Next results
docsource.resetQueryCount();
r = doSearch(collapse, q, 2, 2, chained);
assertEquals(1, r.getHitCount());
assertEquals(2, docsource.getQueryCount());
- assertHit("http://acme.org/j.html",4,2,r.hits().get(0));
+ assertHit("http://acme.org/j.html",4, 2, r.hits().get(0));
}
/**
@@ -265,16 +239,16 @@ public class FieldCollapsingSearcherTestCase {
// The searcher turns off collapsing further on in the chain
q.properties().set("collapse", "0");
Result r = new Result(q);
- r.hits().add(createHit("http://acme.org/a.html",10,1));
- r.hits().add(createHit("http://acme.org/b.html",10,1));
- r.hits().add(createHit("http://acme.org/c.html",10,0));
- r.hits().add(createHit("http://acme.org/d.html",10,0));
- r.hits().add(createHit("http://acme.org/e.html",10,0));
- r.hits().add(createHit("http://acme.org/f.html",10,0));
- r.hits().add(createHit("http://acme.org/g.html",10,0));
- r.hits().add(createHit("http://acme.org/h.html",10,0));
- r.hits().add(createHit("http://acme.org/i.html",10,0));
- r.hits().add(createHit("http://acme.org/j.html",10,1));
+ r.hits().add(createHit("http://acme.org/a.html", 10, 1));
+ r.hits().add(createHit("http://acme.org/b.html", 10, 1));
+ r.hits().add(createHit("http://acme.org/c.html", 10, 0));
+ r.hits().add(createHit("http://acme.org/d.html", 10, 0));
+ r.hits().add(createHit("http://acme.org/e.html", 10, 0));
+ r.hits().add(createHit("http://acme.org/f.html", 10, 0));
+ r.hits().add(createHit("http://acme.org/g.html", 10, 0));
+ r.hits().add(createHit("http://acme.org/h.html", 10, 0));
+ r.hits().add(createHit("http://acme.org/i.html", 10, 0));
+ r.hits().add(createHit("http://acme.org/j.html", 10, 1));
r.setTotalHitCount(10);
docsource.addResult(q, r);
@@ -287,17 +261,6 @@ public class FieldCollapsingSearcherTestCase {
assertHit("http://acme.org/c.html",10,0,r.hits().get(1));
}
- public static class QueryMessupSearcher extends Searcher {
- public Result search(com.yahoo.search.Query query, Execution execution) {
- AndItem a = new AndItem();
- a.addItem(query.getModel().getQueryTree().getRoot());
- a.addItem(new WordItem("b"));
- query.getModel().getQueryTree().setRoot(a);
-
- return execution.search(query);
- }
- }
-
@Test
public void testQueryTransformAndCollapsing() {
// Set up
@@ -309,9 +272,9 @@ public class FieldCollapsingSearcherTestCase {
chained.put(collapse, messUp);
chained.put(messUp, docsource);
- // Caveat: Collapse is set to false, because that's what the
- // collapser asks for
- Query q = new Query("?query=test_collapse+b&collapsefield=amid");
+ // Caveat: Collapse is set to false, because that's what the collapser asks for
+ Query q = new Query("?query=%22test%20collapse%22+b&collapsefield=amid");
+ System.out.println(q);
// The searcher turns off collapsing further on in the chain
q.properties().set("collapse", "0");
Result r = new Result(q);
@@ -327,13 +290,13 @@ public class FieldCollapsingSearcherTestCase {
docsource.addResult(q, r);
// Test basic collapsing on mid
- q = new Query("?query=test_collapse&collapsefield=amid");
+ q = new Query("?query=%22test%20collapse%22&collapsefield=amid");
r = doSearch(collapse, q, 0, 2, chained);
assertEquals(2, docsource.getQueryCount());
assertEquals(2, r.getHitCount());
- assertHit("http://acme.org/a.html",10,0,r.hits().get(0));
- assertHit("http://acme.org/h.html", 6,1,r.hits().get(1));
+ assertHit("http://acme.org/a.html",10, 0, r.hits().get(0));
+ assertHit("http://acme.org/h.html", 6, 1, r.hits().get(1));
}
@Test
@@ -367,10 +330,10 @@ public class FieldCollapsingSearcherTestCase {
assertEquals(4, r.getHitCount());
assertEquals(1, docsource.getQueryCount());
assertTrue(r.isFilled("placeholder"));
- assertHit("http://acme.org/a.html",10,0,r.hits().get(0));
- assertHit("http://acme.org/c.html", 9,1,r.hits().get(1));
- assertHit("http://acme.org/e.html", 8,2,r.hits().get(2));
- assertHit("http://acme.org/g.html", 7,3,r.hits().get(3));
+ assertHit("http://acme.org/a.html",10, 0, r.hits().get(0));
+ assertHit("http://acme.org/c.html", 9, 1, r.hits().get(1));
+ assertHit("http://acme.org/e.html", 8, 2, r.hits().get(2));
+ assertHit("http://acme.org/g.html", 7, 3, r.hits().get(3));
docsource.resetQueryCount();
// Test basic collapsing on mid
@@ -381,10 +344,10 @@ public class FieldCollapsingSearcherTestCase {
assertEquals(1, docsource.getQueryCount());
assertFalse(r.isFilled("placeholder"));
assertTrue(r.isFilled("short"));
- assertHit("http://acme.org/a.html",10,0,r.hits().get(0));
- assertHit("http://acme.org/c.html", 9,1,r.hits().get(1));
- assertHit("http://acme.org/e.html", 8,2,r.hits().get(2));
- assertHit("http://acme.org/g.html", 7,3,r.hits().get(3));
+ assertHit("http://acme.org/a.html",10, 0, r.hits().get(0));
+ assertHit("http://acme.org/c.html", 9, 1, r.hits().get(1));
+ assertHit("http://acme.org/e.html", 8, 2, r.hits().get(2));
+ assertHit("http://acme.org/g.html", 7, 3, r.hits().get(3));
}
@Test
@@ -400,14 +363,14 @@ public class FieldCollapsingSearcherTestCase {
// The searcher turns off collapsing further on in the chain
q.properties().set("collapse", "0");
Result r = new Result(q);
- r.hits().add(createHit("http://acme.org/a.html",10,0));
- r.hits().add(createHit("http://acme.org/b.html", 9,0));
- r.hits().add(createHit("http://acme.org/c.html", 9,1));
- r.hits().add(createHit("http://acme.org/d.html", 8,1));
- r.hits().add(createHit("http://acme.org/e.html", 8,2));
- r.hits().add(createHit("http://acme.org/f.html", 7,2));
- r.hits().add(createHit("http://acme.org/g.html", 7,3));
- r.hits().add(createHit("http://acme.org/h.html", 6,3));
+ r.hits().add(createHit("http://acme.org/a.html",10, 0));
+ r.hits().add(createHit("http://acme.org/b.html", 9, 0));
+ r.hits().add(createHit("http://acme.org/c.html", 9, 1));
+ r.hits().add(createHit("http://acme.org/d.html", 8, 1));
+ r.hits().add(createHit("http://acme.org/e.html", 8, 2));
+ r.hits().add(createHit("http://acme.org/f.html", 7, 2));
+ r.hits().add(createHit("http://acme.org/g.html", 7, 3));
+ r.hits().add(createHit("http://acme.org/h.html", 6, 3));
r.setTotalHitCount(8);
docsource.addResult(q, r);
@@ -416,29 +379,28 @@ public class FieldCollapsingSearcherTestCase {
Result result = new Execution(chain, Execution.Context.createContextStub()).search(query);
// Assert that the regular hits are collapsed
- assertEquals(4+1, result.getHitCount());
+ assertEquals(4 + 1, result.getHitCount());
assertEquals(1, docsource.getQueryCount());
- assertHit("http://acme.org/a.html",10,0,result.hits().get(0));
- assertHit("http://acme.org/c.html", 9,1,result.hits().get(1));
- assertHit("http://acme.org/e.html", 8,2,result.hits().get(2));
- assertHit("http://acme.org/g.html", 7,3,result.hits().get(3));
+ assertHit("http://acme.org/a.html",10, 0, result.hits().get(0));
+ assertHit("http://acme.org/c.html", 9, 1, result.hits().get(1));
+ assertHit("http://acme.org/e.html", 8, 2, result.hits().get(2));
+ assertHit("http://acme.org/g.html", 7, 3, result.hits().get(3));
// Assert that the aggregation group hierarchy is left intact
- HitGroup root= getFirstGroupIn(result.hits());
+ HitGroup root = getFirstGroupIn(result.hits());
assertNotNull(root);
- assertEquals("group:root:",root.getId().stringValue().substring(0,11)); // The id ends by a global counter currently
- assertEquals(1,root.size());
- HitGroup groupList= (GroupList)root.get("grouplist:g1");
+ assertEquals("group:root:",root.getId().stringValue().substring(0, 11)); // The id ends by a global counter currently
+ assertEquals(1, root.size());
+ HitGroup groupList = (GroupList)root.get("grouplist:g1");
assertNotNull(groupList);
- assertEquals(1,groupList.size());
- HitGroup group= (HitGroup)groupList.get("group:long:37");
+ assertEquals(1, groupList.size());
+ HitGroup group = (HitGroup)groupList.get("group:long:37");
assertNotNull(group);
}
private Group getFirstGroupIn(HitGroup hits) {
- for (Hit h : hits) {
+ for (Hit h : hits)
if (h instanceof Group) return (Group)h;
- }
return null;
}
@@ -450,9 +412,8 @@ public class FieldCollapsingSearcherTestCase {
private Chain<Searcher> chainedAsSearchChain(Searcher topOfChain, Map<Searcher, Searcher> chained) {
List<Searcher> searchers = new ArrayList<>();
- for (Searcher current = topOfChain; current != null; current = chained.get(current)) {
+ for (Searcher current = topOfChain; current != null; current = chained.get(current))
searchers.add(current);
- }
return new Chain<>(searchers);
}
@@ -470,7 +431,7 @@ public class FieldCollapsingSearcherTestCase {
@Override
public Result search(Query query, Execution execution) {
- Result r=execution.search(query);
+ Result r = execution.search(query);
r.hits().add(createAggregationGroup("g1"));
return r;
}
@@ -479,10 +440,51 @@ public class FieldCollapsingSearcherTestCase {
Group root = new Group(new RootId(0), new Relevance(1));
GroupList groupList = new GroupList(label);
root.add(groupList);
- Group value=new Group(new LongId(37l),new Relevance(2.11));
+ Group value = new Group(new LongId(37l), new Relevance(2.11));
groupList.add(value);
return root;
}
}
+ private FastHit createHit(String uri,int relevancy,int mid) {
+ FastHit hit = new FastHit(uri,relevancy);
+ hit.setField("amid", String.valueOf(mid));
+ return hit;
+ }
+
+ private void assertHit(String uri,int relevancy,int mid,Hit hit) {
+ assertEquals(uri,hit.getId().toString());
+ assertEquals(relevancy, ((int) hit.getRelevance().getScore()));
+ assertEquals(mid,Integer.parseInt((String) hit.getField("amid")));
+ }
+
+ private static class ZeroHitsControl extends com.yahoo.search.Searcher {
+
+ public int queryCount = 0;
+
+ @Override
+ public Result search(Query query, Execution execution) {
+ ++queryCount;
+ if (query.getHits() == 0) {
+ return new Result(query);
+ } else {
+ return new Result(query, ErrorMessage.createIllegalQuery("Did not request zero hits."));
+ }
+ }
+ }
+
+ public static class QueryMessupSearcher extends Searcher {
+
+ @Override
+ public Result search(com.yahoo.search.Query query, Execution execution) {
+ AndItem a = new AndItem();
+ a.addItem(query.getModel().getQueryTree().getRoot());
+ a.addItem(new WordItem("b"));
+ query.getModel().getQueryTree().setRoot(a);
+
+ return execution.search(query);
+ }
+
+ }
+
}
diff --git a/container-search/src/test/java/com/yahoo/prelude/semantics/test/SegmentSubstitutionTestCase.java b/container-search/src/test/java/com/yahoo/prelude/semantics/test/SegmentSubstitutionTestCase.java
index b8db5e4d90f..a4cf7d8c380 100644
--- a/container-search/src/test/java/com/yahoo/prelude/semantics/test/SegmentSubstitutionTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/semantics/test/SegmentSubstitutionTestCase.java
@@ -23,7 +23,7 @@ public class SegmentSubstitutionTestCase extends RuleBaseAbstractTestCase {
Query q = new Query("?query=ignored&tracelevel=0&tracelevel.rules=0");
q.getModel().getQueryTree().setRoot(a);
- assertSemantics("\"first third\"", q);
+ assertSemantics("AND first third", q);
}
@Test
@@ -32,7 +32,7 @@ public class SegmentSubstitutionTestCase extends RuleBaseAbstractTestCase {
Query q = new Query("?query=ignored&tracelevel=0&tracelevel.rules=0");
q.getModel().getQueryTree().setRoot(a);
- assertSemantics("\"bc first third fg\"", q);
+ assertSemantics("AND bc first third fg", q);
}
@Test
@@ -41,7 +41,7 @@ public class SegmentSubstitutionTestCase extends RuleBaseAbstractTestCase {
Query q = new Query("?query=ignored&tracelevel=0&tracelevel.rules=0");
q.getModel().getQueryTree().setRoot(a);
- assertSemantics("+bc -\"first third\"", q);
+ assertSemantics("+bc -(AND first third)", q);
}
@Test
@@ -50,7 +50,7 @@ public class SegmentSubstitutionTestCase extends RuleBaseAbstractTestCase {
Query q = new Query("?query=ignored&tracelevel=0&tracelevel.rules=0");
q.getModel().getQueryTree().setRoot(a);
- assertSemantics("\"9 2 7 0 bc third 2 3 8 9\"", q);
+ assertSemantics("AND 9 2 7 0 bc third 2 3 8 9", q);
}
private static Item parseQuery(String query) {
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 82a5a0c7a24..e2ac44316e7 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
@@ -73,7 +73,7 @@ public class IndexFactsTestCase {
Query q = newQuery("?query=a:b", indexFacts);
assertEquals("a:b", q.getModel().getQueryTree().getRoot().toString());
q = newQuery("?query=notarealindex:b", indexFacts);
- assertEquals("\"notarealindex b\"", q.getModel().getQueryTree().getRoot().toString());
+ assertEquals("AND notarealindex b", q.getModel().getQueryTree().getRoot().toString());
}
@Test
@@ -302,8 +302,8 @@ public class IndexFactsTestCase {
IndexFacts.Session session2 = indexFacts.newSession(query2.getModel().getSources(), query2.getModel().getRestrict());
assertTrue(session1.getIndex("url").isUriIndex());
assertTrue(session2.getIndex("url").isUriIndex());
- assertEquals("url:\"https foo bar\"", query1.getModel().getQueryTree().toString());
- assertEquals("url:\"https foo bar\"", query2.getModel().getQueryTree().toString());
+ assertEquals("AND url:https url:foo url:bar", query1.getModel().getQueryTree().toString());
+ assertEquals("AND url:https url:foo url:bar", query2.getModel().getQueryTree().toString());
}
@Test
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 0cbf3a6f92c..c6233ffa50b 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
@@ -139,12 +139,24 @@ public class ValidateNearestNeighborTestCase {
assertEquals(ErrorMessage.createIllegalQuery(message), r.hits().getError());
}
+ static String desc(String field, String qt, int th, String errmsg) {
+ StringBuilder r = new StringBuilder();
+ r.append("NEAREST_NEIGHBOR {");
+ r.append("field=").append(field);
+ r.append(",queryTensorName=").append(qt);
+ r.append(",hnsw.exploreAdditionalHits=0");
+ r.append(",approximate=true");
+ r.append(",targetNumHits=").append(th);
+ r.append("} ").append(errmsg);
+ return r.toString();
+ }
+
@Test
public void testMissingTargetNumHits() {
String q = "select * from sources * where nearestNeighbor(dvector,qvector);";
Tensor t = makeTensor(tt_dense_dvector_3);
Result r = doSearch(searcher, q, t);
- assertErrMsg("NEAREST_NEIGHBOR {field=dvector,queryTensorName=qvector,targetNumHits=0} has invalid targetNumHits", r);
+ assertErrMsg(desc("dvector", "qvector", 0, "has invalid targetNumHits"), r);
}
@Test
@@ -152,16 +164,16 @@ public class ValidateNearestNeighborTestCase {
String q = makeQuery("dvector", "foo");
Tensor t = makeTensor(tt_dense_dvector_3);
Result r = doSearch(searcher, q, t);
- assertErrMsg("NEAREST_NEIGHBOR {field=dvector,queryTensorName=foo,targetNumHits=1} query tensor not found", r);
+ assertErrMsg(desc("dvector", "foo", 1, "query tensor not found"), r);
}
@Test
public void testQueryTensorWrongType() {
String q = makeQuery("dvector", "qvector");
Result r = doSearch(searcher, q, "tensor string");
- assertErrMsg("NEAREST_NEIGHBOR {field=dvector,queryTensorName=qvector,targetNumHits=1} query tensor should be a tensor, was: class java.lang.String", r);
+ assertErrMsg(desc("dvector", "qvector", 1, "query tensor should be a tensor, was: class java.lang.String"), r);
r = doSearch(searcher, q, null);
- assertErrMsg("NEAREST_NEIGHBOR {field=dvector,queryTensorName=qvector,targetNumHits=1} query tensor should be a tensor, was: null", r);
+ assertErrMsg(desc("dvector", "qvector", 1, "query tensor should be a tensor, was: null"), r);
}
@Test
@@ -169,7 +181,7 @@ public class ValidateNearestNeighborTestCase {
String q = makeQuery("dvector", "qvector");
Tensor t = makeTensor(tt_dense_dvector_2, 2);
Result r = doSearch(searcher, q, t);
- assertErrMsg("NEAREST_NEIGHBOR {field=dvector,queryTensorName=qvector,targetNumHits=1} field type tensor(x[3]) does not match query tensor type tensor(x[2])", r);
+ assertErrMsg(desc("dvector", "qvector", 1, "field type tensor(x[3]) does not match query tensor type tensor(x[2])"), r);
}
@Test
@@ -177,7 +189,7 @@ public class ValidateNearestNeighborTestCase {
String q = makeQuery("foo", "qvector");
Tensor t = makeTensor(tt_dense_dvector_3);
Result r = doSearch(searcher, q, t);
- assertErrMsg("NEAREST_NEIGHBOR {field=foo,queryTensorName=qvector,targetNumHits=1} field is not an attribute", r);
+ assertErrMsg(desc("foo", "qvector", 1, "field is not an attribute"), r);
}
@Test
@@ -185,7 +197,7 @@ public class ValidateNearestNeighborTestCase {
String q = makeQuery("simple", "qvector");
Tensor t = makeTensor(tt_dense_dvector_3);
Result r = doSearch(searcher, q, t);
- assertErrMsg("NEAREST_NEIGHBOR {field=simple,queryTensorName=qvector,targetNumHits=1} field is not a tensor", r);
+ assertErrMsg(desc("simple", "qvector", 1, "field is not a tensor"), r);
}
@Test
@@ -193,7 +205,7 @@ public class ValidateNearestNeighborTestCase {
String q = makeQuery("sparse", "qvector");
Tensor t = makeTensor(tt_sparse_vector_x);
Result r = doSearch(searcher, q, t);
- assertErrMsg("NEAREST_NEIGHBOR {field=sparse,queryTensorName=qvector,targetNumHits=1} tensor type tensor(x{}) is not a dense vector", r);
+ assertErrMsg(desc("sparse", "qvector", 1, "tensor type tensor(x{}) is not a dense vector"), r);
}
@Test
@@ -201,7 +213,7 @@ public class ValidateNearestNeighborTestCase {
String q = makeQuery("matrix", "qvector");
Tensor t = makeMatrix(tt_dense_matrix_xy);
Result r = doSearch(searcher, q, t);
- assertErrMsg("NEAREST_NEIGHBOR {field=matrix,queryTensorName=qvector,targetNumHits=1} tensor type tensor(x[3],y[1]) is not a dense vector", r);
+ assertErrMsg(desc("matrix", "qvector", 1, "tensor type tensor(x[3],y[1]) is not a dense vector"), r);
}
private static Result doSearch(ValidateNearestNeighborSearcher searcher, String yqlQuery, Object qTensor) {
diff --git a/container-search/src/test/java/com/yahoo/search/searchers/test/InputCheckingSearcherTestCase.java b/container-search/src/test/java/com/yahoo/search/searchers/test/InputCheckingSearcherTestCase.java
index dbeced57c52..aa507d38be5 100644
--- a/container-search/src/test/java/com/yahoo/search/searchers/test/InputCheckingSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchers/test/InputCheckingSearcherTestCase.java
@@ -5,6 +5,7 @@ import static org.junit.Assert.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import org.junit.After;
import org.junit.Before;
@@ -23,50 +24,50 @@ import com.yahoo.text.Utf8;
/**
* Functional test for InputCheckingSearcher.
*
- * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ * @author Steinar Knutsen
*/
public class InputCheckingSearcherTestCase {
Execution execution;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
execution = new Execution(new Chain<Searcher>(new InputCheckingSearcher(MetricReceiver.nullImplementation)),
- Execution.Context.createContextStub(new IndexFacts()));
+ Execution.Context.createContextStub(new IndexFacts()));
}
@After
- public void tearDown() throws Exception {
+ public void tearDown() {
execution = null;
}
@Test
- public final void testCommonCase() {
+ public void testCommonCase() {
Result r = execution.search(new Query("/search/?query=three+blind+mice"));
assertNull(r.hits().getErrorHit());
}
@Test
- public final void candidateButAsciiOnly() {
+ public void candidateButAsciiOnly() {
Result r = execution.search(new Query("/search/?query=a+a+a+a+a+a"));
assertNull(r.hits().getErrorHit());
}
@Test
- public final void candidateButValid() throws UnsupportedEncodingException {
+ public void candidateButValid() throws UnsupportedEncodingException {
Result r = execution.search(new Query("/search/?query=" + URLEncoder.encode("å å å å å å", "UTF-8")));
assertNull(r.hits().getErrorHit());
}
@Test
- public final void candidateButValidAndOutsideFirst256() throws UnsupportedEncodingException {
+ public void candidateButValidAndOutsideFirst256() throws UnsupportedEncodingException {
Result r = execution.search(new Query("/search/?query=" + URLEncoder.encode("œ œ œ œ œ œ", "UTF-8")));
assertNull(r.hits().getErrorHit());
}
@Test
- public final void testDoubleEncoded() throws UnsupportedEncodingException {
+ public void testDoubleEncoded() throws UnsupportedEncodingException {
String rawQuery = "å å å å å å";
byte[] encodedOnce = Utf8.toBytes(rawQuery);
char[] secondEncodingBuffer = new char[encodedOnce.length];
@@ -74,33 +75,42 @@ public class InputCheckingSearcherTestCase {
secondEncodingBuffer[i] = (char) (encodedOnce[i] & 0xFF);
}
String query = new String(secondEncodingBuffer);
- Result r = execution.search(new Query("/search/?query=" + URLEncoder.encode(query, "UTF-8")));
+ Result r = execution.search(new Query("/search/?query=" + URLEncoder.encode(query, StandardCharsets.UTF_8)));
assertEquals(1, r.hits().getErrorHit().errors().size());
}
@Test
- public final void testRepeatedConsecutiveTermsInPhrase() {
- Result r = execution.search(new Query("/search/?query=a.b.0.0.0.0.0.c"));
+ public void testRepeatedConsecutiveTermsInPhrase() {
+ Result r = execution.search(new Query("/search/?query=%22a.b.0.0.0.0.0.c%22"));
assertNull(r.hits().getErrorHit());
- r = execution.search(new Query("/search/?query=a.b.0.0.0.0.0.0.c"));
+ r = execution.search(new Query("/search/?query=%22a.b.0.0.0.0.0.0.c%22"));
assertNotNull(r.hits().getErrorHit());
+ assertEquals("More than 5 ocurrences of term '0' in a row detected in phrase : \"a b 0 0 0 0 0 0 c\"",
+ r.hits().getErrorHit().errorIterator().next().getDetailedMessage());
r = execution.search(new Query("/search/?query=a.b.0.0.0.1.0.0.0.c"));
assertNull(r.hits().getErrorHit());
}
+
@Test
- public final void testThatMaxRepeatedConsecutiveTermsInPhraseIs5() {
- Result r = execution.search(new Query("/search/?query=a.b.0.0.0.0.0.c"));
+ public void testThatMaxRepeatedConsecutiveTermsInPhraseIs5() {
+ Result r = execution.search(new Query("/search/?query=%22a.b.0.0.0.0.0.c%22"));
assertNull(r.hits().getErrorHit());
- r = execution.search(new Query("/search/?query=a.b.0.0.0.0.0.0.c"));
+ r = execution.search(new Query("/search/?query=%22a.b.0.0.0.0.0.0.c%22"));
assertNotNull(r.hits().getErrorHit());
- r = execution.search(new Query("/search/?query=a.b.0.0.0.1.0.0.0.c"));
+ assertEquals("More than 5 ocurrences of term '0' in a row detected in phrase : \"a b 0 0 0 0 0 0 c\"",
+ r.hits().getErrorHit().errorIterator().next().getDetailedMessage());
+ r = execution.search(new Query("/search/?query=%22a.b.0.0.0.1.0.0.0.c%22"));
assertNull(r.hits().getErrorHit());
}
+
@Test
- public final void testThatMaxRepeatedTermsInPhraseIs10() {
- Result r = execution.search(new Query("/search/?query=0.a.1.a.2.a.3.a.4.a.5.a.6.a.7.a.9.a"));
+ public void testThatMaxRepeatedTermsInPhraseIs10() {
+ Result r = execution.search(new Query("/search/?query=%220.a.1.a.2.a.3.a.4.a.5.a.6.a.7.a.9.a%22"));
assertNull(r.hits().getErrorHit());
- r = execution.search(new Query("/search/?query=0.a.1.a.2.a.3.a.4.a.5.a.6.a.7.a.8.a.9.a.10.a"));
+ r = execution.search(new Query("/search/?query=%220.a.1.a.2.a.3.a.4.a.5.a.6.a.7.a.8.a.9.a.10.a%22"));
assertNotNull(r.hits().getErrorHit());
+ assertEquals("Phrase contains more than 10 occurrences of term 'a' in phrase : \"0 a 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 a 9 a 10 a\"",
+ r.hits().getErrorHit().errorIterator().next().getDetailedMessage());
}
+
}
diff --git a/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java b/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java
index 34c3da395b7..c831ee29631 100644
--- a/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java
@@ -16,6 +16,7 @@ import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.IndexModel;
import com.yahoo.prelude.SearchDefinition;
import com.yahoo.prelude.query.AndItem;
+import com.yahoo.prelude.query.AndSegmentItem;
import com.yahoo.prelude.query.CompositeItem;
import com.yahoo.prelude.query.Highlight;
import com.yahoo.prelude.query.IndexedItem;
@@ -941,12 +942,12 @@ public class QueryTestCase {
@Test
public void testImplicitPhraseIsDefault() {
Query query = new Query(httpEncode("?query=it's fine"));
- assertEquals("AND 'it s' fine", query.getModel().getQueryTree().toString());
+ assertEquals("AND (SAND it s) fine", query.getModel().getQueryTree().toString());
}
@Test
public void testImplicitPhrase() {
- Query query = new Query(httpEncode("?query=myfield:it's myfield:a-b myfield:c"));
+ Query query = new Query(httpEncode("?query=myfield:it's myfield:a.b myfield:c"));
SearchDefinition test = new SearchDefinition("test");
Index myField = new Index("myfield");
@@ -961,7 +962,7 @@ public class QueryTestCase {
@Test
public void testImplicitAnd() {
- Query query = new Query(httpEncode("?query=myfield:it's myfield:a-b myfield:c"));
+ Query query = new Query(httpEncode("?query=myfield:it's myfield:a.b myfield:c"));
SearchDefinition test = new SearchDefinition("test");
Index myField = new Index("myfield");
@@ -972,6 +973,56 @@ public class QueryTestCase {
query.getModel().setExecution(new Execution(Execution.Context.createContextStub(new IndexFacts(indexModel))));
assertEquals("AND (SAND myfield:it myfield:s) myfield:a myfield:b myfield:c", query.getModel().getQueryTree().toString());
+ // 'it' and 's' should have connectivity 1
+ AndItem root = (AndItem)query.getModel().getQueryTree().getRoot();
+ AndSegmentItem sand = (AndSegmentItem)root.getItem(0);
+ WordItem it = (WordItem)sand.getItem(0);
+ assertEquals("it", it.getWord());
+ WordItem s = (WordItem)sand.getItem(1);
+ assertEquals("s", s.getWord());
+ assertEquals(s, it.getConnectedItem());
+ assertEquals(1.0, it.getConnectivity(), 0.00000001);
+ }
+
+ @Test
+ public void testImplicitAndConnectivity() {
+ SearchDefinition test = new SearchDefinition("test");
+ Index myField = new Index("myfield");
+ myField.addCommand("phrase-segmenting false");
+ test.addIndex(myField);
+ IndexModel indexModel = new IndexModel(test);
+
+ {
+ Query query = new Query(httpEncode("?query=myfield:b.c.d"));
+ query.getModel().setExecution(new Execution(Execution.Context.createContextStub(new IndexFacts(indexModel))));
+ assertEquals("AND myfield:b myfield:c myfield:d", query.getModel().getQueryTree().toString());
+ AndItem root = (AndItem) query.getModel().getQueryTree().getRoot();
+ WordItem b = (WordItem) root.getItem(0);
+ WordItem c = (WordItem) root.getItem(1);
+ WordItem d = (WordItem) root.getItem(2);
+ assertEquals(c, b.getConnectedItem());
+ assertEquals(1.0, b.getConnectivity(), 0.00000001);
+ assertEquals(d, c.getConnectedItem());
+ assertEquals(1.0, c.getConnectivity(), 0.00000001);
+ }
+
+ {
+ Query query = new Query(httpEncode("?query=myfield:a myfield:b.c.d myfield:e"));
+ query.getModel().setExecution(new Execution(Execution.Context.createContextStub(new IndexFacts(indexModel))));
+ assertEquals("AND myfield:a myfield:b myfield:c myfield:d myfield:e", query.getModel().getQueryTree().toString());
+ AndItem root = (AndItem) query.getModel().getQueryTree().getRoot();
+ WordItem a = (WordItem) root.getItem(0);
+ WordItem b = (WordItem) root.getItem(1);
+ WordItem c = (WordItem) root.getItem(2);
+ WordItem d = (WordItem) root.getItem(3);
+ WordItem e = (WordItem) root.getItem(4);
+ assertNull(a.getConnectedItem());
+ assertEquals(c, b.getConnectedItem());
+ assertEquals(1.0, b.getConnectivity(), 0.00000001);
+ assertEquals(d, c.getConnectedItem());
+ assertEquals(1.0, c.getConnectivity(), 0.00000001);
+ assertNull(d.getConnectedItem());
+ }
}
@Test
diff --git a/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java
index 1106d8c3999..d770b08d31a 100644
--- a/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java
@@ -128,6 +128,9 @@ public class VespaSerializerTestCase {
public void testNearestNeighbor() {
parseAndConfirm("[{\"label\": \"foo\", \"targetNumHits\": 1000}]nearestNeighbor(semantic_embedding, my_property)");
parseAndConfirm("[{\"targetNumHits\": 42}]nearestNeighbor(semantic_embedding, my_property)");
+ parseAndConfirm("[{\"targetNumHits\": 1, \"hnsw.exploreAdditionalHits\": 76}]nearestNeighbor(semantic_embedding, my_property)");
+ parseAndConfirm("[{\"targetNumHits\": 2, \"approximate\": false}]nearestNeighbor(semantic_embedding, my_property)");
+ parseAndConfirm("[{\"targetNumHits\": 3, \"hnsw.exploreAdditionalHits\": 67, \"approximate\": false}]nearestNeighbor(semantic_embedding, my_property)");
}
@Test
diff --git a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java
index 5eb1f3e3de1..e43dbd4e266 100644
--- a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java
@@ -550,9 +550,11 @@ public class YqlParserTestCase {
@Test
public void testNearestNeighbor() {
assertParse("select foo from bar where nearestNeighbor(semantic_embedding, my_vector);",
- "NEAREST_NEIGHBOR {field=semantic_embedding,queryTensorName=my_vector,targetNumHits=0}");
+ "NEAREST_NEIGHBOR {field=semantic_embedding,queryTensorName=my_vector,hnsw.exploreAdditionalHits=0,approximate=true,targetNumHits=0}");
assertParse("select foo from bar where [{\"targetNumHits\": 37}]nearestNeighbor(semantic_embedding, my_vector);",
- "NEAREST_NEIGHBOR {field=semantic_embedding,queryTensorName=my_vector,targetNumHits=37}");
+ "NEAREST_NEIGHBOR {field=semantic_embedding,queryTensorName=my_vector,hnsw.exploreAdditionalHits=0,approximate=true,targetNumHits=37}");
+ assertParse("select foo from bar where [{\"approximate\": false, \"hnsw.exploreAdditionalHits\": 8, \"targetNumHits\": 3}]nearestNeighbor(semantic_embedding, my_vector);",
+ "NEAREST_NEIGHBOR {field=semantic_embedding,queryTensorName=my_vector,hnsw.exploreAdditionalHits=8,approximate=false,targetNumHits=3}");
}
@Test
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/DeploymentData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/DeploymentData.java
new file mode 100644
index 00000000000..43d702d108f
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/DeploymentData.java
@@ -0,0 +1,63 @@
+package com.yahoo.vespa.hosted.controller.api.application.v4.model;
+
+import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint;
+
+import java.util.Optional;
+import java.util.Set;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Data pertaining to a deployment to be done on a config server.
+ *
+ * @author jonmv
+ */
+public class DeploymentData {
+
+ private final ApplicationId instance;
+ private final ZoneId zone;
+ private final byte[] applicationPackage;
+ private final Version platform;
+ private final Set<ContainerEndpoint> containerEndpoints;
+ private final Optional<EndpointCertificateMetadata> endpointCertificateMetadata;
+
+ public DeploymentData(ApplicationId instance, ZoneId zone, byte[] applicationPackage, Version platform,
+ Set<ContainerEndpoint> containerEndpoints,
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata) {
+ this.instance = requireNonNull(instance);
+ this.zone = requireNonNull(zone);
+ this.applicationPackage = requireNonNull(applicationPackage);
+ this.platform = requireNonNull(platform);
+ this.containerEndpoints = requireNonNull(containerEndpoints);
+ this.endpointCertificateMetadata = requireNonNull(endpointCertificateMetadata);
+ }
+
+ public ApplicationId instance() {
+ return instance;
+ }
+
+ public ZoneId zone() {
+ return zone;
+ }
+
+ public byte[] applicationPackage() {
+ return applicationPackage;
+ }
+
+ public Version platform() {
+ return platform;
+ }
+
+ public Set<ContainerEndpoint> containerEndpoints() {
+ return containerEndpoints;
+ }
+
+ public Optional<EndpointCertificateMetadata> endpointCertificateMetadata() {
+ return endpointCertificateMetadata;
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
index 9e5b01a91d7..978a00fbccf 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
@@ -31,7 +31,6 @@ import java.time.Clock;
*
* @author mpolden
*/
-// TODO(mpolden): Access all services through this
public interface ServiceRegistry {
ConfigServer configServer();
@@ -42,6 +41,7 @@ public interface ServiceRegistry {
GlobalRoutingService globalRoutingService();
+ // TODO(mpolden): Remove
RoutingGenerator routingGenerator();
Mailer mailer();
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java
index 171c5caa756..53366c9b922 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/certificates/EndpointCertificateMetadata.java
@@ -16,6 +16,7 @@ public class EndpointCertificateMetadata {
private final String keyName;
private final String certName;
private final int version;
+ // TODO: make these fields required once all certs have them stored
private final Optional<String> request_id;
private final Optional<List<String>> requestedDnsSans;
private final Optional<String> issuer;
@@ -24,10 +25,6 @@ public class EndpointCertificateMetadata {
this(keyName, certName, version, Optional.empty(), Optional.empty(), Optional.empty());
}
- public EndpointCertificateMetadata(String keyName, String certName, int version, String request_id, List<String> requestedDnsSans) {
- this(keyName, certName, version, Optional.of(request_id), Optional.of(requestedDnsSans), Optional.empty());
- }
-
public EndpointCertificateMetadata(String keyName, String certName, int version, Optional<String> request_id, Optional<List<String>> requestedDnsSans, Optional<String> issuer) {
this.keyName = keyName;
this.certName = certName;
@@ -61,6 +58,17 @@ public class EndpointCertificateMetadata {
return issuer;
}
+ public EndpointCertificateMetadata withVersion(int version) {
+ return new EndpointCertificateMetadata(
+ this.keyName,
+ this.certName,
+ version,
+ this.request_id,
+ this.requestedDnsSans,
+ this.issuer
+ );
+ }
+
@Override
public String toString() {
return "EndpointCertificateMetadata{" +
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
index 722f9c4e33b..7ea7a350c0c 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java
@@ -7,6 +7,7 @@ import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.flags.json.FlagData;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeploymentData;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname;
@@ -32,9 +33,7 @@ public interface ConfigServer {
PrepareResponse prepareResponse();
}
- PreparedApplication deploy(DeploymentId deployment, DeployOptions deployOptions,
- Set<ContainerEndpoint> containerEndpoints, Optional<EndpointCertificateMetadata> endpointCertificateMetadata,
- byte[] content);
+ PreparedApplication deploy(DeploymentData deployment);
void restart(DeploymentId deployment, Optional<Hostname> hostname);
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingEndpoint.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingEndpoint.java
index 59ad23aaa23..c0ccd0722f3 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingEndpoint.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingEndpoint.java
@@ -6,6 +6,7 @@ import java.util.Objects;
/**
* @author smorgrav
*/
+// TODO(mpolden): Remove together with RoutingGenerator and its implementations
public class RoutingEndpoint {
private final boolean isGlobal;
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGenerator.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGenerator.java
index f5c82018ac6..8150a99979e 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGenerator.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGenerator.java
@@ -12,6 +12,7 @@ import java.util.Map;
* @author bratseth
* @author smorgrav
*/
+// TODO(mpolden): Remove
public interface RoutingGenerator {
/**
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGeneratorMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGeneratorMock.java
index cd0e6552596..e768a090188 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGeneratorMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/routing/RoutingGeneratorMock.java
@@ -2,9 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.routing;
import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import java.net.URI;
import java.util.List;
@@ -18,31 +16,13 @@ import java.util.stream.Collectors;
* @author bratseth
* @author jonmv
*/
+// TODO(mpolden): Remove
public class RoutingGeneratorMock implements RoutingGenerator {
- public static final List<RoutingEndpoint> TEST_ENDPOINTS =
- List.of(new RoutingEndpoint("http://old-endpoint.vespa.yahooapis.com:4080", "host1", false, "upstream3"),
- new RoutingEndpoint("http://qrs-endpoint.vespa.yahooapis.com:4080", "host1", false, "upstream1"),
- new RoutingEndpoint("http://feeding-endpoint.vespa.yahooapis.com:4080", "host2", false, "upstream2"),
- new RoutingEndpoint("http://global-endpoint.vespa.yahooapis.com:4080", "host1", true, "upstream1"),
- new RoutingEndpoint("http://alias-endpoint.vespa.yahooapis.com:4080", "host1", true, "upstream1"));
-
private final Map<DeploymentId, List<RoutingEndpoint>> routingTable = new ConcurrentHashMap<>();
- private final List<RoutingEndpoint> defaultEndpoints;
- private final ZoneRegistry zoneRegistry;
-
- public RoutingGeneratorMock(List<RoutingEndpoint> endpoints, ZoneRegistry zoneRegistry) {
- this.defaultEndpoints = List.copyOf(endpoints);
- this.zoneRegistry = zoneRegistry;
- }
@Override
public List<RoutingEndpoint> endpoints(DeploymentId deployment) {
- if (!zoneRegistry.zones().routingMethod(RoutingMethod.shared).ids().contains(deployment.zoneId())) {
- throw new IllegalArgumentException(deployment.zoneId() + " does not support routing method " +
- RoutingMethod.shared);
- }
- if (routingTable.isEmpty()) return defaultEndpoints;
return routingTable.getOrDefault(deployment, List.of());
}
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 854f72b27fb..bc31a0a02fa 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
@@ -20,6 +20,7 @@ import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.hosted.controller.api.ActivateResult;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeploymentData;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname;
@@ -36,6 +37,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareRes
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationStore;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepository;
+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.api.integration.deployment.TesterId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
@@ -285,9 +287,65 @@ public class ApplicationController {
return deploy(applicationId, zone, applicationPackageFromDeployer, Optional.empty(), options);
}
- /** Deploys an application. If the application does not exist it is created. */
- // TODO: Get rid of the options arg
- // TODO jonmv: Split this, and choose between deployDirectly and deploy in handler, excluding internally built from the latter.
+ /** Deploys an application package for an existing application instance. */
+ public ActivateResult deploy2(JobId job, boolean deploySourceVersions) { // TODO jonmv: make it number one!
+ if (job.application().instance().isTester())
+ throw new IllegalArgumentException("'" + job.application() + "' is a tester application!");
+
+ TenantAndApplicationId applicationId = TenantAndApplicationId.from(job.application());
+ ZoneId zone = job.type().zone(controller.system());
+
+ try (Lock deploymentLock = lockForDeployment(job.application(), zone)) {
+ Set<ContainerEndpoint> endpoints;
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata;
+
+ Run run = controller.jobController().last(job)
+ .orElseThrow(() -> new IllegalStateException("No known run of '" + job + "'"));
+
+ if (run.hasEnded())
+ throw new IllegalStateException("No deployment expected for " + job + " now, as no job is running");
+
+ Version platform = run.versions().sourcePlatform().filter(__ -> deploySourceVersions).orElse(run.versions().targetPlatform());
+ ApplicationVersion revision = run.versions().sourceApplication().filter(__ -> deploySourceVersions).orElse(run.versions().targetApplication());
+ ApplicationPackage applicationPackage = getApplicationPackage(job.application(), zone, revision);
+
+ try (Lock lock = lock(applicationId)) {
+ LockedApplication application = new LockedApplication(requireApplication(applicationId), lock);
+ Instance instance = application.get().require(job.application().instance());
+
+ Deployment deployment = instance.deployments().get(zone);
+ if ( zone.environment().isProduction() && deployment != null
+ && ( platform.compareTo(deployment.version()) < 0 && ! instance.change().isPinned()
+ || revision.compareTo(deployment.applicationVersion()) < 0 && ! (revision.isUnknown() && controller.system().isCd())))
+ throw new IllegalArgumentException(String.format("Rejecting deployment of application %s to %s, as the requested versions (platform: %s, application: %s)" +
+ " are older than the currently deployed (platform: %s, application: %s).",
+ job.application(), zone, platform, revision, deployment.version(), deployment.applicationVersion()));
+
+ if ( ! applicationPackage.trustedCertificates().isEmpty()
+ && run.testerCertificate().isPresent())
+ applicationPackage = applicationPackage.withTrustedCertificate(run.testerCertificate().get());
+
+ endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(instance, zone);
+
+ endpoints = controller.routing().registerEndpointsInDns(applicationPackage.deploymentSpec(), instance, zone);
+ } // Release application lock while doing the deployment, which is a lengthy task.
+
+ // Carry out deployment without holding the application lock.
+ ActivateResult result = deploy(job.application(), applicationPackage, zone, platform, endpoints, endpointCertificateMetadata);
+
+ lockApplicationOrThrow(applicationId, application ->
+ store(application.with(job.application().instance(),
+ instance -> instance.withNewDeployment(zone, revision, platform,
+ clock.instant(), warningsFrom(result)))));
+ return result;
+ }
+ }
+
+ private ApplicationPackage getApplicationPackage(ApplicationId application, ZoneId zone, ApplicationVersion revision) {
+ return new ApplicationPackage(revision.isUnknown() ? applicationStore.getDev(application, zone)
+ : applicationStore.get(application.tenant(), application.application(), revision));
+ }
+
public ActivateResult deploy(ApplicationId instanceId, ZoneId zone,
Optional<ApplicationPackage> applicationPackageFromDeployer,
Optional<ApplicationVersion> applicationVersionFromDeployer,
@@ -342,13 +400,12 @@ public class ApplicationController {
endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(application.get().require(instance), zone);
- endpoints = controller.routingController().registerEndpointsInDns(applicationPackage.deploymentSpec(), application.get().require(instanceId.instance()), zone);
+ endpoints = controller.routing().registerEndpointsInDns(applicationPackage.deploymentSpec(), application.get().require(instanceId.instance()), zone);
} // Release application lock while doing the deployment, which is a lengthy task.
// Carry out deployment without holding the application lock.
- options = withVersion(platformVersion, options);
- ActivateResult result = deploy(instanceId, applicationPackage, zone, options, endpoints,
- endpointCertificateMetadata);
+ ActivateResult result = deploy(instanceId, applicationPackage, zone, platformVersion,
+ endpoints, endpointCertificateMetadata);
lockApplicationOrThrow(applicationId, application ->
store(application.with(instanceId.instance(),
@@ -398,7 +455,7 @@ public class ApplicationController {
for (InstanceName instance : declaredInstances)
if (applicationPackage.deploymentSpec().requireInstance(instance).concerns(Environment.prod))
- application = controller.routingController().assignRotations(application, instance);
+ application = controller.routing().assignRotations(application, instance);
store(application);
return application;
@@ -420,31 +477,30 @@ public class ApplicationController {
ApplicationPackage applicationPackage = new ApplicationPackage(
artifactRepository.getSystemApplicationPackage(application.id(), zone, version)
);
- DeployOptions options = withVersion(version, DeployOptions.none());
- return deploy(application.id(), applicationPackage, zone, options, Set.of(), /* No application cert */ Optional.empty());
+ return deploy(application.id(), applicationPackage, zone, version, Set.of(), /* No application cert */ Optional.empty());
} else {
throw new RuntimeException("This system application does not have an application package: " + application.id().toShortString());
}
}
/** Deploys the given tester application to the given zone. */
- public ActivateResult deployTester(TesterId tester, ApplicationPackage applicationPackage, ZoneId zone, DeployOptions options) {
- return deploy(tester.id(), applicationPackage, zone, options, Set.of(), /* No application cert for tester*/ Optional.empty());
+ public ActivateResult deployTester(TesterId tester, ApplicationPackage applicationPackage, ZoneId zone, Version platform) {
+ return deploy(tester.id(), applicationPackage, zone, platform, Set.of(), /* No application cert for tester*/ Optional.empty());
}
private ActivateResult deploy(ApplicationId application, ApplicationPackage applicationPackage,
- ZoneId zone, DeployOptions deployOptions, Set<ContainerEndpoint> endpoints,
+ ZoneId zone, Version platform, Set<ContainerEndpoint> endpoints,
Optional<EndpointCertificateMetadata> endpointCertificateMetadata) {
- DeploymentId deploymentId = new DeploymentId(application, zone);
try {
ConfigServer.PreparedApplication preparedApplication =
- configServer.deploy(deploymentId, deployOptions, endpoints, endpointCertificateMetadata, applicationPackage.zippedContent());
+ configServer.deploy(new DeploymentData(application, zone, applicationPackage.zippedContent(), platform,
+ endpoints, endpointCertificateMetadata));
return new ActivateResult(new RevisionId(applicationPackage.hash()), preparedApplication.prepareResponse(),
applicationPackage.zippedContent().length);
} finally {
// Even if prepare fails, a load balancer may have been provisioned. Always refresh routing policies so that
// any DNS updates can be propagated as early as possible.
- controller.routingController().policies().refresh(application, applicationPackage.deploymentSpec(), zone);
+ controller.routing().policies().refresh(application, applicationPackage.deploymentSpec(), zone);
}
}
@@ -516,7 +572,7 @@ public class ApplicationController {
throw new IllegalArgumentException("Could not delete '" + application + "': It has active deployments: " + deployments);
for (Instance instance : application.get().instances().values()) {
- controller.routingController().removeEndpointsInDns(instance);
+ controller.routing().removeEndpointsInDns(instance);
application = application.without(instance.name());
}
@@ -551,7 +607,7 @@ public class ApplicationController {
&& application.get().deploymentSpec().instanceNames().contains(instanceId.instance()))
throw new IllegalArgumentException("Can not delete '" + instanceId + "', which is specified in 'deployment.xml'; remove it there first");
- controller.routingController().removeEndpointsInDns(application.get().require(instanceId.instance()));
+ controller.routing().removeEndpointsInDns(application.get().require(instanceId.instance()));
curator.writeApplication(application.without(instanceId.instance()).get());
controller.jobController().collectGarbage();
log.info("Deleted " + instanceId);
@@ -633,7 +689,7 @@ public class ApplicationController {
} catch (NotFoundException ignored) {
// ok; already gone
} finally {
- controller.routingController().policies().refresh(application.get().id().instance(instanceName), application.get().deploymentSpec(), zone);
+ controller.routing().policies().refresh(application.get().id().instance(instanceName), application.get().deploymentSpec(), zone);
}
return application.with(instanceName, instance -> instance.withoutDeploymentIn(zone));
}
@@ -776,7 +832,7 @@ public class ApplicationController {
}
/** Returns the latest known version within the given major. */
- private Optional<Version> lastCompatibleVersion(int targetMajorVersion) {
+ public Optional<Version> lastCompatibleVersion(int targetMajorVersion) {
return controller.versionStatus().versions().stream()
.map(VespaVersion::versionNumber)
.filter(version -> version.getMajor() == targetMajorVersion)
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
index 3d492bc00d4..5189721df5d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
@@ -124,7 +124,7 @@ public class Controller extends AbstractComponent implements ApplicationIdSource
public JobController jobController() { return jobController; }
/** Returns the instance controlling routing */
- public RoutingController routingController() {
+ public RoutingController routing() {
return routingController;
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java
index a510275b98e..984f6855cb3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java
@@ -7,7 +7,6 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
@@ -16,8 +15,6 @@ import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.ClusterInfo;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
-import com.yahoo.vespa.hosted.controller.application.EndpointId;
-import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.rotation.RotationStatus;
import java.time.Instant;
@@ -166,20 +163,6 @@ public class Instance {
return rotations;
}
- /** Returns the default global endpoints for this in given system - for a given endpoint ID */
- public EndpointList endpointsIn(SystemName system, EndpointId endpointId) {
- if (rotations.isEmpty()) return EndpointList.EMPTY;
- return EndpointList.create(id, endpointId, system);
- }
-
- /** Returns the default global endpoints for this in given system */
- public EndpointList endpointsIn(SystemName system) {
- if (rotations.isEmpty()) return EndpointList.EMPTY;
- final var endpointStream = rotations.stream()
- .flatMap(rotation -> EndpointList.create(id, rotation.endpointId(), system).asList().stream());
- return EndpointList.of(endpointStream);
- }
-
/** Returns the status of the global rotation(s) assigned to this */
public RotationStatus rotationStatus() {
return rotationStatus;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
index cf272d94dcd..4637de9a32a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller;
import com.yahoo.config.application.api.DeploymentInstanceSpec;
import com.yahoo.config.application.api.DeploymentSpec;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.zone.RoutingMethod;
@@ -13,12 +14,13 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerE
import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
-import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingEndpoint;
-import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
+import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
+import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.dns.NameServiceQueue.Priority;
import com.yahoo.vespa.hosted.controller.rotation.RotationLock;
import com.yahoo.vespa.hosted.controller.rotation.RotationRepository;
+import com.yahoo.vespa.hosted.controller.routing.RoutingId;
import com.yahoo.vespa.hosted.controller.routing.RoutingPolicies;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
@@ -29,13 +31,12 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
@@ -48,19 +49,15 @@ import java.util.stream.Collectors;
*/
public class RoutingController {
- private static final Logger log = Logger.getLogger(RoutingController.class.getName());
-
private final Controller controller;
private final RoutingPolicies routingPolicies;
private final RotationRepository rotationRepository;
- private final RoutingGenerator routingGenerator;
public RoutingController(Controller controller, RotationsConfig rotationsConfig) {
this.controller = Objects.requireNonNull(controller, "controller must be non-null");
this.routingPolicies = new RoutingPolicies(controller);
this.rotationRepository = new RotationRepository(rotationsConfig, controller.applications(),
controller.curator());
- this.routingGenerator = controller.serviceRegistry().routingGenerator();
}
public RoutingPolicies policies() {
@@ -71,53 +68,65 @@ public class RoutingController {
return rotationRepository;
}
- /** Returns all legacy endpoint URLs for given deployment, including global, in the shared routing layer */
- public List<URI> legacyEndpointsOf(DeploymentId deployment) {
- return routingEndpointsOf(deployment).stream()
- .map(RoutingEndpoint::endpoint)
- .map(URI::create)
- .collect(Collectors.toUnmodifiableList());
+ /** Returns zone-scoped endpoints for given deployment */
+ public EndpointList endpointsOf(DeploymentId deployment) {
+ var endpoints = new LinkedHashSet<Endpoint>();
+ // TODO(mpolden): Remove this once all applications have deployed once and config server passes correct cluster
+ // id for combined cluster type
+ controller.serviceRegistry().routingGenerator().clusterEndpoints(deployment)
+ .forEach((cluster, url) -> endpoints.add(Endpoint.of(deployment.applicationId())
+ .target(cluster, deployment.zoneId())
+ .routingMethod(RoutingMethod.shared)
+ .on(Port.fromRoutingMethod(RoutingMethod.shared))
+ .in(controller.system())));
+ boolean hasSharedEndpoint = !endpoints.isEmpty();
+ for (var policy : routingPolicies.get(deployment).values()) {
+ if (!policy.status().isActive()) continue;
+ for (var routingMethod : controller.zoneRegistry().routingMethods(policy.id().zone())) {
+ if (hasSharedEndpoint && routingMethod == RoutingMethod.shared) continue;
+ endpoints.add(policy.endpointIn(controller.system(), routingMethod));
+ }
+ }
+ return EndpointList.copyOf(endpoints);
}
- /** Returns legacy zone endpoints for given deployment, in the shared routing layer */
- public Map<ClusterSpec.Id, URI> legacyZoneEndpointsOf(DeploymentId deployment) {
- if (!supportsRoutingMethod(RoutingMethod.shared, deployment.zoneId())) {
- return Map.of();
- }
- try {
- return routingGenerator.clusterEndpoints(deployment);
- } catch (RuntimeException e) {
- log.log(Level.WARNING, "Failed to get endpoint information for " + deployment, e);
- return Map.of();
- }
+ /** Returns global-scoped endpoints for given instance */
+ public EndpointList endpointsOf(ApplicationId instance) {
+ return endpointsOf(controller.applications().requireInstance(instance));
}
- /**
- * Returns all non-global endpoint URLs for given deployment, grouped by their cluster ID. If deployment supports
- * {@link RoutingMethod#exclusive} endpoints defined through routing polices are returned.
- */
- public Map<ClusterSpec.Id, URI> zoneEndpointsOf(DeploymentId deployment) {
- if ( ! controller.applications().getInstance(deployment.applicationId())
- .map(application -> application.deployments().containsKey(deployment.zoneId()))
- .orElse(deployment.applicationId().instance().isTester()))
- throw new NotExistsException("Deployment", deployment.toString());
-
- // In exclusively routed zones we create endpoint URLs from routing policies
- if (supportsRoutingMethod(RoutingMethod.exclusive, deployment.zoneId())) {
- return routingPolicies.get(deployment).values().stream()
- .filter(policy -> policy.endpointIn(controller.system()).scope() == Endpoint.Scope.zone)
- .collect(Collectors.toUnmodifiableMap(policy -> policy.id().cluster(),
- policy -> policy.endpointIn(controller.system())
- .url()));
+ /** Returns global-scoped endpoints for given instance */
+ // TODO(mpolden): Add a endpointsOf(Instance, DeploymentId) variant of this that only returns global endpoint of
+ // which deployment is a member
+ public EndpointList endpointsOf(Instance instance) {
+ var endpoints = new LinkedHashSet<Endpoint>();
+ // Add global endpoints provided by rotations
+ for (var rotation : instance.rotations()) {
+ EndpointList.global(RoutingId.of(instance.id(), rotation.endpointId()),
+ controller.system(), systemRoutingMethods())
+ .requiresRotation()
+ .forEach(endpoints::add);
+ }
+ // Add global endpoints provided by routing policices
+ for (var policy : routingPolicies.get(instance.id()).values()) {
+ if (!policy.status().isActive()) continue;
+ for (var endpointId : policy.endpoints()) {
+ EndpointList.global(RoutingId.of(instance.id(), endpointId),
+ controller.system(), systemRoutingMethods())
+ .not().requiresRotation()
+ .forEach(endpoints::add);
+ }
}
- return legacyZoneEndpointsOf(deployment);
+ return EndpointList.copyOf(endpoints);
}
- /** Returns all non-global endpoint URLs for given deployments, grouped by their cluster ID and zone */
- public Map<ZoneId, Map<ClusterSpec.Id, URI>> zoneEndpointsOf(Collection<DeploymentId> deployments) {
- var endpoints = new TreeMap<ZoneId, Map<ClusterSpec.Id, URI>>(Comparator.comparing(ZoneId::value));
+ /** Returns all non-global endpoints and corresponding cluster IDs for given deployments, grouped by their zone */
+ public Map<ZoneId, Map<URI, ClusterSpec.Id>> zoneEndpointsOf(Collection<DeploymentId> deployments) {
+ var endpoints = new TreeMap<ZoneId, Map<URI, ClusterSpec.Id>>(Comparator.comparing(ZoneId::value));
for (var deployment : deployments) {
- var zoneEndpoints = zoneEndpointsOf(deployment);
+ var zoneEndpoints = endpointsOf(deployment).scope(Endpoint.Scope.zone).asList().stream()
+ .collect(Collectors.toUnmodifiableMap(Endpoint::url,
+ endpoint -> ClusterSpec.Id.from(endpoint.name())));
if (!zoneEndpoints.isEmpty()) {
endpoints.put(deployment.zoneId(), zoneEndpoints);
}
@@ -127,10 +136,9 @@ public class RoutingController {
/** Change status of all global endpoints for given deployment */
public void setGlobalRotationStatus(DeploymentId deployment, EndpointStatus status) {
- var globalEndpoints = legacyGlobalEndpointsOf(deployment);
- globalEndpoints.forEach(endpoint -> {
+ endpointsOf(deployment.applicationId()).requiresRotation().primary().ifPresent(endpoint -> {
try {
- controller.serviceRegistry().configServer().setGlobalRotationStatus(deployment, endpoint.upstreamName(), status);
+ controller.serviceRegistry().configServer().setGlobalRotationStatus(deployment, endpoint.upstreamIdOf(deployment), status);
} catch (Exception e) {
throw new RuntimeException("Failed to set rotation status of " + endpoint + " in " + deployment, e);
}
@@ -138,22 +146,16 @@ public class RoutingController {
}
/** Get global endpoint status for given deployment */
- public Map<RoutingEndpoint, EndpointStatus> globalRotationStatus(DeploymentId deployment) {
- var routingEndpoints = new LinkedHashMap<RoutingEndpoint, EndpointStatus>();
- legacyGlobalEndpointsOf(deployment).forEach(endpoint -> {
- var status = controller.serviceRegistry().configServer().getGlobalRotationStatus(deployment, endpoint.upstreamName());
+ public Map<Endpoint, EndpointStatus> globalRotationStatus(DeploymentId deployment) {
+ var routingEndpoints = new LinkedHashMap<Endpoint, EndpointStatus>();
+ endpointsOf(deployment.applicationId()).requiresRotation().primary().ifPresent(endpoint -> {
+ var upstreamName = endpoint.upstreamIdOf(deployment);
+ var status = controller.serviceRegistry().configServer().getGlobalRotationStatus(deployment, upstreamName);
routingEndpoints.put(endpoint, status);
});
return Collections.unmodifiableMap(routingEndpoints);
}
- /** Find the global endpoints of given deployment */
- private List<RoutingEndpoint> legacyGlobalEndpointsOf(DeploymentId deployment) {
- return routingEndpointsOf(deployment).stream()
- .filter(RoutingEndpoint::isGlobal)
- .collect(Collectors.toUnmodifiableList());
- }
-
/**
* Assigns one or more global rotations to given application, if eligible. The given application is implicitly
* stored, ensuring that the assigned rotation(s) are persisted when this returns.
@@ -181,8 +183,7 @@ public class RoutingController {
.isPresent();
for (var assignedRotation : instance.rotations()) {
var names = new ArrayList<String>();
- var endpoints = instance.endpointsIn(controller.system(), assignedRotation.endpointId())
- .scope(Endpoint.Scope.global);
+ var endpoints = endpointsOf(instance).named(assignedRotation.endpointId()).requiresRotation();
// Skip rotations which do not apply to this zone. Legacy names always point to all zones
if (!registerLegacyNames && !assignedRotation.regions().contains(zone.region())) {
@@ -191,13 +192,13 @@ public class RoutingController {
// Omit legacy DNS names when assigning rotations using <endpoints/> syntax
if (!registerLegacyNames) {
- endpoints = endpoints.legacy(false);
+ endpoints = endpoints.not().legacy();
}
// Register names in DNS
var rotation = rotationRepository.getRotation(assignedRotation.rotationId());
if (rotation.isPresent()) {
- endpoints.asList().forEach(endpoint -> {
+ endpoints.forEach(endpoint -> {
controller.nameServiceForwarder().createCname(RecordName.from(endpoint.dnsName()),
RecordData.fqdn(rotation.get().name()),
Priority.normal);
@@ -214,32 +215,19 @@ public class RoutingController {
/** Remove endpoints in DNS for all rotations assigned to given instance */
public void removeEndpointsInDns(Instance instance) {
- instance.rotations().forEach(assignedRotation -> {
- var endpoints = instance.endpointsIn(controller.system(), assignedRotation.endpointId());
- endpoints.asList().stream()
- .map(Endpoint::dnsName)
- .forEach(name -> {
- controller.nameServiceForwarder().removeRecords(Record.Type.CNAME, RecordName.from(name),
- Priority.normal);
- });
- });
- }
-
- private List<RoutingEndpoint> routingEndpointsOf(DeploymentId deployment) {
- if (!supportsRoutingMethod(RoutingMethod.shared, deployment.zoneId())) {
- return List.of(); // No rotations/shared routing layer in this zone.
- }
- try {
- return routingGenerator.endpoints(deployment);
- } catch (RuntimeException e) {
- log.log(Level.WARNING, "Failed to get endpoints for " + deployment, e);
- return List.of();
- }
+ endpointsOf(instance).requiresRotation()
+ .forEach(endpoint -> controller.nameServiceForwarder()
+ .removeRecords(Record.Type.CNAME,
+ RecordName.from(endpoint.dnsName()),
+ Priority.normal));
}
- /** Returns whether given routingMethod is supported by zone */
- public boolean supportsRoutingMethod(RoutingMethod routingMethod, ZoneId zone) {
- return controller.zoneRegistry().zones().routingMethod(routingMethod).ids().contains(zone);
+ /** Returns all routing methods supported by this system */
+ private List<RoutingMethod> systemRoutingMethods() {
+ return controller.zoneRegistry().zones().all().ids().stream()
+ .flatMap(zone -> controller.zoneRegistry().routingMethods(zone).stream())
+ .distinct()
+ .collect(Collectors.toUnmodifiableList());
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterCost.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterCost.java
deleted file mode 100644
index fb675862320..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterCost.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.application;
-
-import java.util.Objects;
-
-/**
- * Calculate utilization relative to the target utilization,
- * tco and waste for one cluster of one deployment.
- *
- * The target utilization is defined the following assumptions:
- * 1. CPU contention starts to cause problems at 0.8
- * 2. Memory management starts to cause problems at 0.7
- * 3. Load is evenly divided between two deployments - each deployments can handle the other.
- * 4. Memory and disk are agnostic to query load.
- * 5. Peak utilization (daily variations) are twice the size of the average.
- *
- * With this in mind we get:
- *
- * CPU: 0.8/2/2 = 0.2
- * Mem: 0.7
- * Disk: 0.7
- * Disk busy: 0.3
- *
- * @author smorgrav
- */
-public class ClusterCost {
-
- private final double tco;
- private final double waste;
- private final ClusterInfo clusterInfo;
- private final ClusterUtilization systemUtilization;
- private final ClusterUtilization targetUtilization;
- private final ClusterUtilization resultUtilization;
-
- /**
- * @param clusterInfo value object with cluster info e.g. the TCO for the hardware used
- * @param systemUtilization utilization of system resources (as ratios)
- */
- public ClusterCost(ClusterInfo clusterInfo,
- ClusterUtilization systemUtilization) {
-
- Objects.requireNonNull(clusterInfo, "Cluster info cannot be null");
- Objects.requireNonNull(systemUtilization, "Cluster utilization cannot be null");
-
- this.clusterInfo = clusterInfo;
- this.systemUtilization = systemUtilization;
- this.targetUtilization = new ClusterUtilization(0.7,0.2, 0.7, 0.3);
- this.resultUtilization = calculateResultUtilization(systemUtilization, targetUtilization);
-
- this.tco = clusterInfo.getHostnames().size() * clusterInfo.getFlavorCost();
-
- double unusedUtilization = 1 - Math.min(1, resultUtilization.getMaxUtilization());
- this.waste = tco * unusedUtilization;
- }
-
- /** @return The TCO in dollars for this cluster (node tco * nodes) */
- public double getTco() {
- return tco;
- }
-
- /** @return The amount of dollars spent for unused resources in this cluster */
- public double getWaste() {
- return waste;
- }
-
- public ClusterInfo getClusterInfo() {
- return clusterInfo;
- }
-
- public ClusterUtilization getSystemUtilization() {
- return systemUtilization;
- }
-
- public ClusterUtilization getTargetUtilization() {
- return targetUtilization;
- }
-
- public ClusterUtilization getResultUtilization() {
- return resultUtilization;
- }
-
- static ClusterUtilization calculateResultUtilization(ClusterUtilization system, ClusterUtilization target) {
- double cpu = ratio(system.getCpu(), target.getCpu());
- double mem = ratio(system.getMemory(), target.getMemory());
- double disk = ratio(system.getDisk(), target.getDisk());
- double diskbusy = ratio(system.getDiskBusy(), target.getDiskBusy());
-
- return new ClusterUtilization(mem, cpu, disk, diskbusy);
- }
-
- private static double ratio(double a, double b) {
- if (b == 0) return 1;
- return a/b;
- }
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterInfo.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterInfo.java
index 40fc57acdc8..803e88beae2 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterInfo.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterInfo.java
@@ -13,6 +13,7 @@ import java.util.List;
*
* @author smorgrav
*/
+// TODO(mpolden): Remove when we stop writing these fields.
public class ClusterInfo {
private final String flavor;
private final double flavorCPU;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilization.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilization.java
deleted file mode 100644
index ff92ce36d1b..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilization.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.application;
-
-/**
- * System resources as _ratios_ of available resources.
- *
- * Can be for actual readings or target numbers.
- *
- * @author smorgrav
- */
-public class ClusterUtilization {
-
- private final double memory;
- private final double cpu;
- private final double disk;
- private final double diskBusy;
- private final double maxUtilization;
-
- /**
- * Resource utilization as ratios. The ratio is normally between 0 and 1 where
- * one is fully utilized - but can be higher as it consumes more than it are guaranteed.
- *
- * @param memory Memory utilization
- * @param cpu CPU utilization
- * @param disk Disk utilization
- * @param diskBusy Disk busy
- */
- public ClusterUtilization(double memory, double cpu, double disk, double diskBusy) {
- this.memory = memory;
- this.cpu = cpu;
- this.disk = disk;
- this.diskBusy = diskBusy;
-
- double maxUtil = Math.max(cpu, disk);
- maxUtil = Math.max(maxUtil, memory);
- this.maxUtilization = Math.max(maxUtil, diskBusy);
- }
-
- /** @return The utilization ratio of the resource that is utilized the most. */
- public double getMaxUtilization() {
- return maxUtilization;
- }
-
- /** @return The utilization ratio for memory */
- public double getMemory() {
- return memory;
- }
-
- /** @return The utilization ratio for cpu */
- public double getCpu() {
- return cpu;
- }
-
- /** @return The utilization ratio for disk */
- public double getDisk() {
- return disk;
- }
-
- /** @return The disk busy ratio */
- public double getDiskBusy() {
- return diskBusy;
- }
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentCost.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentCost.java
deleted file mode 100644
index 393c14b35d3..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentCost.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.application;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Calculates cost for for an application deployment.
- *
- * @author smorgrav
- */
-public class DeploymentCost {
-
- private final double utilization;
- private final double waste;
- private final double tco;
-
- private final Map<String, ClusterCost> clusters;
-
- public DeploymentCost(Map<String, ClusterCost> clusterCosts) {
- clusters = new HashMap<>(clusterCosts);
-
- double tco = 0;
- double util = 0;
- double waste = 0;
- double maxWaste = -1;
-
- for (ClusterCost costCluster : clusterCosts.values()) {
- tco += costCluster.getTco();
- waste += costCluster.getWaste();
-
- if (costCluster.getWaste() > maxWaste) {
- util = costCluster.getResultUtilization().getMaxUtilization();
- maxWaste = costCluster.getWaste();
- }
- }
-
- this.utilization = util;
- this.waste = waste;
- this.tco = tco;
- }
-
- public Map<String, ClusterCost> getCluster() {
- return clusters;
- }
-
- /** Returns the total monthly cost of ownership for the deployment (sum of all clusters) */
- public double getTco() {
- return tco;
- }
-
- /** Returns the utilization of clusters that wastes most money in this deployment */
- public double getUtilization() {
- return utilization;
- }
-
- /** Returns the amount of dollars spent and not utilized */
- public double getWaste() {
- return waste;
- }
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index 5fa463233fd..d6b7d3d173e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -6,9 +6,13 @@ import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import java.net.URI;
import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Represents an application's endpoint. The endpoint scope can either be global or a specific zone. This is visible to
@@ -23,6 +27,7 @@ public class Endpoint {
private static final String PUBLIC_DNS_SUFFIX = ".public.vespa.oath.cloud";
private static final String PUBLIC_CD_DNS_SUFFIX = ".public-cd.vespa.oath.cloud";
+ private final String name;
private final URI url;
private final Scope scope;
private final boolean legacy;
@@ -37,6 +42,7 @@ public class Endpoint {
Objects.requireNonNull(system, "system must be non-null");
Objects.requireNonNull(port, "port must be non-null");
Objects.requireNonNull(routingMethod, "routingMethod must be non-null");
+ this.name = name;
this.url = createUrl(name, application, zone, system, port, legacy, routingMethod);
this.scope = zone == null ? Scope.global : Scope.zone;
this.legacy = legacy;
@@ -45,6 +51,17 @@ public class Endpoint {
this.wildcard = wildcard;
}
+ /**
+ * Returns the name of this endpoint (the first component of the DNS name). Depending on the endpoint type, this
+ * can be one of the following:
+ * - A wildcard (any scope)
+ * - A cluster name (only zone scope)
+ * - An endpoint ID (only global scope)
+ */
+ public String name() {
+ return name;
+ }
+
/** Returns the URL used to access this */
public URI url() {
return url;
@@ -76,22 +93,35 @@ public class Endpoint {
return tls;
}
+ /** Returns whether this requires a rotation to be reachable */
+ public boolean requiresRotation() {
+ return routingMethod.isShared() && scope == Scope.global;
+ }
+
/** Returns whether this is a wildcard endpoint (used only in certificates) */
public boolean wildcard() {
return wildcard;
}
+ /** Returns the upstream ID of given deployment. This *must* match what the routing layer generates */
+ public String upstreamIdOf(DeploymentId deployment) {
+ if (scope != Scope.global) throw new IllegalArgumentException("Scope " + scope + " does not have upstream name");
+ if (!routingMethod.isShared()) throw new IllegalArgumentException("Routing method " + routingMethod + " does not have upstream name");
+ return upstreamIdOf(name, deployment.applicationId(), deployment.zoneId());
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Endpoint endpoint = (Endpoint) o;
- return url.equals(endpoint.url);
+ return url.equals(endpoint.url) &&
+ routingMethod == endpoint.routingMethod;
}
@Override
public int hashCode() {
- return Objects.hash(url);
+ return Objects.hash(url, routingMethod);
}
@Override
@@ -169,6 +199,30 @@ public class Endpoint {
}
}
+ private static String upstreamIdOf(String name, ApplicationId application, ZoneId zone) {
+ return Stream.of(namePart(name, ""),
+ instancePart(application, ""),
+ application.application().value(),
+ application.tenant().value(),
+ zone.region().value(),
+ zone.environment().value())
+ .filter(Predicate.not(String::isEmpty))
+ .map(Endpoint::sanitizeUpstream)
+ .collect(Collectors.joining("."));
+ }
+
+ /** Remove any invalid characters from a upstream part */
+ private static String sanitizeUpstream(String part) {
+ return truncate(part.toLowerCase()
+ .replace('_', '-')
+ .replaceAll("[^a-z0-9-]*", ""));
+ }
+
+ /** Truncate the given part at the front so its length does not exceed 63 characters */
+ private static String truncate(String part) {
+ return part.substring(Math.max(0, part.length() - 63));
+ }
+
/** An endpoint's scope */
public enum Scope {
@@ -203,6 +257,12 @@ public class Endpoint {
return new Port(443, true);
}
+ /** Returns default port for the given routing method */
+ public static Port fromRoutingMethod(RoutingMethod method) {
+ if (method.isDirect()) return Port.tls();
+ return Port.tls(4443);
+ }
+
/** Create a HTTPS port */
public static Port tls(int port) {
return new Port(port, true);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
index e12bb5cda7f..955f8dc755c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
@@ -2,16 +2,15 @@
package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.collections.AbstractFilteringList;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
+import com.yahoo.vespa.hosted.controller.routing.RoutingId;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
/**
@@ -21,8 +20,6 @@ import java.util.stream.Stream;
*/
public class EndpointList extends AbstractFilteringList<Endpoint, EndpointList> {
- public static final EndpointList EMPTY = new EndpointList(List.of());
-
private EndpointList(Collection<? extends Endpoint> endpoints, boolean negate) {
super(endpoints, negate, EndpointList::new);
if (endpoints.stream().distinct().count() != endpoints.size()) {
@@ -34,14 +31,24 @@ public class EndpointList extends AbstractFilteringList<Endpoint, EndpointList>
this(endpoints, false);
}
- /** Returns the main endpoint, if any */
- public Optional<Endpoint> main() {
- return asList().stream().filter(Predicate.not(Endpoint::legacy)).findFirst();
+ /** Returns the primary (non-legacy) endpoint, if any */
+ public Optional<Endpoint> primary() {
+ return not().matching(Endpoint::legacy).asList().stream().findFirst();
+ }
+
+ /** Returns the subset of endpoints named according to given ID */
+ public EndpointList named(EndpointId id) {
+ return matching(endpoint -> endpoint.name().equals(id.id()));
+ }
+
+ /** Returns the subset of endpoints that are considered legacy */
+ public EndpointList legacy() {
+ return matching(Endpoint::legacy);
}
- /** Returns the subset of endpoints are either legacy or not */
- public EndpointList legacy(boolean legacy) {
- return matching(endpoint -> endpoint.legacy() == legacy);
+ /** Returns the subset of endpoints that require a rotation */
+ public EndpointList requiresRotation() {
+ return matching(Endpoint::requiresRotation);
}
/** Returns the subset of endpoints with given scope */
@@ -49,22 +56,40 @@ public class EndpointList extends AbstractFilteringList<Endpoint, EndpointList>
return matching(endpoint -> endpoint.scope() == scope);
}
- public static EndpointList of(Stream<Endpoint> endpoints) {
- return new EndpointList(endpoints.collect(Collectors.toUnmodifiableList()));
+ /** Returns all global endpoints for given routing ID and system provided by given routing methods */
+ public static EndpointList global(RoutingId routingId, SystemName system, List<RoutingMethod> routingMethods) {
+ var endpoints = new ArrayList<Endpoint>();
+ for (var method : routingMethods) {
+ endpoints.add(Endpoint.of(routingId.application())
+ .named(routingId.endpointId())
+ .on(Port.fromRoutingMethod(method))
+ .routingMethod(method)
+ .in(system));
+ // TODO(mpolden): Remove this once all applications have migrated away from legacy endpoints
+ if (method == RoutingMethod.shared) {
+ endpoints.add(Endpoint.of(routingId.application())
+ .named(routingId.endpointId())
+ .on(Port.plain(4080))
+ .legacy()
+ .routingMethod(method)
+ .in(system));
+ endpoints.add(Endpoint.of(routingId.application())
+ .named(routingId.endpointId())
+ .on(Port.tls(4443))
+ .legacy()
+ .routingMethod(method)
+ .in(system));
+ }
+ }
+ return new EndpointList(endpoints);
}
- /** Returns the default global endpoints in given system. Default endpoints are served by a pre-provisioned routing layer */
- public static EndpointList create(ApplicationId application, EndpointId endpointId, SystemName system) {
- switch (system) {
- case cd:
- case main:
- return new EndpointList(List.of(
- Endpoint.of(application).named(endpointId).on(Port.plain(4080)).legacy().in(system),
- Endpoint.of(application).named(endpointId).on(Port.tls(4443)).legacy().in(system),
- Endpoint.of(application).named(endpointId).on(Port.tls(4443)).in(system)
- ));
- }
- return EMPTY;
+ public static EndpointList global(RoutingId routingId, SystemName system, RoutingMethod routingMethod) {
+ return global(routingId, system, List.of(routingMethod));
+ }
+
+ public static EndpointList copyOf(Collection<Endpoint> endpoints) {
+ return new EndpointList(endpoints);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java
index ca359f5953a..d9f7d5f36b4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java
@@ -122,12 +122,7 @@ public class EndpointCertificateManager {
var latestAvailableVersion = latestVersionInSecretStore(currentCertificateMetadata.get());
if (latestAvailableVersion.isPresent() && latestAvailableVersion.getAsInt() > currentCertificateMetadata.get().version()) {
- var refreshedCertificateMetadata = new EndpointCertificateMetadata(
- currentCertificateMetadata.get().keyName(),
- currentCertificateMetadata.get().certName(),
- latestAvailableVersion.getAsInt()
- );
-
+ var refreshedCertificateMetadata = currentCertificateMetadata.get().withVersion(latestAvailableVersion.getAsInt());
validateEndpointCertificate(refreshedCertificateMetadata, instance, zone);
curator.writeEndpointCertificateMetadata(instance.id(), refreshedCertificateMetadata);
return Optional.of(refreshedCertificateMetadata);
@@ -163,7 +158,7 @@ public class EndpointCertificateManager {
Map<ApplicationId, EndpointCertificateMetadata> allEndpointCertificateMetadata = curator.readAllEndpointCertificateMetadata();
allEndpointCertificateMetadata.forEach((applicationId, storedMetaData) -> {
- if (storedMetaData.requestedDnsSans().isPresent() && storedMetaData.request_id().isPresent())
+ if (storedMetaData.requestedDnsSans().isPresent() && storedMetaData.request_id().isPresent() && storedMetaData.issuer().isPresent())
return;
var hashedCn = commonNameHashOf(applicationId, zoneRegistry.system()); // use as join key
@@ -181,7 +176,7 @@ public class EndpointCertificateManager {
storedMetaData.version(),
providerMetadata.request_id(),
providerMetadata.requestedDnsSans(),
- Optional.empty());
+ providerMetadata.issuer());
if (mode == BackfillMode.DRYRUN) {
log.log(LogLevel.INFO, "Would update stored metadata " + storedMetaData + " with data from provider: " + backfilledMetadata);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
index cd1d7796098..dc7fdeb8a98 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
@@ -115,6 +115,7 @@ public class InternalStepRunner implements StepRunner {
static final NodeResources DEFAULT_TESTER_RESOURCES_AWS =
new NodeResources(2, 8, 50, 0.3, NodeResources.DiskSpeed.any);
+ static final Duration capacityTimeout = Duration.ofMinutes(5);
static final Duration endpointTimeout = Duration.ofMinutes(15);
static final Duration endpointCertificateTimeout = Duration.ofMinutes(15);
static final Duration testerTimeout = Duration.ofMinutes(30);
@@ -159,7 +160,7 @@ public class InternalStepRunner implements StepRunner {
}
catch (RuntimeException e) {
logger.log(WARNING, "Unexpected exception running " + id, e);
- if (JobProfile.of(id.type()).alwaysRun().contains(step.get())) {
+ if (step.get().alwaysRun()) {
logger.log("Will keep trying, as this is a cleanup step.");
return Optional.empty();
}
@@ -173,34 +174,20 @@ public class InternalStepRunner implements StepRunner {
versions.sourcePlatform().orElse(versions.targetPlatform()) +
" and application version " +
versions.sourceApplication().orElse(versions.targetApplication()).id() + " ...");
- return deployReal(id, true, versions, logger);
+ return deployReal(id, true, logger);
}
private Optional<RunStatus> deployReal(RunId id, DualLogger logger) {
Versions versions = controller.jobController().run(id).get().versions();
logger.log("Deploying platform version " + versions.targetPlatform() +
" and application version " + versions.targetApplication().id() + " ...");
- return deployReal(id, false, versions, logger);
+ return deployReal(id, false, logger);
}
- private Optional<RunStatus> deployReal(RunId id, boolean setTheStage, Versions versions, DualLogger logger) {
- Optional<ApplicationPackage> applicationPackage = id.type().environment().isManuallyDeployed()
- ? Optional.of(new ApplicationPackage(controller.applications().applicationStore()
- .getDev(id.application(), id.type().zone(controller.system()))))
- : Optional.empty();
-
- Optional<Version> vespaVersion = id.type().environment().isManuallyDeployed()
- ? Optional.of(versions.targetPlatform())
- : Optional.empty();
+ private Optional<RunStatus> deployReal(RunId id, boolean setTheStage, DualLogger logger) {
return deploy(id.application(),
id.type(),
- () -> controller.applications().deploy(id.application(),
- id.type().zone(controller.system()),
- applicationPackage,
- new DeployOptions(false,
- vespaVersion,
- false,
- setTheStage)),
+ () -> controller.applications().deploy2(id.job(), setTheStage),
controller.jobController().run(id).get()
.stepInfo(setTheStage ? deployInitialReal : deployReal).get()
.startTime().get(),
@@ -215,10 +202,7 @@ public class InternalStepRunner implements StepRunner {
() -> controller.applications().deployTester(id.tester(),
testerPackage(id),
id.type().zone(controller.system()),
- new DeployOptions(true,
- Optional.of(platform),
- false,
- false)),
+ platform),
controller.jobController().run(id).get()
.stepInfo(deployTester).get()
.startTime().get(),
@@ -292,7 +276,9 @@ public class InternalStepRunner implements StepRunner {
return result;
case OUT_OF_CAPACITY:
logger.log(e.getServerMessage());
- return Optional.of(outOfCapacity);
+ return controller.system().isCd() && startTime.plus(capacityTimeout).isAfter(controller.clock().instant())
+ ? Optional.empty()
+ : Optional.of(outOfCapacity);
case INVALID_APPLICATION_PACKAGE:
case BAD_REQUEST:
logger.log(e.getMessage());
@@ -300,7 +286,8 @@ public class InternalStepRunner implements StepRunner {
}
throw e;
- } catch (EndpointCertificateException e) {
+ }
+ catch (EndpointCertificateException e) {
switch (e.type()) {
case CERT_NOT_AVAILABLE:
// Same as CERTIFICATE_NOT_READY above, only from the controller
@@ -450,11 +437,11 @@ public class InternalStepRunner implements StepRunner {
/** Returns true iff all containers in the deployment give 100 consecutive 200 OK responses on /status.html. */
private boolean containersAreUp(ApplicationId id, ZoneId zoneId, DualLogger logger) {
- var endpoints = controller.routingController().zoneEndpointsOf(Set.of(new DeploymentId(id, zoneId)));
+ var endpoints = controller.routing().zoneEndpointsOf(Set.of(new DeploymentId(id, zoneId)));
if ( ! endpoints.containsKey(zoneId))
return false;
- for (URI endpoint : endpoints.get(zoneId).values()) {
+ for (var endpoint : endpoints.get(zoneId).keySet()) {
boolean ready = controller.jobController().cloud().ready(endpoint);
if (!ready) {
logger.log("Failed to get 100 consecutive OKs from " + endpoint);
@@ -477,12 +464,12 @@ public class InternalStepRunner implements StepRunner {
}
private boolean endpointsAvailable(ApplicationId id, ZoneId zone, DualLogger logger) {
- var endpoints = controller.routingController().zoneEndpointsOf(Set.of(new DeploymentId(id, zone)));
+ var endpoints = controller.routing().zoneEndpointsOf(Set.of(new DeploymentId(id, zone)));
if ( ! endpoints.containsKey(zone)) {
logger.log("Endpoints not yet ready.");
return false;
}
- for (var endpoint : endpoints.get(zone).values())
+ for (var endpoint : endpoints.get(zone).keySet())
if ( ! controller.jobController().cloud().exists(endpoint)) {
logger.log(INFO, "DNS lookup yielded no IP address for '" + endpoint + "'.");
return false;
@@ -492,12 +479,12 @@ public class InternalStepRunner implements StepRunner {
return true;
}
- private void logEndpoints(Map<ZoneId, Map<ClusterSpec.Id, URI>> endpoints, DualLogger logger) {
+ private void logEndpoints(Map<ZoneId, Map<URI, ClusterSpec.Id>> endpoints, DualLogger logger) {
List<String> messages = new ArrayList<>();
messages.add("Found endpoints:");
- endpoints.forEach((zone, uris) -> {
+ endpoints.forEach((zone, urls) -> {
messages.add("- " + zone);
- uris.forEach((cluster, uri) -> messages.add(" |-- " + uri + " (" + cluster + ")"));
+ urls.forEach((url, cluster) -> messages.add(" |-- " + url + " (" + cluster + ")"));
});
logger.log(messages);
}
@@ -550,7 +537,7 @@ public class InternalStepRunner implements StepRunner {
deployments.add(new DeploymentId(id.application(), zoneId));
logger.log("Attempting to find endpoints ...");
- var endpoints = controller.routingController().zoneEndpointsOf(deployments);
+ var endpoints = controller.routing().zoneEndpointsOf(deployments);
if ( ! endpoints.containsKey(zoneId)) {
logger.log(WARNING, "Endpoints for the deployment to test vanished again, while it was still active!");
return Optional.of(error);
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 6904bff8548..5d8ad6594df 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
@@ -26,7 +26,6 @@ import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.persistence.BufferedLogStore;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
-import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import java.net.URI;
import java.security.cert.X509Certificate;
@@ -414,9 +413,11 @@ public class JobController {
/** Orders a run of the given type, or throws an IllegalStateException if that job type is already running. */
public void start(ApplicationId id, JobType type, Versions versions) {
- if ( ! type.environment().isManuallyDeployed() && versions.targetApplication().isUnknown())
- throw new IllegalArgumentException("Target application must be a valid reference.");
+ start(id, type, versions, JobProfile.of(type));
+ }
+ /** Orders a run of the given type, or throws an IllegalStateException if that job type is already running. */
+ public void start(ApplicationId id, JobType type, Versions versions, JobProfile profile) {
controller.applications().lockApplicationIfPresent(TenantAndApplicationId.from(id), application -> {
locked(id, type, __ -> {
Optional<Run> last = last(id, type);
@@ -424,7 +425,7 @@ public class JobController {
throw new IllegalStateException("Can not start " + type + " for " + id + "; it is already running!");
RunId newId = new RunId(id, type, last.map(run -> run.id().number()).orElse(0L) + 1);
- curator.writeLastRun(Run.initial(newId, versions, controller.clock().instant()));
+ curator.writeLastRun(Run.initial(newId, versions, controller.clock().instant(), profile));
metric.jobStarted(newId.job());
});
});
@@ -432,9 +433,6 @@ public class JobController {
/** Stores the given package and starts a deployment of it, after aborting any such ongoing deployment. */
public void deploy(ApplicationId id, JobType type, Optional<Version> platform, ApplicationPackage applicationPackage) {
- if ( ! type.environment().isManuallyDeployed())
- throw new IllegalArgumentException("Direct deployments are only allowed to manually deployed environments.");
-
controller.applications().lockApplicationOrThrow(TenantAndApplicationId.from(id), application -> {
if ( ! application.get().instances().containsKey(id.instance()))
application = controller.applications().withNewInstance(application, id);
@@ -445,10 +443,15 @@ public class JobController {
last(id, type).filter(run -> ! run.hasEnded()).ifPresent(run -> abortAndWait(run.id()));
locked(id, type, __ -> {
controller.applications().applicationStore().putDev(id, type.zone(controller.system()), applicationPackage.zippedContent());
- start(id, type, new Versions(platform.orElse(controller.systemVersion()),
- ApplicationVersion.unknown,
- Optional.empty(),
- Optional.empty()));
+ start(id,
+ type,
+ new Versions(platform.orElse(applicationPackage.deploymentSpec().majorVersion()
+ .flatMap(controller.applications()::lastCompatibleVersion)
+ .orElseGet(controller::systemVersion)),
+ ApplicationVersion.unknown,
+ Optional.empty(),
+ Optional.empty()),
+ JobProfile.development);
runner.get().accept(last(id, type).get());
});
@@ -504,7 +507,7 @@ public class JobController {
} finally {
// Passing an empty DeploymentSpec here is fine as it's used for registering global endpoint names, and
// tester instances have none.
- controller.routingController().policies().refresh(id.id(), DeploymentSpec.empty, zone);
+ controller.routing().policies().refresh(id.id(), DeploymentSpec.empty, zone);
}
}
@@ -532,17 +535,12 @@ public class JobController {
.collect(toList()));
}
- /** Returns the tester endpoint URL, if any */
- Optional<URI> testerEndpoint(RunId id) {
- var testerId = new DeploymentId(id.tester().id(), id.type().zone(controller.system()));
- return controller.routingController().zoneEndpointsOf(testerId).values().stream().findFirst();
- }
-
private void prunePackages(TenantAndApplicationId id) {
controller.applications().lockApplicationIfPresent(id, application -> {
application.get().productionDeployments().values().stream()
.flatMap(List::stream)
.map(Deployment::applicationVersion)
+ .filter(version -> ! version.isUnknown())
.min(Comparator.comparingLong(applicationVersion -> applicationVersion.buildNumber().getAsLong()))
.ifPresent(oldestDeployed -> {
controller.applications().applicationStore().prune(id.tenant(), id.application(), oldestDeployed);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java
index 88b3442bd6e..1c1d60a2cf0 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobProfile.java
@@ -34,8 +34,8 @@ public enum JobProfile {
deployTester,
installTester,
startTests,
- endTests),
- EnumSet.of(copyVespaLogs,
+ endTests,
+ copyVespaLogs,
deactivateTester,
deactivateReal,
report)),
@@ -49,37 +49,35 @@ public enum JobProfile {
deployReal,
installReal,
startTests,
- endTests),
- EnumSet.of(copyVespaLogs,
+ endTests,
+ copyVespaLogs,
deactivateTester,
deactivateReal,
report)),
production(EnumSet.of(deployReal,
- installReal),
- EnumSet.of(report)),
+ installReal,
+ report)),
productionTest(EnumSet.of(deployTester,
installTester,
startTests,
- endTests),
- EnumSet.of(deactivateTester,
+ endTests,
+ deactivateTester,
report)),
development(EnumSet.of(deployReal,
- installReal),
- EnumSet.of(copyVespaLogs));
+ installReal,
+ copyVespaLogs));
private final Set<Step> steps;
- private final Set<Step> alwaysRun;
- JobProfile(Set<Step> runWhileSuccess, Set<Step> alwaysRun) {
- runWhileSuccess.addAll(alwaysRun);
- this.steps = Collections.unmodifiableSet(runWhileSuccess);
- this.alwaysRun = Collections.unmodifiableSet(alwaysRun);
+ JobProfile(Set<Step> steps) {
+ this.steps = Collections.unmodifiableSet(steps);
}
+ // TODO jonmv: Let caller decide profile, and store with run?
public static JobProfile of(JobType type) {
switch (type.environment()) {
case test: return systemTest;
@@ -94,7 +92,4 @@ public enum JobProfile {
/** Returns all steps in this profile, the default for which is to run only when all prerequisites are successes. */
public Set<Step> steps() { return steps; }
- /** Returns the set of steps that should always be run, regardless of outcome. */
- public Set<Step> alwaysRun() { return alwaysRun; }
-
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java
index 8cd57fa7d3a..d2481cd97ad 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java
@@ -56,9 +56,9 @@ public class Run {
this.testerCertificate = testerCertificate;
}
- public static Run initial(RunId id, Versions versions, Instant now) {
+ public static Run initial(RunId id, Versions versions, Instant now, JobProfile profile) {
EnumMap<Step, StepInfo> steps = new EnumMap<>(Step.class);
- JobProfile.of(id.type()).steps().forEach(step -> steps.put(step, StepInfo.initial(step)));
+ profile.steps().forEach(step -> steps.put(step, StepInfo.initial(step)));
return new Run(id, steps, requireNonNull(versions), requireNonNull(now), Optional.empty(), running,
-1, Instant.EPOCH, Optional.empty(), Optional.empty(), Optional.empty());
}
@@ -268,9 +268,9 @@ public class Run {
private List<Step> forcedSteps() {
return ImmutableList.copyOf(steps.entrySet().stream()
.filter(entry -> entry.getValue().status() == unfinished
- && JobProfile.of(id.type()).alwaysRun().contains(entry.getKey())
+ && entry.getKey().alwaysRun()
&& entry.getKey().prerequisites().stream()
- .filter(JobProfile.of(id.type()).alwaysRun()::contains)
+ .filter(Step::alwaysRun)
.allMatch(step -> steps.get(step) == null
|| steps.get(step).status() != unfinished))
.map(Map.Entry::getKey)
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java
index 44a93a655a8..75d875a29c5 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java
@@ -26,54 +26,59 @@ import java.util.List;
public enum Step {
/** Download test-jar and assemble and deploy tester application. */
- deployTester,
+ deployTester(false),
/** See that tester is done deploying, and is ready to serve. */
- installTester(deployTester),
+ installTester(false, deployTester),
/** Download and deploy the initial real application, for staging tests. */
- deployInitialReal(deployTester),
+ deployInitialReal(false, deployTester),
/** See that the real application has had its nodes converge to the initial state. */
- installInitialReal(deployInitialReal),
+ installInitialReal(false, deployInitialReal),
/** Ask the tester to run its staging setup. */
- startStagingSetup(installInitialReal, installTester),
+ startStagingSetup(false, installInitialReal, installTester),
/** See that the staging setup is done. */
- endStagingSetup(startStagingSetup),
+ endStagingSetup(false, startStagingSetup),
/** Download and deploy real application, restarting services if required. */
- deployReal(endStagingSetup, deployTester),
+ deployReal(false, endStagingSetup, deployTester),
/** See that real application has had its nodes converge to the wanted version and generation. */
- installReal(deployReal),
+ installReal(false, deployReal),
/** Ask the tester to run its tests. */
- startTests(installReal, installTester),
+ startTests(false, installReal, installTester),
/** See that the tests are done running. */
- endTests(startTests),
+ endTests(false, startTests),
/** Fetch and store Vespa logs from the log server cluster of the deployment -- used for test and dev deployments. */
- copyVespaLogs(installReal, endTests),
+ copyVespaLogs(true, installReal, endTests),
/** Delete the real application -- used for test deployments. */
- deactivateReal(deployInitialReal, deployReal, endTests, copyVespaLogs),
+ deactivateReal(true, deployInitialReal, deployReal, endTests, copyVespaLogs),
/** Deactivate the tester. */
- deactivateTester(deployTester, endTests),
+ deactivateTester(true, deployTester, endTests),
/** Report completion to the deployment orchestration machinery. */
- report(deactivateReal, deactivateTester);
+ report(true, deactivateReal, deactivateTester);
+ private final boolean alwaysRun;
private final List<Step> prerequisites;
- Step(Step... prerequisites) {
+ Step(boolean alwaysRun, Step... prerequisites) {
+ this.alwaysRun = alwaysRun;
this.prerequisites = ImmutableList.copyOf(prerequisites);
}
+ /** Returns whether this is a cleanup-step, and should always run, regardless of job outcome, when specified in a job. */
+ public boolean alwaysRun() { return alwaysRun; }
+
/** Returns the prerequisite steps that must be successfully completed before this, assuming the job contains these steps. */
public List<Step> prerequisites() { return prerequisites; }
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializer.java
index 0dfe945af2b..945ffa529ba 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializer.java
@@ -32,7 +32,7 @@ public class TestConfigSerializer {
public Slime configSlime(ApplicationId id,
JobType type,
boolean isCI,
- Map<ZoneId, Map<ClusterSpec.Id, URI>> deployments,
+ Map<ZoneId, Map<URI, ClusterSpec.Id>> deployments,
Map<ZoneId, List<String>> clusters) {
Slime slime = new Slime();
Cursor root = slime.setObject();
@@ -45,14 +45,14 @@ public class TestConfigSerializer {
Cursor endpointsObject = root.setObject("endpoints"); // TODO jvenstad: remove.
deployments.forEach((zone, endpoints) -> {
Cursor endpointArray = endpointsObject.setArray(zone.value());
- for (URI endpoint : endpoints.values())
+ for (URI endpoint : endpoints.keySet())
endpointArray.addString(endpoint.toString());
});
Cursor zoneEndpointsObject = root.setObject("zoneEndpoints");
deployments.forEach((zone, endpoints) -> {
Cursor clusterEndpointsObject = zoneEndpointsObject.setObject(zone.value());
- endpoints.forEach((cluster, endpoint) -> {
+ endpoints.forEach((endpoint, cluster) -> {
clusterEndpointsObject.setString(cluster.value(), endpoint.toString());
});
});
@@ -73,7 +73,7 @@ public class TestConfigSerializer {
public byte[] configJson(ApplicationId id,
JobType type,
boolean isCI,
- Map<ZoneId, Map<ClusterSpec.Id, URI>> deployments,
+ Map<ZoneId, Map<URI, ClusterSpec.Id>> deployments,
Map<ZoneId, List<String>> clusters) {
try {
return SlimeUtils.toJsonBytes(configSlime(id, type, isCI, deployments, clusters));
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java
deleted file mode 100644
index 88c1a48653a..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.maintenance;
-
-import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.HostName;
-import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.Instance;
-import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
-import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
-import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository;
-import com.yahoo.vespa.hosted.controller.application.ClusterInfo;
-import com.yahoo.vespa.hosted.controller.application.Deployment;
-
-import java.time.Duration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-/**
- * Maintains information about hardware, hostnames and cluster specifications.
- *
- * This is used to calculate cost metrics for the application api.
- *
- * @author smorgrav
- */
-public class ClusterInfoMaintainer extends Maintainer {
-
- private static final Logger log = Logger.getLogger(ClusterInfoMaintainer.class.getName());
-
- private final Controller controller;
- private final NodeRepository nodeRepository;
-
- ClusterInfoMaintainer(Controller controller, Duration duration, JobControl jobControl) {
- super(controller, duration, jobControl);
- this.controller = controller;
- this.nodeRepository = controller.serviceRegistry().configServer().nodeRepository();
- }
-
- private Map<ClusterSpec.Id, ClusterInfo> getClusterInfo(List<Node> nodes) {
- Map<ClusterSpec.Id, ClusterInfo> infoMap = new HashMap<>();
-
- // Group nodes by clusterid
- Map<String, List<Node>> clusters = nodes.stream().collect(Collectors.groupingBy(Node::clusterId));
-
- // For each cluster - get info
- for (String id : clusters.keySet()) {
- List<Node> clusterNodes = clusters.get(id);
-
- // Assume they are all equal and use first node as a representative for the cluster
- Node node = clusterNodes.get(0);
-
- // Add to map
- List<String> hostnames = clusterNodes.stream()
- .map(Node::hostname)
- .map(HostName::value)
- .collect(Collectors.toList());
- ClusterInfo info = new ClusterInfo(node.flavor(), node.cost(),
- node.resources().vcpu(), node.resources().memoryGb(), node.resources().diskGb(),
- ClusterSpec.Type.from(node.clusterType().name()), hostnames);
- infoMap.put(new ClusterSpec.Id(id), info);
- }
-
- return infoMap;
- }
-
- @Override
- protected void maintain() {
- for (Application application : controller().applications().asList()) {
- for (Instance instance : application.instances().values()) {
- for (Deployment deployment : instance.deployments().values()) {
- DeploymentId deploymentId = new DeploymentId(instance.id(), deployment.zone());
- try {
- var nodes = nodeRepository.list(deploymentId.zoneId(), deploymentId.applicationId());
- Map<ClusterSpec.Id, ClusterInfo> clusterInfo = getClusterInfo(nodes);
- controller().applications().lockApplicationIfPresent(application.id(), lockedApplication ->
- controller.applications().store(lockedApplication.with(instance.name(),
- locked -> locked.withClusterInfo(deployment.zone(), clusterInfo))));
- }
- catch (Exception e) {
- log.log(Level.WARNING, "Failing getting cluster information for " + deploymentId, 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 ec209c5ed98..18fe96fc9b2 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
@@ -32,7 +32,6 @@ public class ControllerMaintenance extends AbstractComponent {
private final VersionStatusUpdater versionStatusUpdater;
private final Upgrader upgrader;
private final ReadyJobsTrigger readyJobsTrigger;
- private final ClusterInfoMaintainer clusterInfoMaintainer;
private final DeploymentMetricsMaintainer deploymentMetricsMaintainer;
private final ApplicationOwnershipConfirmer applicationOwnershipConfirmer;
private final SystemUpgrader systemUpgrader;
@@ -64,7 +63,6 @@ public class ControllerMaintenance extends AbstractComponent {
versionStatusUpdater = new VersionStatusUpdater(controller, Duration.ofMinutes(3), jobControl);
upgrader = new Upgrader(controller, maintenanceInterval, jobControl, curator);
readyJobsTrigger = new ReadyJobsTrigger(controller, Duration.ofMinutes(1), jobControl);
- clusterInfoMaintainer = new ClusterInfoMaintainer(controller, Duration.ofHours(2), jobControl);
deploymentMetricsMaintainer = new DeploymentMetricsMaintainer(controller, Duration.ofMinutes(5), jobControl);
applicationOwnershipConfirmer = new ApplicationOwnershipConfirmer(controller, Duration.ofHours(12), jobControl, controller.serviceRegistry().ownershipIssues());
systemUpgrader = new SystemUpgrader(controller, Duration.ofMinutes(1), jobControl);
@@ -95,7 +93,6 @@ public class ControllerMaintenance extends AbstractComponent {
versionStatusUpdater.deconstruct();
upgrader.deconstruct();
readyJobsTrigger.deconstruct();
- clusterInfoMaintainer.deconstruct();
deploymentMetricsMaintainer.deconstruct();
applicationOwnershipConfirmer.deconstruct();
systemUpgrader.deconstruct();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
index d88469645b4..a4ace4cf518 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
@@ -64,8 +64,8 @@ public class MetricsReporter extends Maintainer {
}
private void reportRemainingRotations() {
- try (RotationLock lock = controller().routingController().rotations().lock()) {
- int availableRotations = controller().routingController().rotations().availableRotations(lock).size();
+ try (RotationLock lock = controller().routing().rotations().lock()) {
+ int availableRotations = controller().routing().rotations().availableRotations(lock).size();
metric.set(REMAINING_ROTATIONS, availableRotations, metric.createContext(Map.of()));
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java
index bdfaa9c098f..1d1d5e93dd8 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java
@@ -82,7 +82,7 @@ public class RotationStatusUpdater extends Maintainer {
private RotationStatus getStatus(Instance instance) {
var statusMap = new LinkedHashMap<RotationId, RotationStatus.Targets>();
for (var assignedRotation : instance.rotations()) {
- var rotation = controller().routingController().rotations().getRotation(assignedRotation.rotationId());
+ var rotation = controller().routing().rotations().getRotation(assignedRotation.rotationId());
if (rotation.isEmpty()) continue;
var targets = service.getHealthStatus(rotation.get().name()).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, (kv) -> from(kv.getValue())));
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
index ad411a895fc..a15bfd07a66 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
@@ -92,6 +92,7 @@ public class ApplicationSerializer {
private static final String pemDeployKeysField = "pemDeployKeys";
private static final String assignedRotationClusterField = "clusterId";
private static final String assignedRotationRotationField = "rotationId";
+ private static final String assignedRotationRegionsField = "regions";
private static final String versionField = "version";
// Instance fields
@@ -190,7 +191,7 @@ public class ApplicationSerializer {
instanceObject.setString(instanceNameField, instance.name().value());
deploymentsToSlime(instance.deployments().values(), instanceObject.setArray(deploymentsField));
toSlime(instance.jobPauses(), instanceObject.setObject(deploymentJobsField));
- assignedRotationsToSlime(instance.rotations(), instanceObject, assignedRotationsField);
+ assignedRotationsToSlime(instance.rotations(), instanceObject);
toSlime(instance.rotationStatus(), instanceObject.setArray(rotationStatusField));
toSlime(instance.change(), instanceObject, deployingField);
}
@@ -210,6 +211,7 @@ public class ApplicationSerializer {
object.setString(versionField, deployment.version().toString());
object.setLong(deployTimeField, deployment.at().toEpochMilli());
toSlime(deployment.applicationVersion(), object.setObject(applicationPackageRevisionField));
+ // TODO(mpolden): Stop writing this after next release.
clusterInfoToSlime(deployment.clusterInfo(), object);
deploymentMetricsToSlime(deployment.metrics(), object);
deployment.activity().lastQueried().ifPresent(instant -> object.setLong(lastQueriedField, instant.toEpochMilli()));
@@ -308,13 +310,17 @@ public class ApplicationSerializer {
});
}
- private void assignedRotationsToSlime(List<AssignedRotation> rotations, Cursor parent, String fieldName) {
- var rotationsArray = parent.setArray(fieldName);
+ private void assignedRotationsToSlime(List<AssignedRotation> rotations, Cursor parent) {
+ var rotationsArray = parent.setArray(assignedRotationsField);
for (var rotation : rotations) {
var object = rotationsArray.addObject();
object.setString(assignedRotationEndpointField, rotation.endpointId().id());
object.setString(assignedRotationRotationField, rotation.rotationId().asString());
object.setString(assignedRotationClusterField, rotation.clusterId().value());
+ var regionsArray = object.setArray(assignedRotationRegionsField);
+ for (var region : rotation.regions()) {
+ regionsArray.addString(region.value());
+ }
}
}
@@ -393,7 +399,7 @@ public class ApplicationSerializer {
applicationVersionFromSlime(deploymentObject.field(applicationPackageRevisionField)),
Version.fromString(deploymentObject.field(versionField).asString()),
Instant.ofEpochMilli(deploymentObject.field(deployTimeField).asLong()),
- clusterInfoMapFromSlime(deploymentObject.field(clusterInfoField)),
+ Map.of(),
deploymentMetricsFromSlime(deploymentObject.field(deploymentMetricsField)),
DeploymentActivity.create(Serializers.optionalInstant(deploymentObject.field(lastQueriedField)),
Serializers.optionalInstant(deploymentObject.field(lastWrittenField)),
@@ -444,25 +450,6 @@ public class ApplicationSerializer {
return Collections.unmodifiableMap(rotationStatus);
}
- private Map<ClusterSpec.Id, ClusterInfo> clusterInfoMapFromSlime (Inspector object) {
- Map<ClusterSpec.Id, ClusterInfo> map = new HashMap<>();
- object.traverse((String name, Inspector value) -> map.put(new ClusterSpec.Id(name), clusterInfoFromSlime(value)));
- return map;
- }
-
- private ClusterInfo clusterInfoFromSlime(Inspector inspector) {
- String flavor = inspector.field(clusterInfoFlavorField).asString();
- int cost = (int)inspector.field(clusterInfoCostField).asLong();
- String type = inspector.field(clusterInfoTypeField).asString();
- double flavorCpu = inspector.field(clusterInfoCpuField).asDouble();
- double flavorMem = inspector.field(clusterInfoMemField).asDouble();
- double flavorDisk = inspector.field(clusterInfoDiskField).asDouble();
-
- List<String> hostnames = new ArrayList<>();
- inspector.field(clusterInfoHostnamesField).traverse((ArrayTraverser)(int index, Inspector value) -> hostnames.add(value.asString()));
- return new ClusterInfo(flavor, cost, flavorCpu, flavorMem, flavorDisk, ClusterSpec.Type.from(type), hostnames);
- }
-
private ZoneId zoneIdFromSlime(Inspector object) {
return ZoneId.from(object.field(environmentField).asString(), object.field(regionField).asString());
}
@@ -514,11 +501,11 @@ public class ApplicationSerializer {
private List<AssignedRotation> assignedRotationsFromSlime(DeploymentSpec deploymentSpec, InstanceName instance, Inspector root) {
var assignedRotations = new LinkedHashMap<EndpointId, AssignedRotation>();
-
root.field(assignedRotationsField).traverse((ArrayTraverser) (idx, inspector) -> {
var clusterId = new ClusterSpec.Id(inspector.field(assignedRotationClusterField).asString());
var endpointId = EndpointId.of(inspector.field(assignedRotationEndpointField).asString());
var rotationId = new RotationId(inspector.field(assignedRotationRotationField).asString());
+ // TODO(mpolden): Read regions from field instead of deployment spec after next release
var regions = deploymentSpec.instance(instance)
.map(spec -> globalEndpointRegions(spec, endpointId))
.orElse(Set.of());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java
index 501d3a06d42..80d8270eaaa 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializer.java
@@ -48,6 +48,7 @@ public class EndpointCertificateMetadataSerializer {
Cursor cursor = object.setArray(requestedDnsSansField);
sans.forEach(cursor::addString);
});
+ metadata.issuer().ifPresent(id -> object.setString(issuerField, id));
return slime;
}
@@ -77,7 +78,7 @@ public class EndpointCertificateMetadataSerializer {
issuer);
}
- public static EndpointCertificateMetadata fromJsonString(String zkdata) {
- return fromSlime(SlimeUtils.jsonToSlime(zkdata).get());
+ public static EndpointCertificateMetadata fromJsonString(String zkData) {
+ return fromSlime(SlimeUtils.jsonToSlime(zkData).get());
}
}
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 5e234d31322..7b2e1ad7a8e 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
@@ -29,10 +29,7 @@ import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
-import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
-import com.yahoo.vespa.athenz.api.AthenzUser;
-import com.yahoo.vespa.hosted.controller.AlreadyExistsException;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.Instance;
@@ -66,10 +63,7 @@ import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.AssignedRotation;
import com.yahoo.vespa.hosted.controller.application.Change;
-import com.yahoo.vespa.hosted.controller.application.ClusterCost;
-import com.yahoo.vespa.hosted.controller.application.ClusterUtilization;
import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.application.DeploymentCost;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
@@ -111,6 +105,7 @@ import java.time.Duration;
import java.time.Instant;
import java.time.YearMonth;
import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Comparator;
@@ -853,28 +848,20 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
}
+ // TODO(mpolden): Remove once legacy dashboard and integration tests stop expecting these fields
private void globalEndpointsToSlime(Cursor object, Instance instance) {
var globalEndpointUrls = new LinkedHashSet<String>();
- // Add default global endpoints. These are backed by rotations.
- instance.endpointsIn(controller.system())
- .scope(Endpoint.Scope.global)
- .legacy(false) // Hide legacy names
- .asList().stream()
- .map(Endpoint::url)
- .map(URI::toString)
- .forEach(globalEndpointUrls::add);
-
- // Per-cluster endpoints. These are backed by load balancers.
- var routingPolicies = controller.routingController().policies().get(instance.id()).values();
- for (var policy : routingPolicies) {
- policy.globalEndpointsIn(controller.system()).asList().stream()
+ // Add global endpoints backed by rotations
+ controller.routing().endpointsOf(instance.id())
+ .requiresRotation()
+ .not().legacy() // Hide legacy names
+ .asList().stream()
.map(Endpoint::url)
.map(URI::toString)
.forEach(globalEndpointUrls::add);
- }
- // TODO(mpolden): Remove once clients stop expecting this field
+
var globalRotationsArray = object.setArray("globalRotations");
globalEndpointUrls.forEach(globalRotationsArray::addString);
@@ -1047,6 +1034,14 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
.ifPresent(version -> toSlime(version, object.setObject("revision")));
}
+ private void toSlime(Endpoint endpoint, String cluster, Cursor object) {
+ object.setString("cluster", cluster);
+ object.setBool("tls", endpoint.tls());
+ object.setString("url", endpoint.url().toString());
+ object.setString("scope", endpointScopeString(endpoint.scope()));
+ object.setString("routingMethod", routingMethodString(endpoint.routingMethod()));
+ }
+
private void toSlime(Cursor response, DeploymentId deploymentId, Deployment deployment, HttpRequest request) {
response.setString("tenant", deploymentId.applicationId().tenant().value());
response.setString("application", deploymentId.applicationId().application().value());
@@ -1054,68 +1049,28 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
response.setString("environment", deploymentId.zoneId().environment().value());
response.setString("region", deploymentId.zoneId().region().value());
- // Add zone endpoints defined by routing policies
+ var application = controller.applications().requireApplication(TenantAndApplicationId.from(deploymentId.applicationId()));
+ var instance = application.instances().get(deploymentId.applicationId().instance());
+
+ // Add zone endpoints
var endpointArray = response.setArray("endpoints");
- for (var policy : controller.routingController().policies().get(deploymentId).values()) {
- // TODO(mpolden): Always add endpoints from all policies, independent of routing method. This allows removal
- // of RoutingGenerator and eliminates the external call to the routing layer below.
- if (!controller.routingController().supportsRoutingMethod(RoutingMethod.exclusive, deployment.zone())) continue;
- if (!policy.status().isActive()) continue;
- {
- var endpointObject = endpointArray.addObject();
- var endpoint = policy.endpointIn(controller.system());
- endpointObject.setString("cluster", policy.id().cluster().value());
- endpointObject.setBool("tls", endpoint.tls());
- endpointObject.setString("url", endpoint.url().toString());
- endpointObject.setString("scope", endpointScopeString(endpoint.scope()));
- endpointObject.setString("routingMethod", routingMethodString(RoutingMethod.exclusive));
- }
- // Add global endpoints that point to this policy
- for (var endpoint : policy.globalEndpointsIn(controller.system()).asList()) {
- var endpointObject = endpointArray.addObject();
- endpointObject.setString("cluster", policy.id().cluster().value());
- endpointObject.setBool("tls", endpoint.tls());
- endpointObject.setString("url", endpoint.url().toString());
- endpointObject.setString("scope", endpointScopeString(endpoint.scope()));
- endpointObject.setString("routingMethod", routingMethodString(RoutingMethod.exclusive));
+ var serviceUrls = new ArrayList<URI>();
+ for (var endpoint : controller.routing().endpointsOf(deploymentId)) {
+ toSlime(endpoint, endpoint.name(), endpointArray.addObject());
+ if (endpoint.routingMethod() == RoutingMethod.shared) {
+ serviceUrls.add(endpoint.url());
}
}
- // Add zone endpoints served by shared routing layer
- for (var clusterAndUrl : controller.routingController().legacyZoneEndpointsOf(deploymentId).entrySet()) {
- var endpointObject = endpointArray.addObject();
- endpointObject.setString("cluster", clusterAndUrl.getKey().value());
- endpointObject.setBool("tls", true);
- endpointObject.setString("url", clusterAndUrl.getValue().toString());
- endpointObject.setString("scope", endpointScopeString(Endpoint.Scope.zone));
- endpointObject.setString("routingMethod", routingMethodString(RoutingMethod.shared));
- }
- // Add global endpoints served by shared routing layer
- var application = controller.applications().requireApplication(TenantAndApplicationId.from(deploymentId.applicationId()));
- var instance = application.instances().get(deploymentId.applicationId().instance());
+ // Add global endpoints
if (deploymentId.zoneId().environment().isProduction()) { // Global endpoints can only point to production deployments
- for (var rotation : instance.rotations()) {
- var endpoints = instance.endpointsIn(controller.system(), rotation.endpointId())
- .legacy(false)
- .scope(Endpoint.Scope.global)
- .asList();
- for (var endpoint : endpoints) {
- var endpointObject = endpointArray.addObject();
- endpointObject.setString("cluster", rotation.clusterId().value());
- endpointObject.setBool("tls", true);
- endpointObject.setString("url", endpoint.url().toString());
- endpointObject.setString("scope", endpointScopeString(endpoint.scope()));
- endpointObject.setString("routingMethod", routingMethodString(RoutingMethod.shared));
- }
+ for (var endpoint : controller.routing().endpointsOf(instance).not().legacy()) {
+ // TODO(mpolden): Pass cluster name. Cluster that a global endpoint points to is not available at this level.
+ toSlime(endpoint, "", endpointArray.addObject());
}
}
-
- // serviceUrls contains all valid endpoints for this deployment, including global. The name of these endpoints
- // may contain the cluster name (if non-default). Since the controller has no knowledge of clusters for legacy
- // endpoints, we can't generate these URLs on-the-fly and we have to query the routing layer.
- // TODO(mpolden): Remove this once all clients stop reading this.
+ // TODO(mpolden): Remove this once all clients stop reading it
Cursor serviceUrlArray = response.setArray("serviceUrls");
- controller.routingController().legacyEndpointsOf(deploymentId)
- .forEach(endpoint -> serviceUrlArray.addString(endpoint.toString()));
+ serviceUrls.forEach(url -> serviceUrlArray.addString(url.toString()));
response.setString("nodes", withPath("/zone/v2/" + deploymentId.zoneId().environment() + "/" + deploymentId.zoneId().region() + "/nodes/v2/node/?&recursive=true&application=" + deploymentId.applicationId().tenant() + "." + deploymentId.applicationId().application() + "." + deploymentId.applicationId().instance(), request.getUri()).toString());
response.setString("yamasUrl", monitoringSystemUri(deploymentId).toString());
@@ -1155,12 +1110,6 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
deployment.activity().lastQueriesPerSecond().ifPresent(value -> activity.setDouble("lastQueriesPerSecond", value));
deployment.activity().lastWritesPerSecond().ifPresent(value -> activity.setDouble("lastWritesPerSecond", value));
- // Cost
- // TODO(mpolden): Unused, remove this field and related code.
- DeploymentCost appCost = new DeploymentCost(Map.of());
- Cursor costObject = response.setObject("cost");
- toSlime(appCost, costObject);
-
// Metrics
DeploymentMetrics metrics = deployment.metrics();
Cursor metricsObject = response.setObject("metrics");
@@ -1257,7 +1206,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private void setGlobalEndpointStatus(DeploymentId deployment, boolean inService, HttpRequest request) {
var agent = isOperator(request) ? GlobalRouting.Agent.operator : GlobalRouting.Agent.tenant;
var status = inService ? GlobalRouting.Status.in : GlobalRouting.Status.out;
- controller.routingController().policies().setGlobalRoutingStatus(deployment, status, agent);
+ controller.routing().policies().setGlobalRoutingStatus(deployment, status, agent);
}
/** Set the global rotation status for given deployment. This only applies to global endpoints backed by a rotation */
@@ -1268,7 +1217,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
long timestamp = controller.clock().instant().getEpochSecond();
var status = inService ? EndpointStatus.Status.in : EndpointStatus.Status.out;
var endpointStatus = new EndpointStatus(status, reason, agent.name(), timestamp);
- controller.routingController().setGlobalRotationStatus(deployment, endpointStatus);
+ controller.routing().setGlobalRotationStatus(deployment, endpointStatus);
}
private HttpResponse getGlobalRotationOverride(String tenantName, String applicationName, String instanceName, String environment, String region) {
@@ -1276,9 +1225,9 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
ZoneId.from(environment, region));
Slime slime = new Slime();
Cursor array = slime.setObject().setArray("globalrotationoverride");
- controller.routingController().globalRotationStatus(deploymentId)
+ controller.routing().globalRotationStatus(deploymentId)
.forEach((endpoint, status) -> {
- array.addString(endpoint.upstreamName());
+ array.addString(endpoint.upstreamIdOf(deploymentId));
Cursor statusObject = array.addObject();
statusObject.setString("status", status.getStatus().name());
statusObject.setString("reason", status.getReason() == null ? "" : status.getReason());
@@ -1533,6 +1482,9 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
private HttpResponse jobDeploy(ApplicationId id, JobType type, HttpRequest request) {
+ if ( ! type.environment().isManuallyDeployed() && ! isOperator(request))
+ throw new IllegalArgumentException("Direct deployments are only allowed to manually deployed environments.");
+
Map<String, byte[]> dataParts = parseDataParts(request);
if ( ! dataParts.containsKey("applicationZip"))
throw new IllegalArgumentException("Missing required form part 'applicationZip'");
@@ -1722,7 +1674,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return new SlimeJsonResponse(testConfigSerializer.configSlime(id,
type,
false,
- controller.routingController().zoneEndpointsOf(deployments),
+ controller.routing().zoneEndpointsOf(deployments),
controller.applications().contentClustersByZone(deployments)));
}
@@ -1950,57 +1902,6 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return controller.versionStatus().versions().stream().anyMatch(v -> v.versionNumber().equals(version));
}
- public static void toSlime(DeploymentCost deploymentCost, Cursor object) {
- object.setLong("tco", (long)deploymentCost.getTco());
- object.setLong("waste", (long)deploymentCost.getWaste());
- object.setDouble("utilization", deploymentCost.getUtilization());
- Cursor clustersObject = object.setObject("cluster");
- for (Map.Entry<String, ClusterCost> clusterEntry : deploymentCost.getCluster().entrySet())
- toSlime(clusterEntry.getValue(), clustersObject.setObject(clusterEntry.getKey()));
- }
-
- private static void toSlime(ClusterCost clusterCost, Cursor object) {
- object.setLong("count", clusterCost.getClusterInfo().getHostnames().size());
- object.setString("resource", getResourceName(clusterCost.getResultUtilization()));
- object.setDouble("utilization", clusterCost.getResultUtilization().getMaxUtilization());
- object.setLong("tco", (int)clusterCost.getTco());
- object.setLong("waste", (int)clusterCost.getWaste());
- object.setString("flavor", clusterCost.getClusterInfo().getFlavor());
- object.setDouble("flavorCost", clusterCost.getClusterInfo().getFlavorCost());
- object.setDouble("flavorCpu", clusterCost.getClusterInfo().getFlavorCPU());
- object.setDouble("flavorMem", clusterCost.getClusterInfo().getFlavorMem());
- object.setDouble("flavorDisk", clusterCost.getClusterInfo().getFlavorDisk());
- object.setString("type", clusterCost.getClusterInfo().getClusterType().name());
- Cursor utilObject = object.setObject("util");
- utilObject.setDouble("cpu", clusterCost.getResultUtilization().getCpu());
- utilObject.setDouble("mem", clusterCost.getResultUtilization().getMemory());
- utilObject.setDouble("disk", clusterCost.getResultUtilization().getDisk());
- utilObject.setDouble("diskBusy", clusterCost.getResultUtilization().getDiskBusy());
- Cursor usageObject = object.setObject("usage");
- usageObject.setDouble("cpu", clusterCost.getSystemUtilization().getCpu());
- usageObject.setDouble("mem", clusterCost.getSystemUtilization().getMemory());
- usageObject.setDouble("disk", clusterCost.getSystemUtilization().getDisk());
- usageObject.setDouble("diskBusy", clusterCost.getSystemUtilization().getDiskBusy());
- Cursor hostnamesArray = object.setArray("hostnames");
- for (String hostname : clusterCost.getClusterInfo().getHostnames())
- hostnamesArray.addString(hostname);
- }
-
- private static String getResourceName(ClusterUtilization utilization) {
- String name = "cpu";
- double max = utilization.getMaxUtilization();
-
- if (utilization.getMemory() == max) {
- name = "mem";
- } else if (utilization.getDisk() == max) {
- name = "disk";
- } else if (utilization.getDiskBusy() == max) {
- name = "diskbusy";
- }
-
- return name;
- }
-
private static boolean recurseOverTenants(HttpRequest request) {
return recurseOverApplications(request) || "tenant".equals(request.getProperty("recursive"));
}
@@ -2126,6 +2027,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
switch (method) {
case exclusive: return "exclusive";
case shared: return "shared";
+ case sharedLayer4: return "sharedLayer4";
}
throw new IllegalArgumentException("Unknown routing method " + method);
}
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 0d1dca391bd..4d79b7bb24c 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
@@ -597,7 +597,10 @@ class JobControllerApiHandlerHelper {
.ifPresent(until -> stepObject.setLong("delayedUntil", until.toEpochMilli()));
stepStatus.pausedUntil().ifPresent(until -> stepObject.setLong("pausedUntil", until.toEpochMilli()));
stepStatus.coolingDownUntil(change).ifPresent(until -> stepObject.setLong("coolingDownUntil", until.toEpochMilli()));
- stepStatus.blockedUntil(change).ifPresent(until -> stepObject.setLong("blockedUntil", until.toEpochMilli()));
+ stepStatus.blockedUntil(Change.of(controller.systemVersion())) // Dummy version — just anything with a platform.
+ .ifPresent(until -> stepObject.setLong("platformBlockedUntil", until.toEpochMilli()));
+ application.latestVersion().map(Change::of).flatMap(stepStatus::blockedUntil) // Dummy version — just anything with an application.
+ .ifPresent(until -> stepObject.setLong("applicationBlockedUntil", until.toEpochMilli()));
if (stepStatus.type() == DeploymentStatus.StepType.instance) {
Cursor deployingObject = stepObject.setObject("deploying");
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java
index 26ccecee3e6..ba40f9c2085 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java
@@ -24,7 +24,6 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLoggingRequestHandler;
import com.yahoo.vespa.hosted.controller.routing.GlobalRouting;
-import com.yahoo.vespa.hosted.controller.routing.RoutingPolicy;
import com.yahoo.yolean.Exceptions;
import java.net.URI;
@@ -170,7 +169,7 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
var zone = zoneFrom(path);
if (controller.zoneRegistry().zones().directlyRouted().ids().contains(zone)) {
var status = in ? GlobalRouting.Status.in : GlobalRouting.Status.out;
- controller.routingController().policies().setGlobalRoutingStatus(zone, status);
+ controller.routing().policies().setGlobalRoutingStatus(zone, status);
} else {
controller.serviceRegistry().configServer().setGlobalRotationStatus(zone, in);
}
@@ -188,7 +187,7 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
private void toSlime(ZoneId zone, Cursor zoneObject) {
if (controller.zoneRegistry().zones().directlyRouted().ids().contains(zone)) {
- var zonePolicy = controller.routingController().policies().get(zone);
+ var zonePolicy = controller.routing().policies().get(zone);
zoneStatusToSlime(zoneObject, zonePolicy.zone(), zonePolicy.globalRouting(), RoutingMethod.exclusive);
} else {
// Rotation status per zone only exposes in/out status, no agent or time of change.
@@ -210,11 +209,11 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
var endpointStatus = new EndpointStatus(in ? EndpointStatus.Status.in : EndpointStatus.Status.out, "",
agent.name(),
controller.clock().instant().getEpochSecond());
- controller.routingController().setGlobalRotationStatus(deployment, endpointStatus);
+ controller.routing().setGlobalRotationStatus(deployment, endpointStatus);
}
// Set policy status
- controller.routingController().policies().setGlobalRoutingStatus(deployment, status, agent);
+ controller.routing().policies().setGlobalRoutingStatus(deployment, status, agent);
return new MessageResponse("Set global routing status for " + deployment + " to " + (in ? "IN" : "OUT"));
}
@@ -242,7 +241,7 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
var deploymentId = new DeploymentId(instance.id(), zone);
// Include status from rotation
if (rotationCanRouteTo(zone, instance)) {
- var rotationStatus = controller.routingController().globalRotationStatus(deploymentId);
+ var rotationStatus = controller.routing().globalRotationStatus(deploymentId);
// Status is equal across all global endpoints, as the status is per deployment, not per endpoint.
var endpointStatus = rotationStatus.values().stream().findFirst();
if (endpointStatus.isPresent()) {
@@ -263,9 +262,12 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
}
// Include status from routing policies
- var routingPolicies = controller.routingController().policies().get(deploymentId);
+ var routingPolicies = controller.routing().policies().get(deploymentId);
for (var policy : routingPolicies.values()) {
- deploymentStatusToSlime(deploymentsArray.addObject(), policy);
+ if (!controller.zoneRegistry().routingMethods(policy.id().zone()).contains(RoutingMethod.exclusive)) continue;
+ deploymentStatusToSlime(deploymentsArray.addObject(), new DeploymentId(policy.id().owner(),
+ policy.id().zone()),
+ policy.status().globalRouting(), RoutingMethod.exclusive);
}
}
}
@@ -273,9 +275,11 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
}
- /** Returns whether instance has an assigned rotation and a deployment in given zone */
- private static boolean rotationCanRouteTo(ZoneId zone, Instance instance) {
- return !instance.rotations().isEmpty() && instance.deployments().containsKey(zone);
+ /** Returns whether instance has an assigned rotation that can route to given zone */
+ private boolean rotationCanRouteTo(ZoneId zone, Instance instance) {
+ return !instance.rotations().isEmpty() &&
+ instance.deployments().containsKey(zone) &&
+ controller.zoneRegistry().routingMethods(zone).get(0).isShared();
}
private static void zoneStatusToSlime(Cursor object, ZoneId zone, GlobalRouting globalRouting, RoutingMethod method) {
@@ -297,11 +301,6 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
object.setLong("changedAt", globalRouting.changedAt().toEpochMilli());
}
- private static void deploymentStatusToSlime(Cursor object, RoutingPolicy policy) {
- deploymentStatusToSlime(object, new DeploymentId(policy.id().owner(), policy.id().zone()),
- policy.status().globalRouting(), RoutingMethod.exclusive);
- }
-
private TenantName tenantFrom(Path path) {
return TenantName.from(path.get("tenant"));
}
@@ -355,7 +354,8 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
switch (method) {
case shared: return "shared";
case exclusive: return "exclusive";
- default: return "unknonwn";
+ case sharedLayer4: return "sharedLayer4";
+ default: return "unknown";
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
index 847a6c96a53..66cbf4d17ef 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
@@ -20,7 +20,6 @@ import com.yahoo.slime.SlimeStream;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.LockedTenant;
-import com.yahoo.vespa.hosted.controller.api.integration.ApplicationIdSnapshot;
import com.yahoo.vespa.hosted.controller.api.integration.user.Roles;
import com.yahoo.vespa.hosted.controller.api.integration.user.User;
import com.yahoo.vespa.hosted.controller.api.integration.user.UserId;
@@ -125,7 +124,6 @@ public class UserApiHandler extends LoggingRequestHandler {
User user = getAttribute(request, User.ATTRIBUTE_NAME, User.class);
Set<Role> roles = getAttribute(request, SecurityContext.ATTRIBUTE_NAME, SecurityContext.class).roles();
- ApplicationIdSnapshot snapshot = controller.applicationIdSnapshot();
Map<TenantName, List<TenantRole>> tenantRolesByTenantName = roles.stream()
.flatMap(role -> filterTenantRoles(role).stream())
.distinct()
@@ -156,17 +154,6 @@ public class UserApiHandler extends LoggingRequestHandler {
Cursor tenantRolesObject = tenantObject.setArray("roles");
tenantRolesByTenantName.getOrDefault(tenant, List.of())
.forEach(role -> tenantRolesObject.addString(role.definition().name()));
-
- Cursor tenantApplicationsObject = tenantObject.setObject("applications");
- snapshot.applications(tenant).stream()
- .sorted()
- .forEach(application -> {
- Cursor applicationObject = tenantApplicationsObject.setObject(application.value());
- Cursor applicationInstancesObject = applicationObject.setArray("instances");
- snapshot.instances(tenant, application).stream()
- .sorted()
- .forEach(instance -> applicationInstancesObject.addString(instance.value()));
- });
});
if (!operatorRoles.isEmpty()) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java
index 5543d0ea0b7..8b012b15cd3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java
@@ -48,4 +48,8 @@ public class RoutingId {
return "routing id for " + endpointId + " of " + application;
}
+ public static RoutingId of(ApplicationId application, EndpointId endpoint) {
+ return new RoutingId(application, endpoint);
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java
index 8657a601837..033539f64c4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.routing;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -13,6 +14,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
+import com.yahoo.vespa.hosted.controller.dns.NameServiceForwarder;
import com.yahoo.vespa.hosted.controller.dns.NameServiceQueue.Priority;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
@@ -56,14 +58,9 @@ public class RoutingPolicies {
/** Read all known routing policies for given deployment */
public Map<RoutingPolicyId, RoutingPolicy> get(DeploymentId deployment) {
- return get(deployment.applicationId(), deployment.zoneId());
- }
-
- /** Read all known routing policies for given deployment */
- public Map<RoutingPolicyId, RoutingPolicy> get(ApplicationId application, ZoneId zone) {
- return db.readRoutingPolicies(application).entrySet()
+ return db.readRoutingPolicies(deployment.applicationId()).entrySet()
.stream()
- .filter(kv -> kv.getKey().zone().equals(zone))
+ .filter(kv -> kv.getKey().zone().equals(deployment.zoneId()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
@@ -77,16 +74,15 @@ public class RoutingPolicies {
* load balancers for given application have changed.
*/
public void refresh(ApplicationId application, DeploymentSpec deploymentSpec, ZoneId zone) {
- if (!controller.zoneRegistry().zones().directlyRouted().ids().contains(zone)) return;
var loadBalancers = new AllocatedLoadBalancers(application, zone, controller.serviceRegistry().configServer()
.getLoadBalancers(application, zone),
deploymentSpec);
var inactiveZones = inactiveZones(application, deploymentSpec);
try (var lock = db.lockRoutingPolicies()) {
- if (!application.instance().isTester()) removeGlobalDnsUnreferencedBy(loadBalancers, lock);
+ removeGlobalDnsUnreferencedBy(loadBalancers, lock);
storePoliciesOf(loadBalancers, lock);
removePoliciesUnreferencedBy(loadBalancers, lock);
- if (!application.instance().isTester()) updateGlobalDnsOf(get(loadBalancers.application).values(), inactiveZones, lock);
+ updateGlobalDnsOf(get(loadBalancers.deployment.applicationId()).values(), inactiveZones, lock);
}
}
@@ -127,6 +123,7 @@ public class RoutingPolicies {
var staleTargets = new LinkedHashSet<AliasTarget>();
for (var policy : routeEntry.getValue()) {
if (policy.dnsZone().isEmpty()) continue;
+ if (!controller.zoneRegistry().routingMethods(policy.id().zone()).contains(RoutingMethod.exclusive)) continue;
var target = new AliasTarget(policy.canonicalName(), policy.dnsZone().get(), policy.id().zone());
var zonePolicy = db.readZoneRoutingPolicy(policy.id().zone());
// Remove target zone if global routing status is set out at:
@@ -146,9 +143,10 @@ public class RoutingPolicies {
staleTargets.clear();
}
if (!targets.isEmpty()) {
- var endpoint = RoutingPolicy.globalEndpointOf(routeEntry.getKey().application(),
- routeEntry.getKey().endpointId(), controller.system());
- controller.nameServiceForwarder().createAlias(RecordName.from(endpoint.dnsName()), targets, Priority.normal);
+ var endpoints = controller.routing().endpointsOf(routeEntry.getKey().application())
+ .named(routeEntry.getKey().endpointId())
+ .not().requiresRotation();
+ endpoints.forEach(endpoint -> controller.nameServiceForwarder().createAlias(RecordName.from(endpoint.dnsName()), targets, Priority.normal));
}
staleTargets.forEach(t -> controller.nameServiceForwarder().removeRecords(Record.Type.ALIAS,
RecordData.fqdn(t.name().value()),
@@ -158,9 +156,9 @@ public class RoutingPolicies {
/** Store routing policies for given load balancers */
private void storePoliciesOf(AllocatedLoadBalancers loadBalancers, @SuppressWarnings("unused") Lock lock) {
- var policies = new LinkedHashMap<>(get(loadBalancers.application));
+ var policies = new LinkedHashMap<>(get(loadBalancers.deployment.applicationId()));
for (LoadBalancer loadBalancer : loadBalancers.list) {
- var policyId = new RoutingPolicyId(loadBalancer.application(), loadBalancer.cluster(), loadBalancers.zone);
+ var policyId = new RoutingPolicyId(loadBalancer.application(), loadBalancer.cluster(), loadBalancers.deployment.zoneId());
var existingPolicy = policies.get(policyId);
var newPolicy = new RoutingPolicy(policyId, loadBalancer.hostname(), loadBalancer.dnsZone(),
loadBalancers.endpointIdsOf(loadBalancer),
@@ -172,42 +170,45 @@ public class RoutingPolicies {
updateZoneDnsOf(newPolicy);
policies.put(newPolicy.id(), newPolicy);
}
- db.writeRoutingPolicies(loadBalancers.application, policies);
+ db.writeRoutingPolicies(loadBalancers.deployment.applicationId(), policies);
}
/** Update zone DNS record for given policy */
private void updateZoneDnsOf(RoutingPolicy policy) {
- var name = RecordName.from(policy.endpointIn(controller.system()).dnsName());
+ var name = RecordName.from(policy.endpointIn(controller.system(), RoutingMethod.exclusive).dnsName());
var data = RecordData.fqdn(policy.canonicalName().value());
- controller.nameServiceForwarder().createCname(name, data, Priority.normal);
+ nameUpdaterIn(policy.id().zone()).createCname(name, data);
}
/** Remove policies and zone DNS records unreferenced by given load balancers */
private void removePoliciesUnreferencedBy(AllocatedLoadBalancers loadBalancers, @SuppressWarnings("unused") Lock lock) {
- var policies = get(loadBalancers.application);
+ var policies = get(loadBalancers.deployment.applicationId());
var newPolicies = new LinkedHashMap<>(policies);
var activeLoadBalancers = loadBalancers.list.stream().map(LoadBalancer::hostname).collect(Collectors.toSet());
for (var policy : policies.values()) {
// Leave active load balancers and irrelevant zones alone
if (activeLoadBalancers.contains(policy.canonicalName()) ||
- !policy.id().zone().equals(loadBalancers.zone)) continue;
+ !policy.id().zone().equals(loadBalancers.deployment.zoneId())) continue;
- var dnsName = policy.endpointIn(controller.system()).dnsName();
- controller.nameServiceForwarder().removeRecords(Record.Type.CNAME, RecordName.from(dnsName), Priority.normal);
+ var dnsName = policy.endpointIn(controller.system(), RoutingMethod.exclusive).dnsName();
+ nameUpdaterIn(loadBalancers.deployment.zoneId()).removeRecords(Record.Type.CNAME, RecordName.from(dnsName));
newPolicies.remove(policy.id());
}
- db.writeRoutingPolicies(loadBalancers.application, newPolicies);
+ db.writeRoutingPolicies(loadBalancers.deployment.applicationId(), newPolicies);
}
/** Remove unreferenced global endpoints from DNS */
private void removeGlobalDnsUnreferencedBy(AllocatedLoadBalancers loadBalancers, @SuppressWarnings("unused") Lock lock) {
- var zonePolicies = get(loadBalancers.application, loadBalancers.zone).values();
+ var zonePolicies = get(loadBalancers.deployment).values();
var removalCandidates = new HashSet<>(routingTableFrom(zonePolicies).keySet());
var activeRoutingIds = routingIdsFrom(loadBalancers);
removalCandidates.removeAll(activeRoutingIds);
for (var id : removalCandidates) {
- var endpoint = RoutingPolicy.globalEndpointOf(id.application(), id.endpointId(), controller.system());
- controller.nameServiceForwarder().removeRecords(Record.Type.ALIAS, RecordName.from(endpoint.dnsName()), Priority.normal);
+ var endpoints = controller.routing().endpointsOf(id.application())
+ .not().requiresRotation()
+ .named(id.endpointId());
+ var nameUpdater = nameUpdaterIn(loadBalancers.deployment.zoneId());
+ endpoints.forEach(endpoint -> nameUpdater.removeRecords(Record.Type.ALIAS, RecordName.from(endpoint.dnsName())));
}
}
@@ -258,22 +259,20 @@ public class RoutingPolicies {
/** Load balancers allocated to a deployment */
private static class AllocatedLoadBalancers {
- private final ApplicationId application;
- private final ZoneId zone;
+ private final DeploymentId deployment;
private final List<LoadBalancer> list;
private final DeploymentSpec deploymentSpec;
private AllocatedLoadBalancers(ApplicationId application, ZoneId zone, List<LoadBalancer> loadBalancers,
DeploymentSpec deploymentSpec) {
- this.application = application;
- this.zone = zone;
+ this.deployment = new DeploymentId(application, zone);
this.list = List.copyOf(loadBalancers);
this.deploymentSpec = deploymentSpec;
}
/** Compute all endpoint IDs for given load balancer */
private Set<EndpointId> endpointIdsOf(LoadBalancer loadBalancer) {
- if (!zone.environment().isProduction()) { // Only production deployments have configurable endpoints
+ if (!deployment.zoneId().environment().isProduction()) { // Only production deployments have configurable endpoints
return Set.of();
}
var instanceSpec = deploymentSpec.instance(loadBalancer.application().instance());
@@ -282,7 +281,7 @@ public class RoutingPolicies {
}
return instanceSpec.get().endpoints().stream()
.filter(endpoint -> endpoint.containerId().equals(loadBalancer.cluster().value()))
- .filter(endpoint -> endpoint.regions().contains(zone.region()))
+ .filter(endpoint -> endpoint.regions().contains(deployment.zoneId().region()))
.map(com.yahoo.config.application.api.Endpoint::endpointId)
.map(EndpointId::of)
.collect(Collectors.toSet());
@@ -301,4 +300,53 @@ public class RoutingPolicies {
.collect(Collectors.toUnmodifiableSet());
}
+ /** Returns the name updater to use for given zone */
+ private NameUpdater nameUpdaterIn(ZoneId zone) {
+ if (controller.zoneRegistry().routingMethods(zone).contains(RoutingMethod.exclusive)) {
+ return new NameUpdater(controller.nameServiceForwarder());
+ }
+ return new DiscardingNameUpdater();
+ }
+
+ /** A name updater that passes name service operations to the next handler */
+ private static class NameUpdater {
+
+ private final NameServiceForwarder forwarder;
+
+ public NameUpdater(NameServiceForwarder forwarder) {
+ this.forwarder = forwarder;
+ }
+
+ public void removeRecords(Record.Type type, RecordName name) {
+ forwarder.removeRecords(type, name, Priority.normal);
+ }
+
+ public void createAlias(RecordName name, Set<AliasTarget> targets) {
+ forwarder.createAlias(name, targets, Priority.normal);
+ }
+
+ public void createCname(RecordName name, RecordData data) {
+ forwarder.createCname(name, data, Priority.normal);
+ }
+
+ }
+
+ /** A name updater that does nothing */
+ private static class DiscardingNameUpdater extends NameUpdater {
+
+ private DiscardingNameUpdater() {
+ super(null);
+ }
+
+ @Override
+ public void removeRecords(Record.Type type, RecordName name) {}
+
+ @Override
+ public void createAlias(RecordName name, Set<AliasTarget> target) {}
+
+ @Override
+ public void createCname(RecordName name, RecordData data) {}
+
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java
index 32ec9f359a9..37027e8a8c9 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java
@@ -2,14 +2,12 @@
package com.yahoo.vespa.hosted.controller.routing;
import com.google.common.collect.ImmutableSortedSet;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
-import com.yahoo.vespa.hosted.controller.application.EndpointList;
import java.util.Objects;
import java.util.Optional;
@@ -70,18 +68,14 @@ public class RoutingPolicy {
}
/** Returns the endpoint of this */
- public Endpoint endpointIn(SystemName system) {
- return Endpoint.of(id.owner()).target(id.cluster(), id.zone())
- .routingMethod(RoutingMethod.exclusive)
- .on(Port.tls())
+ public Endpoint endpointIn(SystemName system, RoutingMethod routingMethod) {
+ return Endpoint.of(id.owner())
+ .target(id.cluster(), id.zone())
+ .on(Port.fromRoutingMethod(routingMethod))
+ .routingMethod(routingMethod)
.in(system);
}
- /** Returns global endpoints which this is a member of */
- public EndpointList globalEndpointsIn(SystemName system) {
- return EndpointList.of(endpoints.stream().map(endpointId -> globalEndpointOf(id.owner(), endpointId, system)));
- }
-
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -102,12 +96,4 @@ public class RoutingPolicy {
id.zone().value());
}
- /** Creates a global endpoint for given application */
- public static Endpoint globalEndpointOf(ApplicationId application, EndpointId endpointId, SystemName system) {
- return Endpoint.of(application).named(endpointId)
- .on(Port.tls())
- .routingMethod(RoutingMethod.exclusive)
- .in(system);
- }
-
}
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 b082f66b70a..32213a27a83 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
@@ -6,7 +6,6 @@ import com.yahoo.component.Version;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
-import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.RegionName;
@@ -38,11 +37,9 @@ import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
-import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -176,40 +173,34 @@ public class ControllerTest {
@Test
public void testGlobalRotations() {
- // Setup
- ControllerTester tester = this.tester.controllerTester();
- ZoneId zone = ZoneId.from("prod", "us-west-1");
- ApplicationId app = ApplicationId.from("tenant", "app1", "default");
- DeploymentId deployment = new DeploymentId(app, zone);
- tester.serviceRegistry().routingGeneratorMock().putEndpoints(deployment, List.of(
- new RoutingEndpoint("http://old-endpoint.vespa.yahooapis.com:4080", "host1", false, "upstream2"),
- new RoutingEndpoint("http://qrs-endpoint.vespa.yahooapis.com:4080", "host1", false, "upstream1"),
- new RoutingEndpoint("http://feeding-endpoint.vespa.yahooapis.com:4080", "host2", false, "upstream3"),
- new RoutingEndpoint("http://global-endpoint-2.vespa.yahooapis.com:4080", "host2", true, "upstream4"),
- new RoutingEndpoint("http://global-endpoint.vespa.yahooapis.com:4080", "host1", true, "upstream1"),
- new RoutingEndpoint("http://alias-endpoint.vespa.yahooapis.com:4080", "host1", true, "upstream1")
- ));
-
- Supplier<Map<RoutingEndpoint, EndpointStatus>> globalRotationStatus = () -> tester.controller().routingController().globalRotationStatus(deployment);
- Supplier<List<EndpointStatus>> upstreamOneEndpoints = () -> {
- return globalRotationStatus.get()
- .entrySet().stream()
- .filter(kv -> kv.getKey().upstreamName().equals("upstream1"))
- .map(Map.Entry::getValue)
- .collect(Collectors.toList());
- };
+ var context = tester.newDeploymentContext();
+ var zone1 = ZoneId.from("prod", "us-west-1");
+ var zone2 = ZoneId.from("prod", "us-east-3");
+ var applicationPackage = new ApplicationPackageBuilder()
+ .region(zone1.region())
+ .region(zone2.region())
+ .endpoint("default", "default", zone1.region().value(), zone2.region().value())
+ .build();
+ context.submit(applicationPackage).deploy();
// Check initial rotation status
- assertEquals(3, globalRotationStatus.get().size());
- assertEquals(2, upstreamOneEndpoints.get().size());
- assertTrue("All upstreams are in", upstreamOneEndpoints.get().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.in));
-
- // Set the global rotations out of service
- EndpointStatus status = new EndpointStatus(EndpointStatus.Status.out, "unit-test", "Test", tester.clock().instant().getEpochSecond());
- tester.controller().routingController().setGlobalRotationStatus(deployment, status);
- assertEquals(2, upstreamOneEndpoints.get().size());
- assertTrue("All upstreams are out", upstreamOneEndpoints.get().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.out));
- assertTrue("Reason is set", upstreamOneEndpoints.get().stream().allMatch(es -> es.getReason().equals("unit-test")));
+ var deployment1 = context.deploymentIdIn(zone1);
+ var status1 = tester.controller().routing().globalRotationStatus(deployment1);
+ assertEquals(1, status1.size());
+ assertTrue("All upstreams are in", status1.values().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.in));
+
+ // Set the deployment out of service in the global rotation
+ var newStatus = new EndpointStatus(EndpointStatus.Status.out, "unit-test", ControllerTest.class.getSimpleName(), tester.clock().instant().getEpochSecond());
+ tester.controller().routing().setGlobalRotationStatus(deployment1, newStatus);
+ status1 = tester.controller().routing().globalRotationStatus(deployment1);
+ assertEquals(1, status1.size());
+ assertTrue("All upstreams are out", status1.values().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.out));
+ assertTrue("Reason is set", status1.values().stream().allMatch(es -> es.getReason().equals("unit-test")));
+
+ // Other deployment remains in
+ var status2 = tester.controller().routing().globalRotationStatus(context.deploymentIdIn(zone2));
+ assertEquals(1, status2.size());
+ assertTrue("All upstreams are in", status2.values().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.in));
}
@Test
@@ -517,9 +508,9 @@ public class ControllerTest {
context.submit(applicationPackage);
tester.applications().deleteApplication(context.application().id(),
tester.controllerTester().credentialsFor(context.application().id().tenant()));
- try (RotationLock lock = tester.controller().routingController().rotations().lock()) {
+ try (RotationLock lock = tester.controller().routing().rotations().lock()) {
assertTrue("Rotation is unassigned",
- tester.controller().routingController().rotations().availableRotations(lock)
+ tester.controller().routing().rotations().availableRotations(lock)
.containsKey(new RotationId("rotation-id-01")));
}
context.flushDnsUpdates();
@@ -788,4 +779,28 @@ public class ControllerTest {
assertEquals("Deployed application", 1, context.instance().deployments().size());
}
+ @Test
+ public void testDeployWithRoutingGeneratorEndpoints() {
+ var context = tester.newDeploymentContext();
+ var applicationPackage = new ApplicationPackageBuilder()
+ .upgradePolicy("default")
+ .environment(Environment.prod)
+ .region("us-west-1")
+ .build();
+
+ var zones = Set.of(systemTest.zone(tester.controller().system()),
+ stagingTest.zone(tester.controller().system()),
+ ZoneId.from("prod", "us-west-1"));
+ for (var zone : zones) {
+ tester.controllerTester().serviceRegistry().routingGeneratorMock()
+ .putEndpoints(context.deploymentIdIn(zone),
+ List.of(new RoutingEndpoint("http://legacy-endpoint", "hostname",
+ false, "upstreamName")));
+ }
+ // Defer load balancer provisioning in all environments so that routing controller uses routing generator
+ context.deferLoadBalancerProvisioningIn(zones.stream().map(ZoneId::environment).collect(Collectors.toSet()))
+ .submit(applicationPackage)
+ .deploy();
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterCostTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterCostTest.java
deleted file mode 100644
index 6df2d55d52b..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterCostTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.application;
-
-import com.yahoo.config.provision.ClusterSpec;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author smorgrav
- */
-public class ClusterCostTest {
-
- @Test
- public void clusterCost() {
- List<String> hostnames = new ArrayList<>();
- hostnames.add("host1");
- hostnames.add("host2");
- ClusterInfo info = new ClusterInfo("test", 100, 10, 10, 10, ClusterSpec.Type.container, hostnames);
- ClusterUtilization util = new ClusterUtilization(0.3, 0.2, 0.5, 0.1);
- ClusterCost cost = new ClusterCost(info, util);
-
- // CPU is fully utilized
- Assert.assertEquals(200, cost.getTco(), Double.MIN_VALUE);
- Assert.assertEquals(0, cost.getWaste(), Double.MIN_VALUE);
-
- // Set Disk as the most utilized resource
- util = new ClusterUtilization(0.3, 0.1, 0.5, 0.1);
- cost = new ClusterCost(info, util);
- Assert.assertEquals(200, cost.getTco(), Double.MIN_NORMAL); // TCO is independent of utilization
- Assert.assertEquals(57.1428571429, cost.getWaste(), 0.001); // Waste is not independent
- }
-}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilizationTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilizationTest.java
deleted file mode 100644
index c930978eb1e..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/ClusterUtilizationTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.application;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * @author smorgrav
- */
-public class ClusterUtilizationTest {
-
- private static final double delta = Double.MIN_NORMAL;
-
- @Test
- public void getMaxUtilization() {
- ClusterUtilization resources = new ClusterUtilization(0.3, 0.1, 0.4, 0.5);
- Assert.assertEquals(0.5, resources.getMaxUtilization(), delta);
-
- resources = new ClusterUtilization(0.3, 0.1, 0.4, 0.0);
- Assert.assertEquals(0.4, resources.getMaxUtilization(), delta);
-
- resources = new ClusterUtilization(0.4, 0.3, 0.3, 0.0);
- Assert.assertEquals(0.4, resources.getMaxUtilization(), delta);
-
- resources = new ClusterUtilization(0.1, 0.3, 0.3, 0.0);
- Assert.assertEquals(0.3, resources.getMaxUtilization(), delta);
- }
-
-}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentCostTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentCostTest.java
deleted file mode 100644
index 2e58253d768..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentCostTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.application;
-
-import com.yahoo.config.provision.ClusterSpec;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author smorgrav
- */
-public class DeploymentCostTest {
-
- @Test
- public void deploymentCost() {
- Map<String, ClusterCost> clusters = new HashMap<>();
- clusters.put("cluster1", createClusterCost(100, 0.2));
- clusters.put("cluster2", createClusterCost(50, 0.1));
-
- DeploymentCost cost = new DeploymentCost(clusters);
- Assert.assertEquals(300, cost.getTco(), Double.MIN_VALUE); // 2*100 + 2*50
- Assert.assertEquals(28.5714285714, cost.getWaste(), 0.001); // from cluster2
- Assert.assertEquals(0.7142857142857143, cost.getUtilization(), Double.MIN_VALUE); // from cluster2
- }
-
- private ClusterCost createClusterCost(int flavorCost, double cpuUtil) {
- List<String> hostnames = new ArrayList<>();
- hostnames.add("host1");
- hostnames.add("host2");
- ClusterInfo info = new ClusterInfo("test", flavorCost, 10, 10, 10, ClusterSpec.Type.container, hostnames);
- ClusterUtilization util = new ClusterUtilization(0.3, cpuUtil, 0.5, 0.1);
- return new ClusterCost(info, util);
- }
-} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index 81bbbb479b2..f968b8c76d7 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -6,6 +6,7 @@ import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.application.Endpoint.Port;
import org.junit.Test;
@@ -22,7 +23,7 @@ public class EndpointTest {
private static final ApplicationId app2 = ApplicationId.from("t2", "a2", "i2");
@Test
- public void test_global_endpoints() {
+ public void global_endpoints() {
EndpointId endpointId = EndpointId.defaultId();
Map<String, Endpoint> tests = Map.of(
@@ -70,7 +71,7 @@ public class EndpointTest {
}
@Test
- public void test_global_endpoints_with_endpoint_id() {
+ public void global_endpoints_with_endpoint_id() {
var endpointId = EndpointId.defaultId();
Map<String, Endpoint> tests = Map.of(
@@ -118,7 +119,7 @@ public class EndpointTest {
}
@Test
- public void test_zone_endpoints() {
+ public void zone_endpoints() {
var cluster = ClusterSpec.Id.from("default"); // Always default for non-direct routing
var prodZone = ZoneId.from("prod", "us-north-1");
var testZone = ZoneId.from("test", "us-north-2");
@@ -168,7 +169,7 @@ public class EndpointTest {
}
@Test
- public void test_wildcard_endpoints() {
+ public void wildcard_endpoints() {
var defaultCluster = ClusterSpec.Id.from("default");
var prodZone = ZoneId.from("prod", "us-north-1");
var testZone = ZoneId.from("test", "us-north-2");
@@ -225,4 +226,30 @@ public class EndpointTest {
tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
}
+
+ @Test
+ public void upstream_name() {
+ var zone = ZoneId.from("prod", "us-north-1");
+ var tests1 = Map.of(
+ // With default cluster
+ "a1.t1.us-north-1.prod",
+ Endpoint.of(app1).named(EndpointId.defaultId()).on(Port.tls(4443)).in(SystemName.main),
+
+ // With non-default cluster
+ "c1.a1.t1.us-north-1.prod",
+ Endpoint.of(app1).named(EndpointId.of("c1")).on(Port.tls(4443)).in(SystemName.main)
+ );
+ var tests2 = Map.of(
+ // With non-default instance
+ "i2.a2.t2.us-north-1.prod",
+ Endpoint.of(app2).named(EndpointId.defaultId()).on(Port.tls(4443)).in(SystemName.main),
+
+ // With non-default instance and cluster
+ "c2.i2.a2.t2.us-north-1.prod",
+ Endpoint.of(app2).named(EndpointId.of("c2")).on(Port.tls(4443)).in(SystemName.main)
+ );
+ tests1.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(new DeploymentId(app1, zone))));
+ tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(new DeploymentId(app2, zone))));
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java
index f9fed820a5b..d7bc73adf37 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java
@@ -112,7 +112,7 @@ public class EndpointCertificateManagerTest {
@Test
public void reprovisions_certificate_when_necessary() {
- mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, "uuid", List.of()));
+ mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, Optional.of("uuid"), Optional.of(List.of()), Optional.empty()));
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate)+X509CertificateUtils.toPem(testCertificate), 0);
Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
index 3e1f468691c..5eae68adf5a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
@@ -8,7 +8,6 @@ import com.yahoo.config.provision.AthenzService;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
@@ -19,7 +18,6 @@ import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
-import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
@@ -27,8 +25,6 @@ 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.deployment.TesterCloud;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId;
-import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingEndpoint;
-import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGeneratorMock;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
@@ -49,7 +45,6 @@ import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
-import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -102,10 +97,8 @@ public class DeploymentContext {
private final ApplicationId instanceId;
private final TesterId testerId;
private final JobController jobs;
- private final RoutingGeneratorMock routing;
private final JobRunner runner;
private final DeploymentTester tester;
- private final Set<Environment> deferLoadBalancerProvisioning = new HashSet<>();
private ApplicationVersion lastSubmission = null;
private boolean deferDnsUpdates = false;
@@ -118,7 +111,6 @@ public class DeploymentContext {
this.jobs = tester.controller().jobController();
this.runner = tester.runner();
this.tester = tester;
- this.routing = tester.controllerTester().serviceRegistry().routingGeneratorMock();
createTenantAndApplication();
}
@@ -197,15 +189,15 @@ public class DeploymentContext {
return this;
}
- /**
- * Defer provisioning of load balancers in zones in given environment. Default behaviour is to automatically
- * provision load balancers in all zones that support direct routing.
- */
+ /** Defer provisioning of load balancers in zones in given environment */
public DeploymentContext deferLoadBalancerProvisioningIn(Environment... environment) {
- deferLoadBalancerProvisioning.addAll(List.of(environment));
- return this;
+ return deferLoadBalancerProvisioningIn(Set.of(environment));
}
+ public DeploymentContext deferLoadBalancerProvisioningIn(Set<Environment> environments) {
+ configServer().deferLoadBalancerProvisioningIn(environments);
+ return this;
+ }
/** Defer DNS updates */
public DeploymentContext deferDnsUpdates() {
@@ -229,25 +221,16 @@ public class DeploymentContext {
return this;
}
- /** Add a routing policy for this in given zone, with status set to active */
- public DeploymentContext addRoutingPolicy(ZoneId zone, boolean active) {
- return addRoutingPolicy(instanceId, zone, active);
- }
-
- /** Add a routing policy for tester instance of this in given zone, with status set to active */
- public DeploymentContext addTesterRoutingPolicy(ZoneId zone, boolean active) {
- return addRoutingPolicy(testerId.id(), zone, active);
- }
-
- private DeploymentContext addRoutingPolicy(ApplicationId instance, ZoneId zone, boolean active) {
- var clusterId = "default" + (!active ? "-inactive" : "");
- var id = new RoutingPolicyId(instance, ClusterSpec.Id.from(clusterId), zone);
- var policies = new LinkedHashMap<>(tester.controller().curator().readRoutingPolicies(instance));
+ /** Add a routing policy for this in given zone, with status set to inactive */
+ public DeploymentContext addInactiveRoutingPolicy(ZoneId zone) {
+ var clusterId = "default-inactive";
+ var id = new RoutingPolicyId(instanceId, ClusterSpec.Id.from(clusterId), zone);
+ var policies = new LinkedHashMap<>(tester.controller().curator().readRoutingPolicies(instanceId));
policies.put(id, new RoutingPolicy(id, HostName.from("lb-host"),
Optional.empty(),
- Set.of(EndpointId.of("c0")),
- new Status(active, GlobalRouting.DEFAULT_STATUS)));
- tester.controller().curator().writeRoutingPolicies(instance, policies);
+ Set.of(EndpointId.of("default")),
+ new Status(false, GlobalRouting.DEFAULT_STATUS)));
+ tester.controller().curator().writeRoutingPolicies(instanceId, policies);
return this;
}
@@ -303,7 +286,6 @@ public class DeploymentContext {
runner.advance(currentRun(job));
assertTrue(jobs.run(id).get().hasFailed());
assertTrue(jobs.run(id).get().hasEnded());
- doTeardown(job);
return this;
}
@@ -357,7 +339,6 @@ public class DeploymentContext {
}
if (job.type().isTest())
doTests(job);
- doTeardown(job);
return this;
}
@@ -390,7 +371,6 @@ public class DeploymentContext {
runner.advance(currentRun(job));
assertTrue(jobs.run(id).get().hasFailed());
assertTrue(jobs.run(id).get().hasEnded());
- doTeardown(job);
return this;
}
@@ -405,7 +385,6 @@ public class DeploymentContext {
runner.advance(currentRun(job));
assertTrue(jobs.run(id).get().hasFailed());
assertTrue(jobs.run(id).get().hasEnded());
- doTeardown(job);
return this;
}
@@ -429,11 +408,11 @@ public class DeploymentContext {
/** Start tests in system test stage */
public RunId startSystemTestTests() {
- RunId id = newRun(JobType.systemTest);
+ var id = newRun(JobType.systemTest);
+ var testZone = JobType.systemTest.zone(tester.controller().system());
runner.run();
- configServer().convergeServices(instanceId, JobType.systemTest.zone(tester.controller().system()));
- configServer().convergeServices(testerId.id(), JobType.systemTest.zone(tester.controller().system()));
- setEndpoints(JobType.systemTest.zone(tester.controller().system()));
+ configServer().convergeServices(instanceId, testZone);
+ configServer().convergeServices(testerId.id(), testZone);
runner.run();
assertEquals(unfinished, jobs.run(id).get().stepStatuses().get(Step.endTests));
assertTrue(jobs.run(id).get().steps().get(Step.endTests).startTime().isPresent());
@@ -456,16 +435,6 @@ public class DeploymentContext {
ZoneId zone = zone(job);
DeploymentId deployment = new DeploymentId(job.application(), zone);
- // Provision load balancers in directly routed zones, unless explicitly deferred
- if (provisionLoadBalancerIn(zone)) {
- configServer().putLoadBalancers(zone, List.of(new LoadBalancer(deployment.toString(),
- deployment.applicationId(),
- ClusterSpec.Id.from("default"),
- HostName.from("lb-0--" + instanceId.serializedForm() + "--" + zone.toString()),
- LoadBalancer.State.active,
- Optional.of("dns-zone-1"))));
- }
-
// First step is always a deployment.
runner.advance(currentRun(job));
@@ -477,7 +446,6 @@ public class DeploymentContext {
Versions versions = currentRun(job).versions();
tester.configServer().nodeRepository().doUpgrade(deployment, Optional.empty(), versions.sourcePlatform().orElse(versions.targetPlatform()));
configServer().convergeServices(id.application(), zone);
- setEndpoints(zone);
runner.advance(currentRun(job));
assertEquals(Step.Status.succeeded, jobs.run(id).get().stepStatuses().get(Step.installInitialReal));
@@ -514,27 +482,6 @@ public class DeploymentContext {
return run;
}
- /** Sets a single endpoint in the routing layer */
- DeploymentContext setEndpoints(ZoneId zone) {
- if (!supportsRoutingMethod(RoutingMethod.shared, zone)) return this;
- var id = instanceId;
- routing.putEndpoints(new DeploymentId(id, zone),
- Collections.singletonList(new RoutingEndpoint(String.format("https://%s--%s--%s.%s.%s.vespa:43",
- id.instance().value(),
- id.application().value(),
- id.tenant().value(),
- zone.region().value(),
- zone.environment().value()),
- "host1",
- true,
- String.format("cluster1.%s.%s.%s.%s",
- id.application().value(),
- id.tenant().value(),
- zone.region().value(),
- zone.environment().value()))));
- return this;
- }
-
/** Lets nodes converge on new application version. */
private void doConverge(JobId job) {
RunId id = currentRun(job).id();
@@ -542,14 +489,13 @@ public class DeploymentContext {
assertEquals(unfinished, jobs.run(id).get().stepStatuses().get(Step.installReal));
configServer().convergeServices(id.application(), zone);
- setEndpoints(zone);
runner.advance(currentRun(job));
if (job.type().environment().isManuallyDeployed()) {
assertEquals(Step.Status.succeeded, jobs.run(id).get().stepStatuses().get(Step.installReal));
assertTrue(jobs.run(id).get().hasEnded());
return;
}
- assertEquals(Step.Status.succeeded, jobs.run(id).get().stepStatuses().get(Step.installReal));
+ assertEquals("Status of " + id, Step.Status.succeeded, jobs.run(id).get().stepStatuses().get(Step.installReal));
}
/** Installs tester and starts tests. */
@@ -587,26 +533,6 @@ public class DeploymentContext {
assertTrue(configServer().nodeRepository().list(zone, TesterId.of(id.application()).id()).isEmpty());
}
- /** Removes endpoints from routing layer — always call this. */
- private void doTeardown(JobId job) {
- ZoneId zone = zone(job);
- DeploymentId deployment = new DeploymentId(job.application(), zone);
-
- if ( ! instance().deployments().containsKey(zone))
- routing.removeEndpoints(deployment);
- routing.removeEndpoints(new DeploymentId(TesterId.of(job.application()).id(), zone));
- }
-
- /** Returns whether a load balancer is expected to be provisioned in given zone */
- private boolean provisionLoadBalancerIn(ZoneId zone) {
- return !deferLoadBalancerProvisioning.contains(zone.environment()) &&
- supportsRoutingMethod(RoutingMethod.exclusive, zone);
- }
-
- private boolean supportsRoutingMethod(RoutingMethod method, ZoneId zone) {
- return tester.controller().zoneRegistry().zones().routingMethod(method).ids().contains(zone);
- }
-
private JobId jobId(JobType type) {
return new JobId(instanceId, type);
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java
index 938b801b88d..7114edcc44e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.log.LogLevel;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.controller.Application;
@@ -10,9 +9,7 @@ import com.yahoo.vespa.hosted.controller.ApplicationController;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.Instance;
-import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzDbMock;
-import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGeneratorMock;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockTesterCloud;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock;
@@ -28,7 +25,6 @@ import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.TemporalAdjusters;
-import java.util.Collections;
import java.util.logging.Logger;
import static org.junit.Assert.assertTrue;
@@ -49,7 +45,6 @@ public class DeploymentTester {
private final ControllerTester tester;
private final JobController jobs;
- private final RoutingGeneratorMock routing;
private final MockTesterCloud cloud;
private final JobRunner runner;
private final Upgrader upgrader;
@@ -57,7 +52,6 @@ public class DeploymentTester {
private final OutstandingChangeDeployer outstandingChangeDeployer;
public JobController jobs() { return jobs; }
- public RoutingGeneratorMock routing() { return routing; }
public MockTesterCloud cloud() { return cloud; }
public JobRunner runner() { return runner; }
public ConfigServerMock configServer() { return tester.configServer(); }
@@ -79,7 +73,6 @@ public class DeploymentTester {
public DeploymentTester(ControllerTester controllerTester) {
tester = controllerTester;
jobs = tester.controller().jobController();
- routing = tester.serviceRegistry().routingGeneratorMock();
cloud = (MockTesterCloud) tester.controller().jobController().cloud();
var jobControl = new JobControl(tester.controller().curator());
runner = new JobRunner(tester.controller(), Duration.ofDays(1), jobControl,
@@ -88,7 +81,6 @@ public class DeploymentTester {
upgrader.setUpgradesPerMinute(1); // Anything that makes it at least one for any maintenance period is fine.
readyJobsTrigger = new ReadyJobsTrigger(tester.controller(), maintenanceInterval, jobControl);
outstandingChangeDeployer = new OutstandingChangeDeployer(tester.controller(), maintenanceInterval, jobControl);
- routing.putEndpoints(new DeploymentId(null, null), Collections.emptyList()); // Turn off default behaviour for the mock.
// Get deployment job logs to stderr.
Logger.getLogger("").setLevel(LogLevel.DEBUG);
@@ -138,13 +130,6 @@ public class DeploymentTester {
return newDeploymentContext(tenantName, applicationName, instanceName).application();
}
- /**
- * Sets a single endpoint in the routing mock; this matches that required for the tester.
- */
- public void setEndpoints(ApplicationId id, ZoneId zone) {
- newDeploymentContext(id).setEndpoints(zone);
- }
-
/** Aborts and finishes all running jobs. */
public void abortAll() {
triggerJobs();
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 a1c8ccffaf0..e684d479158 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
@@ -3,13 +3,19 @@ package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.component.Version;
import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
+import com.yahoo.vespa.hosted.controller.application.SystemApplication;
+import com.yahoo.vespa.hosted.controller.integration.ServiceRegistryMock;
+import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
import org.junit.Test;
@@ -22,11 +28,14 @@ import java.util.Optional;
import java.util.OptionalLong;
import java.util.stream.Collectors;
+import static com.yahoo.config.provision.SystemName.cd;
import static com.yahoo.config.provision.SystemName.main;
import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionApNortheast1;
import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionApNortheast2;
import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionApSoutheast1;
import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionAwsUsEast1a;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionCdAwsUsEast1a;
+import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionCdUsCentral1;
import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionEuWest1;
import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsCentral1;
import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsEast3;
@@ -57,7 +66,7 @@ import static org.junit.Assert.assertTrue;
*/
public class DeploymentTriggerTest {
- private final DeploymentTester tester = new DeploymentTester();
+ private DeploymentTester tester = new DeploymentTester();
@Test
public void testTriggerFailing() {
@@ -1066,4 +1075,58 @@ public class DeploymentTriggerTest {
assertEquals(Change.of(app.lastSubmission().get()), app.instance().change());
}
+ @Test
+ public void mixedDirectAndPipelineJobsInProduction() {
+ ApplicationPackage cdPackage = new ApplicationPackageBuilder().region("cd-us-central-1")
+ .region("cd-aws-us-east-1a")
+ .build();
+ ServiceRegistryMock services = new ServiceRegistryMock();
+ var zones = List.of(ZoneApiMock.fromId("test.cd-us-central-1"),
+ ZoneApiMock.fromId("staging.cd-us-central-1"),
+ ZoneApiMock.fromId("prod.cd-us-central-1"),
+ ZoneApiMock.fromId("prod.cd-aws-us-east-1a"));
+ services.zoneRegistry()
+ .setSystemName(SystemName.cd)
+ .setZones(zones)
+ .setRoutingMethod(zones, RoutingMethod.exclusive);
+ tester = new DeploymentTester(new ControllerTester(services));
+ tester.configServer().bootstrap(services.zoneRegistry().zones().all().ids(), SystemApplication.values());
+ tester.controllerTester().upgradeSystem(Version.fromString("6.1"));
+ tester.controllerTester().computeVersionStatus();
+ var app = tester.newDeploymentContext();
+
+ app.runJob(productionCdUsCentral1, cdPackage);
+ app.submit(cdPackage);
+ app.runJob(systemTest);
+ // Staging test requires unknown initial version, and is broken.
+ tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionCdUsCentral1, "user", false);
+ app.runJob(productionCdUsCentral1)
+ .abortJob(stagingTest) // Complete failing run.
+ .runJob(stagingTest)
+ .runJob(productionCdAwsUsEast1a);
+
+ app.runJob(productionCdUsCentral1, cdPackage);
+ var version = new Version("7.1");
+ tester.controllerTester().upgradeSystem(version);
+ tester.upgrader().maintain();
+ // System and staging tests both require unknown versions, and are broken.
+ tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionCdUsCentral1, "user", false);
+ app.runJob(productionCdUsCentral1)
+ .abortJob(systemTest)
+ .abortJob(stagingTest)
+ .runJob(systemTest)
+ .runJob(stagingTest)
+ .runJob(productionCdAwsUsEast1a);
+
+ app.runJob(productionCdUsCentral1, cdPackage);
+ app.submit(cdPackage);
+ app.runJob(systemTest);
+ // Staging test requires unknown initial version, and is broken.
+ tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionCdUsCentral1, "user", false);
+ app.runJob(productionCdUsCentral1)
+ .jobAborted(stagingTest)
+ .runJob(stagingTest)
+ .runJob(productionCdAwsUsEast1a);
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
index db07aff34e5..a57e302d939 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
@@ -9,7 +9,6 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
-import com.yahoo.slime.ArrayTraverser;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions;
@@ -170,9 +169,15 @@ public class InternalStepRunnerTest {
public void waitsForEndpointsAndTimesOut() {
app.newRun(JobType.systemTest);
- // Tester fails to show up for staging tests, and the real deployment for system tests.
- tester.setEndpoints(app.testerId().id(), JobType.systemTest.zone(system()));
- tester.setEndpoints(app.instanceId(), JobType.stagingTest.zone(system()));
+ // Tester endpoint fails to show up for staging tests, and the real deployment for system tests.
+ var testZone = JobType.systemTest.zone(system());
+ var stagingZone = JobType.stagingTest.zone(system());
+ tester.newDeploymentContext(app.testerId().id())
+ .deferLoadBalancerProvisioningIn(testZone.environment());
+ tester.newDeploymentContext(app.instanceId())
+ .deferLoadBalancerProvisioningIn(stagingZone.environment());
+ tester.controllerTester().serviceRegistry().routingGeneratorMock().putEndpoints(new DeploymentId(app.testerId().id(), testZone), List.of());
+ tester.controllerTester().serviceRegistry().routingGeneratorMock().putEndpoints(new DeploymentId(app.instanceId(), stagingZone), List.of());
tester.runner().run();
tester.configServer().convergeServices(app.instanceId(), JobType.stagingTest.zone(system()));
@@ -197,14 +202,10 @@ public class InternalStepRunnerTest {
// Node is down too long in system test, and no nodes go down in staging.
tester.runner().run();
- tester.setEndpoints(app.testerId().id(), JobType.systemTest.zone(system()));
tester.configServer().setVersion(app.testerId().id(), JobType.systemTest.zone(system()), tester.controller().systemVersion());
tester.configServer().convergeServices(app.testerId().id(), JobType.systemTest.zone(system()));
- tester.setEndpoints(app.instanceId(), JobType.systemTest.zone(system()));
- tester.setEndpoints(app.testerId().id(), JobType.stagingTest.zone(system()));
tester.configServer().setVersion(app.testerId().id(), JobType.stagingTest.zone(system()), tester.controller().systemVersion());
tester.configServer().convergeServices(app.testerId().id(), JobType.stagingTest.zone(system()));
- tester.setEndpoints(app.instanceId(), JobType.stagingTest.zone(system()));
tester.runner().run();
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installTester));
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.stagingTest).get().stepStatuses().get(Step.installTester));
@@ -240,13 +241,11 @@ public class InternalStepRunnerTest {
app.newRun(JobType.systemTest);
tester.runner().run();
tester.configServer().convergeServices(app.instanceId(), JobType.systemTest.zone(system()));
- tester.setEndpoints(app.instanceId(), JobType.systemTest.zone(system()));
tester.runner().run();
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal));
assertEquals(unfinished, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installTester));
tester.applications().deactivate(app.instanceId(), JobType.systemTest.zone(system()));
- tester.setEndpoints(app.testerId().id(), JobType.systemTest.zone(system()));
tester.configServer().convergeServices(app.testerId().id(), JobType.systemTest.zone(system()));
tester.runner().run();
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installTester));
@@ -266,8 +265,6 @@ public class InternalStepRunnerTest {
tester.configServer().convergeServices(app.testerId().id(), JobType.systemTest.zone(system()));
assertEquals(unfinished, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal));
assertEquals(unfinished, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installTester));
- app.addRoutingPolicy(JobType.systemTest.zone(system()), true);
- app.addTesterRoutingPolicy(JobType.systemTest.zone(system()), true);
tester.runner().run();;
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal));
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installTester));
@@ -314,13 +311,13 @@ public class InternalStepRunnerTest {
RunId id = app.startSystemTestTests();
tester.runner().run();
assertEquals(unfinished, tester.jobs().run(id).get().stepStatuses().get(Step.endTests));
+ var testZone = JobType.systemTest.zone(system());
Inspector configObject = SlimeUtils.jsonToSlime(tester.cloud().config()).get();
assertEquals(app.instanceId().serializedForm(), configObject.field("application").asString());
- assertEquals(JobType.systemTest.zone(system()).value(), configObject.field("zone").asString());
+ assertEquals(testZone.value(), configObject.field("zone").asString());
assertEquals(system().value(), configObject.field("system").asString());
assertEquals(1, configObject.field("endpoints").children());
- assertEquals(1, configObject.field("endpoints").field(JobType.systemTest.zone(system()).value()).entries());
- configObject.field("endpoints").field(JobType.systemTest.zone(system()).value()).traverse((ArrayTraverser) (__, endpoint) -> assertEquals(tester.routing().endpoints(new DeploymentId(instanceId, JobType.systemTest.zone(system()))).get(0).endpoint(), endpoint.asString()));
+ assertEquals(1, configObject.field("endpoints").field(testZone.value()).entries());
long lastId = tester.jobs().details(id).get().lastId().getAsLong();
tester.cloud().add(new LogEntry(0, Instant.ofEpochMilli(123), info, "Ready!"));
@@ -366,7 +363,6 @@ public class InternalStepRunnerTest {
tester.runner().run(); // Job run order determined by JobType enum order per application.
tester.configServer().convergeServices(app.instanceId(), zone);
- tester.setEndpoints(app.instanceId(), zone);
assertEquals(unfinished, tester.jobs().run(id).get().stepStatuses().get(Step.installReal));
assertEquals(applicationPackage.hash(), tester.configServer().application(app.instanceId(), zone).get().applicationPackage().hash());
assertEquals(otherPackage.hash(), tester.configServer().application(app.instanceId(), JobType.perfUsEast3.zone(system())).get().applicationPackage().hash());
@@ -375,12 +371,6 @@ public class InternalStepRunnerTest {
tester.runner().run();
assertEquals(1, tester.jobs().active().size());
assertEquals(version, tester.instance(app.instanceId()).deployments().get(zone).version());
-
- try {
- tester.jobs().deploy(app.instanceId(), JobType.productionApNortheast1, Optional.empty(), applicationPackage);
- fail("Deployments outside dev should not be allowed.");
- }
- catch (IllegalArgumentException expected) { }
}
@Test
@@ -422,13 +412,13 @@ public class InternalStepRunnerTest {
@Test
public void certificateTimeoutAbortsJob() {
- tester.controllerTester().zoneRegistry().setSystemName(SystemName.PublicCd);
+ tester.controllerTester().zoneRegistry().setSystemName(SystemName.Public);
var zones = List.of(ZoneApiMock.fromId("test.aws-us-east-1c"),
- ZoneApiMock.fromId("staging.aws-us-east-1c"),
- ZoneApiMock.fromId("prod.aws-us-east-1c"));
+ ZoneApiMock.fromId("staging.aws-us-east-1c"),
+ ZoneApiMock.fromId("prod.aws-us-east-1c"));
tester.controllerTester().zoneRegistry()
.setZones(zones)
- .setRoutingMethod(zones, RoutingMethod.shared);
+ .setRoutingMethod(zones, RoutingMethod.exclusive);
tester.configServer().bootstrap(tester.controllerTester().zoneRegistry().zones().all().ids(), SystemApplication.values());
RunId id = app.startSystemTestTests();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
index f31038a7aba..05330d306fe 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
@@ -29,8 +29,8 @@ public class TestConfigSerializerTest {
byte[] json = new TestConfigSerializer(SystemName.PublicCd).configJson(instanceId,
JobType.systemTest,
true,
- Map.of(zone, Map.of(ClusterSpec.Id.from("ai"),
- URI.create("https://server/"))),
+ Map.of(zone, Map.of(URI.create("https://server/"),
+ ClusterSpec.Id.from("ai"))),
Map.of(zone, List.of("facts")));
byte[] expected = Files.readAllBytes(Paths.get("src/test/resources/testConfig.json"));
assertEquals(new String(SlimeUtils.toJsonBytes(SlimeUtils.jsonToSlime(expected))),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
index 5d93881129f..204e0a1c1b8 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
@@ -5,13 +5,15 @@ import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.flags.json.FlagData;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
-import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeploymentData;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
@@ -19,7 +21,6 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname;
import com.yahoo.vespa.hosted.controller.api.identifiers.Identifier;
import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId;
import com.yahoo.vespa.hosted.controller.api.integration.LogEntry;
-import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
@@ -50,6 +51,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -72,6 +74,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
private final Version initialVersion = new Version(6, 1, 0);
private final Set<DeploymentId> suspendedApplications = new HashSet<>();
private final Map<ZoneId, Set<LoadBalancer>> loadBalancers = new HashMap<>();
+ private final Set<Environment> deferLoadBalancerProvisioning = new HashSet<>();
private final Map<DeploymentId, List<Log>> warnings = new HashMap<>();
private final Map<DeploymentId, Set<String>> rotationNames = new HashMap<>();
private final Map<DeploymentId, List<ClusterMetrics>> clusterMetrics = new HashMap<>();
@@ -245,6 +248,10 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
this.clusterMetrics.put(deployment, clusterMetrics);
}
+ public void deferLoadBalancerProvisioningIn(Set<Environment> environments) {
+ deferLoadBalancerProvisioning.addAll(environments);
+ }
+
@Override
public NodeRepositoryMock nodeRepository() {
return nodeRepository;
@@ -309,43 +316,51 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
}
@Override
- public PreparedApplication deploy(DeploymentId deployment, DeployOptions deployOptions,
- Set<ContainerEndpoint> containerEndpoints,
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata, byte[] content) {
- lastPrepareVersion = deployOptions.vespaVersion.map(Version::fromString).orElse(null);
+ public PreparedApplication deploy(DeploymentData deployment) {
+ lastPrepareVersion = deployment.platform();
if (prepareException != null) {
RuntimeException prepareException = this.prepareException;
this.prepareException = null;
throw prepareException;
}
- applications.put(deployment, new Application(deployment.applicationId(), lastPrepareVersion, new ApplicationPackage(content)));
+ DeploymentId id = new DeploymentId(deployment.instance(), deployment.zone());
+ applications.put(id, new Application(id.applicationId(), lastPrepareVersion, new ApplicationPackage(deployment.applicationPackage())));
- if (nodeRepository().list(deployment.zoneId(), deployment.applicationId()).isEmpty())
- provision(deployment.zoneId(), deployment.applicationId());
+ if (nodeRepository().list(id.zoneId(), id.applicationId()).isEmpty())
+ provision(id.zoneId(), id.applicationId());
this.rotationNames.put(
- deployment,
- containerEndpoints.stream()
- .map(ContainerEndpoint::names)
- .flatMap(Collection::stream)
- .collect(Collectors.toSet())
+ id,
+ deployment.containerEndpoints().stream()
+ .map(ContainerEndpoint::names)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet())
);
+ if (!deferLoadBalancerProvisioning.contains(id.zoneId().environment())) {
+ putLoadBalancers(id.zoneId(), List.of(new LoadBalancer(UUID.randomUUID().toString(),
+ id.applicationId(),
+ ClusterSpec.Id.from("default"),
+ HostName.from("lb-0--" + id.applicationId().serializedForm() + "--" + id.zoneId().toString()),
+ LoadBalancer.State.active,
+ Optional.of("dns-zone-1"))));
+ }
+
return () -> {
- Application application = applications.get(deployment);
+ Application application = applications.get(id);
application.activate();
- List<Node> nodes = nodeRepository.list(deployment.zoneId(), deployment.applicationId());
+ List<Node> nodes = nodeRepository.list(id.zoneId(), id.applicationId());
for (Node node : nodes) {
- nodeRepository.putByHostname(deployment.zoneId(), new Node.Builder(node)
+ nodeRepository.putByHostname(id.zoneId(), new Node.Builder(node)
.state(Node.State.active)
.wantedVersion(application.version().get())
.build());
}
- serviceStatus.put(deployment, new ServiceConvergence(deployment.applicationId(),
- deployment.zoneId(),
- false,
- 2,
- nodes.stream()
+ serviceStatus.put(id, new ServiceConvergence(id.applicationId(),
+ id.zoneId(),
+ false,
+ 2,
+ nodes.stream()
.map(node -> new ServiceConvergence.Status(node.hostname(),
43,
"container",
@@ -360,7 +375,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
Collections.emptyList());
setConfigChangeActions(null);
prepareResponse.tenant = new TenantId("tenant");
- prepareResponse.log = warnings.getOrDefault(deployment, Collections.emptyList());
+ prepareResponse.log = warnings.getOrDefault(id, Collections.emptyList());
return prepareResponse;
};
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index 323b86be1d3..cbb93a51bfd 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -64,7 +64,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
public ServiceRegistryMock(SystemName system) {
this.zoneRegistryMock = new ZoneRegistryMock(system);
this.configServerMock = new ConfigServerMock(zoneRegistryMock);
- this.routingGeneratorMock = new RoutingGeneratorMock(RoutingGeneratorMock.TEST_ENDPOINTS, zoneRegistryMock);
+ this.routingGeneratorMock = new RoutingGeneratorMock();
}
@Inject
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
index 2092c5ec9a3..efc875b06f5 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
@@ -116,6 +116,9 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry
}
public ZoneRegistryMock setRoutingMethod(ZoneApi zone, List<RoutingMethod> routingMethods) {
+ if (routingMethods.stream().distinct().count() != routingMethods.size()) {
+ throw new IllegalArgumentException("Routing methods must be distinct");
+ }
this.zoneRoutingMethods.put(zone, List.copyOf(routingMethods));
return this;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java
deleted file mode 100644
index ff72a2f7231..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainerTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.maintenance;
-
-import com.yahoo.component.Version;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.NodeResources;
-import com.yahoo.config.provision.NodeType;
-import com.yahoo.config.provision.zone.ZoneId;
-import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
-import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
-import org.junit.Test;
-
-import java.time.Duration;
-import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * @author smorgrav
- */
-public class ClusterInfoMaintainerTest {
-
- private final ControllerTester tester = new ControllerTester();
-
- @Test
- public void maintain() {
- tester.createTenant("tenant1", "domain123", 321L);
- ApplicationId app = tester.createApplication("tenant1", "app1", "default").id().defaultInstance();
- ZoneId zone = ZoneId.from("dev", "us-east-1");
- tester.deploy(app, zone);
-
- // Precondition: no cluster info attached to the deployments
- Deployment deployment = tester.controller().applications().getInstance(app).get().deployments().values().stream()
- .findFirst()
- .get();
- assertEquals(0, deployment.clusterInfo().size());
-
- addNodes(zone);
- ClusterInfoMaintainer maintainer = new ClusterInfoMaintainer(tester.controller(), Duration.ofHours(1),
- new JobControl(new MockCuratorDb()));
- maintainer.maintain();
-
- deployment = tester.controller().applications().getInstance(app).get().deployments().values().stream()
- .findFirst()
- .get();
- assertEquals(2, deployment.clusterInfo().size());
- assertEquals(10, deployment.clusterInfo().get(ClusterSpec.Id.from("clusterA")).getFlavorCost());
- }
-
- private void addNodes(ZoneId zone) {
- var nodeA = new Node.Builder()
- .hostname(HostName.from("hostA"))
- .parentHostname(HostName.from("parentHostA"))
- .state(Node.State.active)
- .type(NodeType.tenant)
- .owner(ApplicationId.from("tenant1", "app1", "default"))
- .currentVersion(Version.fromString("7.42"))
- .wantedVersion(Version.fromString("7.42"))
- .currentOsVersion(Version.fromString("7.6"))
- .wantedOsVersion(Version.fromString("7.6"))
- .serviceState(Node.ServiceState.expectedUp)
- .resources(new NodeResources(1, 1, 1, 1))
- .cost(10)
- .clusterId("clusterA")
- .clusterType(Node.ClusterType.container)
- .build();
- var nodeB = new Node.Builder()
- .hostname(HostName.from("hostB"))
- .parentHostname(HostName.from("parentHostB"))
- .state(Node.State.active)
- .type(NodeType.tenant)
- .owner(ApplicationId.from("tenant1", "app1", "default"))
- .currentVersion(Version.fromString("7.42"))
- .wantedVersion(Version.fromString("7.42"))
- .currentOsVersion(Version.fromString("7.6"))
- .wantedOsVersion(Version.fromString("7.6"))
- .serviceState(Node.ServiceState.expectedUp)
- .resources(new NodeResources(1, 1, 1, 1))
- .cost(20)
- .clusterId("clusterB")
- .clusterType(Node.ClusterType.container)
- .build();
- tester.configServer().nodeRepository().addNodes(zone, List.of(nodeA, nodeB));
- }
-
-}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
index dde1333eb5f..008052d1b5d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
@@ -99,7 +99,7 @@ public class ApplicationSerializerTest {
Instant activityAt = Instant.parse("2018-06-01T10:15:30.00Z");
deployments.add(new Deployment(zone1, applicationVersion1, Version.fromString("1.2.3"), Instant.ofEpochMilli(3))); // One deployment without cluster info and utils
deployments.add(new Deployment(zone2, applicationVersion2, Version.fromString("1.2.3"), Instant.ofEpochMilli(5),
- createClusterInfo(3, 4),
+ Map.of(),
new DeploymentMetrics(2, 3, 4, 5, 6,
Optional.of(Instant.now().truncatedTo(ChronoUnit.MILLIS)),
Map.of(DeploymentMetrics.Warning.all, 3)),
@@ -188,14 +188,7 @@ public class ApplicationSerializerTest {
assertEquals(original.require(id3.instance()).change(), serialized.require(id3.instance()).change());
// Test cluster info
- assertEquals(3, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().size());
- assertEquals(10, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorCost());
- assertEquals(ClusterSpec.Type.content, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getClusterType());
- assertEquals("flavor2", serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavor());
- assertEquals(4, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getHostnames().size());
- assertEquals(2, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorCPU(), Double.MIN_VALUE);
- assertEquals(4, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorMem(), Double.MIN_VALUE);
- assertEquals(50, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().get(ClusterSpec.Id.from("id2")).getFlavorDisk(), Double.MIN_VALUE);
+ assertEquals(0, serialized.require(id1.instance()).deployments().get(zone2).clusterInfo().size());
// Test metrics
assertEquals(original.metrics().queryServiceQuality(), serialized.metrics().queryServiceQuality(), Double.MIN_VALUE);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java
index 5f8a3eaa98a..58957e50d2d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java
@@ -4,6 +4,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCe
import org.junit.Test;
import java.util.List;
+import java.util.Optional;
import static org.junit.Assert.*;
@@ -12,7 +13,7 @@ public class EndpointCertificateMetadataSerializerTest {
private EndpointCertificateMetadata sample =
new EndpointCertificateMetadata("keyName", "certName", 1);
private EndpointCertificateMetadata sampleWithRequestMetadata =
- new EndpointCertificateMetadata("keyName", "certName", 1, "requestId", List.of("SAN1", "SAN2"));
+ new EndpointCertificateMetadata("keyName", "certName", 1, Optional.of("requestId"), Optional.of(List.of("SAN1", "SAN2")), Optional.of("issuer"));
@Test
public void serialize() {
@@ -24,7 +25,7 @@ public class EndpointCertificateMetadataSerializerTest {
@Test
public void serializeWithRequestMetadata() {
assertEquals(
- "{\"keyName\":\"keyName\",\"certName\":\"certName\",\"version\":1,\"requestId\":\"requestId\",\"requestedDnsSans\":[\"SAN1\",\"SAN2\"]}",
+ "{\"keyName\":\"keyName\",\"certName\":\"certName\",\"version\":1,\"requestId\":\"requestId\",\"requestedDnsSans\":[\"SAN1\",\"SAN2\"],\"issuer\":\"issuer\"}",
EndpointCertificateMetadataSerializer.toSlime(sampleWithRequestMetadata).toString());
}
@@ -41,6 +42,6 @@ public class EndpointCertificateMetadataSerializerTest {
assertEquals(
sampleWithRequestMetadata,
EndpointCertificateMetadataSerializer.fromJsonString(
- "{\"keyName\":\"keyName\",\"certName\":\"certName\",\"version\":1,\"requestId\":\"requestId\",\"requestedDnsSans\":[\"SAN1\",\"SAN2\"]}"));
+ "{\"keyName\":\"keyName\",\"certName\":\"certName\",\"version\":1,\"requestId\":\"requestId\",\"requestedDnsSans\":[\"SAN1\",\"SAN2\"],\"issuer\":\"issuer\"}"));
}
} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
index 0df94598935..11bcb983fe2 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java
@@ -11,6 +11,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
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.deployment.ConvergenceSummary;
+import com.yahoo.vespa.hosted.controller.deployment.JobProfile;
import com.yahoo.vespa.hosted.controller.deployment.Run;
import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
import com.yahoo.vespa.hosted.controller.deployment.Step;
@@ -148,7 +149,7 @@ public class RunSerializerTest {
assertEquals(run.versions(), phoenix.versions());
assertEquals(run.steps(), phoenix.steps());
- Run initial = Run.initial(id, run.versions(), run.start());
+ Run initial = Run.initial(id, run.versions(), run.start(), JobProfile.production);
assertEquals(initial, serializer.runFromSlime(serializer.toSlime(initial)));
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index 019231321d6..810b9c2550c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -45,7 +45,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringData;
import com.yahoo.vespa.hosted.controller.api.integration.resource.MockTenantCost;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
-import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingEndpoint;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMeteringClient;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
@@ -258,8 +257,22 @@ public class ApplicationApiTest extends ControllerContainerTest {
ApplicationId id = ApplicationId.from("tenant1", "application1", "instance1");
var app1 = deploymentTester.newDeploymentContext(id);
- // POST (deploy) an application to start a manual deployment to dev
+ // POST (deploy) an application to start a manual deployment in prod is not allowed
MultiPartStreamer entity = createApplicationDeployData(applicationPackageInstance1, true);
+ tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/production-us-east-3/", POST)
+ .data(entity)
+ .userIdentity(USER_ID),
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Direct deployments are only allowed to manually deployed environments.\"}", 400);
+
+ // POST (deploy) an application to start a manual deployment in prod is allowed for operators
+ tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/production-us-east-3/", POST)
+ .data(entity)
+ .userIdentity(HOSTED_VESPA_OPERATOR),
+ "{\"message\":\"Deployment started in run 1 of production-us-east-3 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}");
+ app1.runJob(JobType.productionUsEast3);
+ tester.controller().applications().deactivate(app1.instanceId(), ZoneId.from("prod", "us-east-3"));
+
+ // POST (deploy) an application to start a manual deployment to dev
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/dev-us-east-1/", POST)
.data(entity)
.userIdentity(USER_ID),
@@ -634,10 +647,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
ZoneId.from("dev", "us-east-1"),
Optional.of(applicationPackageDefault),
new DeployOptions(false, Optional.empty(), false, false));
- tester.serviceRegistry().routingGeneratorMock().putEndpoints(new DeploymentId(ApplicationId.from("tenant1", "application1", "default"), ZoneId.from("prod", "us-central-1")),
- List.of(new RoutingEndpoint("https://us-central-1.prod.default", "host", false, "upstream")));
- tester.serviceRegistry().routingGeneratorMock().putEndpoints(new DeploymentId(ApplicationId.from("tenant1", "application1", "my-user"), ZoneId.from("dev", "us-east-1")),
- List.of(new RoutingEndpoint("https://us-east-1.dev.my-user", "host", false, "upstream")));
+
// GET test-config for local tests against a dev deployment
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/my-user/job/dev-us-east-1/test-config", GET)
.userIdentity(USER_ID),
@@ -798,8 +808,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
// Create tenant and deploy
var app = deploymentTester.newDeploymentContext(createTenantAndApplication());
app.submit(applicationPackage).deploy();
- app.addRoutingPolicy(westZone, true);
- app.addRoutingPolicy(eastZone, true);
// Invalid application fails
tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/environment/prod/region/us-west-1/instance/default/global-rotation", GET)
@@ -1467,8 +1475,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
-
-
@Test
public void applicationWithRoutingPolicy() {
var app = deploymentTester.newDeploymentContext(createTenantAndApplication());
@@ -1481,8 +1487,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.region(zone.region().value())
.build();
app.submit(applicationPackage).deploy();
- app.addRoutingPolicy(zone, true);
- app.addRoutingPolicy(zone, false);
+ app.addInactiveRoutingPolicy(zone);
// GET application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", GET)
@@ -1643,7 +1648,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
private void assertGlobalRouting(DeploymentId deployment, GlobalRouting.Status status, GlobalRouting.Agent agent) {
var changedAt = tester.controller().clock().instant();
- var westPolicies = tester.controller().routingController().policies().get(deployment);
+ var westPolicies = tester.controller().routing().policies().get(deployment);
assertEquals(1, westPolicies.size());
var westPolicy = westPolicies.values().iterator().next();
assertEquals(status, westPolicy.status().globalRouting().status());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
index 1c96f46dd31..6b71968822f 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
@@ -23,7 +23,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
-import java.util.Date;
import java.util.Optional;
import static com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException.ErrorCode.INVALID_APPLICATION_PACKAGE;
@@ -153,7 +152,6 @@ public class JobControllerApiHandlerHelperTest {
tester.configServer().setLogStream("Nope, this won't be logged");
tester.configServer().convergeServices(app.instanceId(), zone);
- tester.setEndpoints(app.instanceId(), zone);
tester.runner().run();
assertResponse(JobControllerApiHandlerHelper.jobTypeResponse(tester.controller(), app.instanceId(), URI.create("https://some.url:43/root")), "dev-overview.json");
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 282c18046d3..fe9d0573ed2 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
@@ -464,8 +464,8 @@
],
"runs": [
{
- "id": 1,
- "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-east-3/run/1",
+ "id": 2,
+ "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-east-3/run/2",
"start": "(ignore)",
"status": "aborted",
"versions": {
@@ -491,6 +491,31 @@
"status": "unfinished"
}
]
+ },
+ {
+ "id": 1,
+ "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-east-3/run/1",
+ "start": "(ignore)",
+ "end": "(ignore)",
+ "status": "success",
+ "versions": {
+ "targetPlatform": "6.1.0",
+ "targetApplication": {}
+ },
+ "steps": [
+ {
+ "name": "deployReal",
+ "status": "succeeded"
+ },
+ {
+ "name": "installReal",
+ "status": "succeeded"
+ },
+ {
+ "name": "copyVespaLogs",
+ "status": "succeeded"
+ }
+ ]
}
]
},
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json
index eb8bf523474..cd47859c7cc 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json
@@ -15,20 +15,13 @@
{
"cluster": "default",
"tls": true,
- "url": "https://c0.instance1.application1.tenant1.global.vespa.oath.cloud/",
- "scope": "global",
- "routingMethod": "exclusive"
- },
- {
- "cluster": "default",
- "tls": true,
- "url": "https://instance1--application1--tenant1.us-west-1.prod.vespa:43",
+ "url": "https://instance1--application1--tenant1.us-west-1.vespa.oath.cloud:4443/",
"scope": "zone",
"routingMethod": "shared"
}
],
"serviceUrls": [
- "https://instance1--application1--tenant1.us-west-1.prod.vespa:43"
+ "https://instance1--application1--tenant1.us-west-1.vespa.oath.cloud:4443/"
],
"nodes": "http://localhost:8080/zone/v2/prod/us-west-1/nodes/v2/node/%3F&recursive=true&application=tenant1.application1.instance1",
"yamasUrl": "http://monitoring-system.test/?environment=prod&region=us-west-1&application=tenant1.application1.instance1",
@@ -52,12 +45,6 @@
},
"status": "complete",
"activity": {},
- "cost": {
- "tco": 0,
- "waste": 0,
- "utilization": 0.0,
- "cluster": {}
- },
"metrics": {
"queriesPerSecond": 0.0,
"writesPerSecond": 0.0,
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
index ef8899c0860..726df575028 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
@@ -8,12 +8,12 @@
{
"cluster": "default",
"tls": true,
- "url": "https://instance1--application1--tenant1.us-central-1.prod.vespa:43",
+ "url": "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/",
"scope": "zone",
"routingMethod": "shared"
},
{
- "cluster": "foo",
+ "cluster": "",
"tls": true,
"url": "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/",
"scope": "global",
@@ -21,7 +21,7 @@
}
],
"serviceUrls": [
- "https://instance1--application1--tenant1.us-central-1.prod.vespa:43"
+ "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/"
],
"nodes": "http://localhost:8080/zone/v2/prod/us-central-1/nodes/v2/node/%3F&recursive=true&application=tenant1.application1.instance1",
"yamasUrl": "http://monitoring-system.test/?environment=prod&region=us-central-1&application=tenant1.application1.instance1",
@@ -59,12 +59,6 @@
"lastQueriesPerSecond": 1.0,
"lastWritesPerSecond": 2.0
},
- "cost": {
- "tco": 0,
- "waste": 0,
- "utilization": 0.0,
- "cluster": {}
- },
"metrics": {
"queriesPerSecond": 1.0,
"writesPerSecond": 2.0,
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json
index b63031fab4f..d151e615f17 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json
@@ -6,7 +6,7 @@
{
"at": 0,
"type": "info",
- "message": " |-- https://default--application--tenant.us-east-1.dev.vespa:43 (cluster 'default')"
+ "message": " |-- https://application--tenant.us-east-1.dev.vespa.oath.cloud:4443/ (cluster 'default')"
},
{
"at": 0,
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json
index cc930c94051..7c231beb5ed 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json
@@ -8,13 +8,13 @@
{
"cluster": "default",
"tls": true,
- "url": "https://instance1--application1--tenant1.us-east-1.dev.vespa:43",
+ "url": "https://instance1--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/",
"scope": "zone",
"routingMethod": "shared"
}
],
"serviceUrls": [
- "https://instance1--application1--tenant1.us-east-1.dev.vespa:43"
+ "https://instance1--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/"
],
"nodes": "http://localhost:8080/zone/v2/dev/us-east-1/nodes/v2/node/%3F&recursive=true&application=tenant1.application1.instance1",
"yamasUrl": "http://monitoring-system.test/?environment=dev&region=us-east-1&application=tenant1.application1.instance1",
@@ -28,12 +28,6 @@
"lastQueriesPerSecond": 1.0,
"lastWritesPerSecond": 2.0
},
- "cost": {
- "tco": 0,
- "waste": 0,
- "utilization": 0.0,
- "cluster": {}
- },
"metrics": {
"queriesPerSecond": 1.0,
"writesPerSecond": 2.0,
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json
index a21b1558aee..f7c512842fd 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json
@@ -1 +1,11 @@
-{"globalrotationoverride":["cluster1.application1.tenant1.us-west-1.prod",{"status":"in","reason":"","agent":"","timestamp":1497618757}]}
+{
+ "globalrotationoverride": [
+ "instance1.application1.tenant1.us-west-1.prod",
+ {
+ "status": "in",
+ "reason": "",
+ "agent": "",
+ "timestamp": 1497618757
+ }
+ ]
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json
index 18f5127718f..16061053b98 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json
@@ -180,9 +180,7 @@
],
"changeBlockers": [],
"compileVersion": "(ignore)",
- "globalRotations": [
- "https://c0.instance1.application1.tenant1.global.vespa.oath.cloud/"
- ],
+ "globalRotations": [],
"instances": [
{
"environment": "prod",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json
index fd8bc256ac5..3d1c5ade300 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json
@@ -13,8 +13,8 @@
"projectId": 123,
"deploying": {
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -30,10 +30,10 @@
"success": true,
"lastTriggered": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -47,10 +47,10 @@
},
"lastCompleted": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -64,10 +64,10 @@
},
"lastSuccess": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -85,10 +85,10 @@
"success": true,
"lastTriggered": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -102,10 +102,10 @@
},
"lastCompleted": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -119,10 +119,10 @@
},
"lastSuccess": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -140,10 +140,10 @@
"success": true,
"lastTriggered": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -157,10 +157,10 @@
},
"lastCompleted": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -174,10 +174,10 @@
},
"lastSuccess": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -192,13 +192,13 @@
},
{
"type": "production-us-east-3",
- "success": false,
+ "success": true,
"lastTriggered": {
- "id": 1,
- "version": "(ignore)",
+ "id": 2,
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -209,6 +209,18 @@
},
"reason": "unknown reason",
"at": "(ignore)"
+ },
+ "lastCompleted": {
+ "id": 1,
+ "version": "6.1.0",
+ "reason": "unknown reason",
+ "at": "(ignore)"
+ },
+ "lastSuccess": {
+ "id": 1,
+ "version": "6.1.0",
+ "reason": "unknown reason",
+ "at": "(ignore)"
}
},
{
@@ -263,7 +275,7 @@
"rotationId": "rotation-id-1",
"clusterId": "foo",
"status": "IN",
- "lastUpdated":"(ignore)"
+ "lastUpdated": "(ignore)"
}
],
"environment": "prod",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
index ee75d129241..1b7e7893222 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
@@ -13,8 +13,8 @@
"projectId": 123,
"deploying": {
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -30,10 +30,10 @@
"success": true,
"lastTriggered": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -47,10 +47,10 @@
},
"lastCompleted": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -64,10 +64,10 @@
},
"lastSuccess": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -85,10 +85,10 @@
"success": true,
"lastTriggered": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -102,10 +102,10 @@
},
"lastCompleted": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -119,10 +119,10 @@
},
"lastSuccess": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -140,10 +140,10 @@
"success": true,
"lastTriggered": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -157,10 +157,10 @@
},
"lastCompleted": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -174,10 +174,10 @@
},
"lastSuccess": {
"id": 1,
- "version": "(ignore)",
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -192,13 +192,13 @@
},
{
"type": "production-us-east-3",
- "success": false,
+ "success": true,
"lastTriggered": {
- "id": 1,
- "version": "(ignore)",
+ "id": 2,
+ "version": "6.1.0",
"revision": {
- "buildNumber": "(ignore)",
- "hash": "(ignore)",
+ "buildNumber": 1,
+ "hash": "1.0.1-commit1",
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -209,6 +209,18 @@
},
"reason": "unknown reason",
"at": "(ignore)"
+ },
+ "lastCompleted": {
+ "id": 1,
+ "version": "6.1.0",
+ "reason": "unknown reason",
+ "at": "(ignore)"
+ },
+ "lastSuccess": {
+ "id": 1,
+ "version": "6.1.0",
+ "reason": "unknown reason",
+ "at": "(ignore)"
}
},
{
@@ -241,7 +253,7 @@
]
}
],
- "compileVersion": "(ignore)",
+ "compileVersion": "6.0.0",
"globalRotations": [
"https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"
],
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 8cd102432d0..b16ca4cc67c 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
@@ -275,7 +275,7 @@
"us-east-3": {
"runs": [
{
- "id": 1,
+ "id": 2,
"status": "aborted",
"start": "(ignore)",
"wantedPlatform": "6.1",
@@ -296,6 +296,26 @@
"report": "unfinished"
},
"tasks": {},
+ "log": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-east-3/run/2"
+ },
+ {
+ "id": 1,
+ "status": "success",
+ "start": "(ignore)",
+ "end": "(ignore)",
+ "wantedPlatform": "6.1",
+ "wantedApplication": {
+ "hash": "unknown"
+ },
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "copyVespaLogs": "succeeded"
+ },
+ "tasks": {
+ "deploy": "succeeded",
+ "install": "succeeded"
+ },
"log": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-east-3/run/1"
}
],
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json
index 436c2767b3e..41f3908f12f 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json
@@ -11,12 +11,12 @@
{
"cluster": "default",
"tls": true,
- "url": "https://instance1--application1--tenant1.us-central-1.prod.vespa:43",
+ "url": "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/",
"scope": "zone",
"routingMethod": "shared"
},
{
- "cluster": "foo",
+ "cluster": "",
"tls": true,
"url": "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/",
"scope": "global",
@@ -24,7 +24,7 @@
}
],
"serviceUrls": [
- "https://instance1--application1--tenant1.us-central-1.prod.vespa:43"
+ "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/"
],
"nodes": "http://localhost:8080/zone/v2/prod/us-central-1/nodes/v2/node/%3F&recursive=true&application=tenant1.application1.instance1",
"yamasUrl": "http://monitoring-system.test/?environment=prod&region=us-central-1&application=tenant1.application1.instance1",
@@ -62,12 +62,6 @@
"lastQueriesPerSecond": 1.0,
"lastWritesPerSecond": 2.0
},
- "cost": {
- "tco": 0,
- "waste": 0,
- "utilization": 0.0,
- "cluster": {}
- },
"metrics": {
"queriesPerSecond": 1.0,
"writesPerSecond": 2.0,
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json
index 6db83522c26..c01ab2f61b3 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json
@@ -212,7 +212,7 @@
{
"at": "(ignore)",
"type": "info",
- "message": " |-- https://instance1--application1--tenant1.us-east-1.test.vespa:43 (cluster 'default')"
+ "message": " |-- https://instance1--application1--tenant1.us-east-1.test.vespa.oath.cloud:4443/ (cluster 'default')"
},
{
"at": "(ignore)",
@@ -239,7 +239,7 @@
{
"at": "(ignore)",
"type": "info",
- "message": " |-- https://instance1--application1--tenant1.us-east-1.test.vespa:43 (cluster 'default')"
+ "message": " |-- https://instance1--application1--tenant1.us-east-1.test.vespa.oath.cloud:4443/ (cluster 'default')"
},
{
"at": "(ignore)",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json
index 818dc72a9d9..0632ab7a67b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json
@@ -5,18 +5,18 @@
"isCI": false,
"endpoints": {
"dev.us-east-1": [
- "https://us-east-1.dev.my-user"
+ "https://my-user--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/"
],
"prod.us-central-1": [
- "https://us-central-1.prod.default"
+ "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/"
]
},
"zoneEndpoints": {
"dev.us-east-1": {
- "default": "https://us-east-1.dev.my-user"
+ "default": "https://my-user--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/"
},
"prod.us-central-1": {
- "default": "https://us-central-1.prod.default"
+ "default": "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/"
}
},
"clusters": {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json
index c1ccbc7100f..c81ed767239 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json
@@ -5,12 +5,12 @@
"isCI": false,
"endpoints": {
"prod.us-central-1": [
- "https://us-central-1.prod.default"
+ "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/"
]
},
"zoneEndpoints": {
"prod.us-central-1": {
- "default": "https://us-central-1.prod.default"
+ "default": "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/"
}
},
"clusters": {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
index 3371c5563c9..fbdf8caaed7 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
@@ -10,9 +10,6 @@
"name": "CloudEventReporter"
},
{
- "name": "ClusterInfoMaintainer"
- },
- {
"name": "ContactInformationMaintainer"
},
{
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java
index d191b460697..f079bd1d7e6 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java
@@ -131,8 +131,6 @@ public class RoutingApiTest extends ControllerContainerTest {
.endpoint("default", "default", eastZone.region().value(), westZone.region().value())
.build();
context.submit(applicationPackage).deploy();
- context.addRoutingPolicy(westZone, true);
- context.addRoutingPolicy(eastZone, true);
// GET initial deployment status
tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1",
@@ -255,9 +253,6 @@ public class RoutingApiTest extends ControllerContainerTest {
.build();
context.submit(applicationPackage).deploy();
- // Assign policy in one zone
- context.addRoutingPolicy(westZone, true);
-
// GET status with both policy and rotation assigned
tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1",
"", Request.Method.GET),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
index 079e2c9c388..56108dce94f 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
@@ -11,57 +11,21 @@
"administrator",
"developer",
"reader"
- ],
- "applications": {
- "app1": {
- "instances": [
- "default"
- ]
- },
- "app2": {
- "instances": [
- "default",
- "dev"
- ]
- }
- }
+ ]
},
"tenant1": {
"roles": [
"administrator",
"developer",
"reader"
- ],
- "applications": {
- "app1": {
- "instances": [
- "default"
- ]
- },
- "app2": {
- "instances": [
- "default",
- "myinstance"
- ]
- },
- "app3": {
- "instances": []
- }
- }
+ ]
},
"tenant2": {
"roles": [
"administrator",
"developer",
"reader"
- ],
- "applications": {
- "app2": {
- "instances": [
- "test"
- ]
- }
- }
+ ]
}
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
index f0ea10ed888..ea76aa977ce 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
@@ -10,53 +10,17 @@
"roles": [
"developer",
"reader"
- ],
- "applications": {
- "app1": {
- "instances": [
- "default"
- ]
- },
- "app2": {
- "instances": [
- "default",
- "dev"
- ]
- }
- }
+ ]
},
"tenant1": {
"roles": [
"administrator"
- ],
- "applications": {
- "app1": {
- "instances": [
- "default"
- ]
- },
- "app2": {
- "instances": [
- "default",
- "myinstance"
- ]
- },
- "app3": {
- "instances": []
- }
- }
+ ]
},
"tenant2": {
"roles": [
"developer"
- ],
- "applications": {
- "app2": {
- "instances": [
- "test"
- ]
- }
- }
+ ]
}
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
index 1bb531edbc5..b67422bc06b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
@@ -2,12 +2,16 @@
package com.yahoo.vespa.hosted.controller.rotation;
import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.AssignedRotation;
+import com.yahoo.vespa.hosted.controller.application.EndpointId;
+import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
+import com.yahoo.vespa.hosted.controller.routing.RoutingId;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
import org.junit.Before;
import org.junit.Rule;
@@ -55,7 +59,7 @@ public class RotationRepositoryTest {
@Before
public void before() {
tester = new DeploymentTester(new ControllerTester(rotationsConfig));
- repository = tester.controller().routingController().rotations();
+ repository = tester.controller().routing().rotations();
application = tester.newDeploymentContext("tenant1", "app1", "default");
}
@@ -67,7 +71,8 @@ public class RotationRepositoryTest {
assertEquals(List.of(expected.id()), rotationIds(application.instance().rotations()));
assertEquals(URI.create("https://app1--tenant1.global.vespa.oath.cloud:4443/"),
- application.instance().endpointsIn(SystemName.main).main().get().url());
+ EndpointList.global(RoutingId.of(application.instanceId(), EndpointId.defaultId()), SystemName.main, RoutingMethod.shared)
+ .primary().get().url());
try (RotationLock lock = repository.lock()) {
List<AssignedRotation> rotations = repository.getOrAssignRotations(application.application().deploymentSpec(),
application.instance(),
@@ -83,7 +88,7 @@ public class RotationRepositoryTest {
@Test
public void strips_whitespace_in_rotation_fqdn() {
tester = new DeploymentTester(new ControllerTester(rotationsConfigWhitespaces));
- RotationRepository repository = tester.controller().routingController().rotations();
+ RotationRepository repository = tester.controller().routing().rotations();
var application2 = tester.newDeploymentContext("tenant1", "app2", "default");
application2.submit(applicationPackage);
@@ -143,7 +148,8 @@ public class RotationRepositoryTest {
application2.submit(applicationPackage);
assertEquals(List.of(new RotationId("foo-1")), rotationIds(application2.instance().rotations()));
assertEquals("https://cd--app2--tenant2.global.vespa.oath.cloud:4443/",
- application2.instance().endpointsIn(SystemName.cd).main().get().url().toString());
+ EndpointList.global(RoutingId.of(application2.instanceId(), EndpointId.defaultId()), SystemName.cd, RoutingMethod.shared)
+ .primary().get().url().toString());
}
@Test
@@ -159,9 +165,11 @@ public class RotationRepositoryTest {
assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations()));
assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations()));
assertEquals(URI.create("https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"),
- instance1.instance().endpointsIn(SystemName.main).main().get().url());
+ EndpointList.global(RoutingId.of(instance1.instanceId(), EndpointId.defaultId()), SystemName.main, RoutingMethod.shared)
+ .primary().get().url());
assertEquals(URI.create("https://instance2--application1--tenant1.global.vespa.oath.cloud:4443/"),
- instance2.instance().endpointsIn(SystemName.main).main().get().url());
+ EndpointList.global(RoutingId.of(instance2.instanceId(), EndpointId.defaultId()), SystemName.main, RoutingMethod.shared)
+ .primary().get().url());
}
@Test
@@ -179,9 +187,11 @@ public class RotationRepositoryTest {
assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations()));
assertEquals(URI.create("https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"),
- instance1.instance().endpointsIn(SystemName.main).main().get().url());
+ EndpointList.global(RoutingId.of(instance1.instanceId(), EndpointId.defaultId()), SystemName.main, RoutingMethod.shared)
+ .primary().get().url());
assertEquals(URI.create("https://instance2--application1--tenant1.global.vespa.oath.cloud:4443/"),
- instance2.instance().endpointsIn(SystemName.main).main().get().url());
+ EndpointList.global(RoutingId.of(instance2.instanceId(), EndpointId.defaultId()), SystemName.main, RoutingMethod.shared)
+ .primary().get().url());
}
private void assertSingleRotation(Rotation expected, List<AssignedRotation> assignedRotations, RotationRepository repository) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
index a27b2e1084a..a48554e2af4 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
@@ -22,6 +22,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
+import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
@@ -31,7 +32,6 @@ import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
import org.junit.Test;
-import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -39,7 +39,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -248,22 +247,6 @@ public class RoutingPoliciesTest {
}
@Test
- public void cluster_endpoints_resolve_from_policies() {
- var tester = new RoutingPoliciesTester();
- var context = tester.newDeploymentContext("tenant1", "app1", "default");
- tester.provisionLoadBalancers(3, context.instanceId(), zone1, zone2);
- context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy();
- tester.controllerTester().serviceRegistry().routingGeneratorMock().putEndpoints(context.deploymentIdIn(zone1), List.of());
- assertEquals(Map.of(ClusterSpec.Id.from("c0"),
- URI.create("https://c0.app1.tenant1.us-west-1.vespa.oath.cloud/"),
- ClusterSpec.Id.from("c1"),
- URI.create("https://c1.app1.tenant1.us-west-1.vespa.oath.cloud/"),
- ClusterSpec.Id.from("c2"),
- URI.create("https://c2.app1.tenant1.us-west-1.vespa.oath.cloud/")),
- tester.controllerTester().controller().routingController().zoneEndpointsOf(context.deploymentIdIn(zone1)));
- }
-
- @Test
public void manual_deployment_creates_routing_policy() {
// Empty application package is valid in manually deployed environments
var tester = new RoutingPoliciesTester();
@@ -274,7 +257,6 @@ public class RoutingPoliciesTest {
tester.controllerTester().serviceRegistry().zoneRegistry()
.setZones(zoneApi)
.exclusiveRoutingIn(zoneApi);
- tester.provisionLoadBalancers(1, context.instanceId(), zone);
// Deploy to dev
tester.controllerTester().controller().applications().deploy(context.instanceId(), zone, Optional.of(emptyApplicationPackage), DeployOptions.none());
@@ -283,7 +265,7 @@ public class RoutingPoliciesTest {
// Routing policy is created and DNS is updated
assertEquals(1, tester.policiesOf(context.instanceId()).size());
- assertEquals(Set.of("c0.app1.tenant1.us-east-1.dev.vespa.oath.cloud"), tester.recordNames());
+ assertEquals(Set.of("app1.tenant1.us-east-1.dev.vespa.oath.cloud"), tester.recordNames());
}
@Test
@@ -302,14 +284,13 @@ public class RoutingPoliciesTest {
// Deploy to dev under different instance
var devInstance = context.application().id().instance("user");
- tester.provisionLoadBalancers(1, devInstance, zone);
tester.controllerTester().controller().applications().deploy(devInstance, zone, Optional.of(applicationPackage), DeployOptions.none());
assertEquals("DeploymentSpec is persisted", applicationPackage.deploymentSpec(), context.application().deploymentSpec());
context.flushDnsUpdates();
// Routing policy is created and DNS is updated
assertEquals(1, tester.policiesOf(devInstance).size());
- assertEquals(Sets.union(prodRecords, Set.of("c0.user.app1.tenant1.us-east-1.dev.vespa.oath.cloud")), tester.recordNames());
+ assertEquals(Sets.union(prodRecords, Set.of("user.app1.tenant1.us-east-1.dev.vespa.oath.cloud")), tester.recordNames());
}
@Test
@@ -618,7 +599,7 @@ public class RoutingPoliciesTest {
}
public RoutingPolicies routingPolicies() {
- return tester.controllerTester().controller().routingController().policies();
+ return tester.controllerTester().controller().routing().policies();
}
public DeploymentContext newDeploymentContext(String tenant, String application, String instance) {
@@ -668,7 +649,8 @@ public class RoutingPoliciesTest {
}
private void assertTargets(ApplicationId application, EndpointId endpointId, int loadBalancerId, ZoneId ...zone) {
- var endpoint = RoutingPolicy.globalEndpointOf(application, endpointId, tester.controller().system()).dnsName();
+ var endpoint = EndpointList.global(RoutingId.of(application, endpointId), tester.controller().system(), RoutingMethod.exclusive)
+ .primary().get().dnsName();
var zoneTargets = Arrays.stream(zone)
.map(z -> "lb-" + loadBalancerId + "--" + application.serializedForm() + "--" +
z.value() + "/dns-zone-1/" + z.value())
diff --git a/dist/vespa.spec b/dist/vespa.spec
index 1c1f5071684..4281ce243fa 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -108,6 +108,7 @@ BuildRequires: openblas-devel
BuildRequires: lz4-devel
BuildRequires: libzstd-devel
BuildRequires: zlib-devel
+BuildRequires: re2-devel
%if ! 0%{?el7}
BuildRequires: libicu-devel
%endif
@@ -151,6 +152,7 @@ Requires: openblas-serial
Requires: lz4
Requires: libzstd
Requires: zlib
+Requires: re2
%if ! 0%{?el7}
Requires: libicu
%endif
@@ -162,6 +164,7 @@ Requires: llvm5.0
Requires: vespa-openssl >= 1.1.1c-1
Requires: vespa-icu >= 65.1.0-1
Requires: vespa-protobuf >= 3.7.0-4
+Requires: vespa-telegraf >= 1.1.1-1
%define _vespa_llvm_version 5.0
%define _extra_link_directory /usr/lib64/llvm5.0/lib;%{_vespa_deps_prefix}/lib64
%define _extra_include_directory /usr/include/llvm5.0;%{_vespa_deps_prefix}/include;/usr/include/openblas
diff --git a/docker-api/CMakeLists.txt b/docker-api/CMakeLists.txt
index 1288c624890..edcdcfb5bfc 100644
--- a/docker-api/CMakeLists.txt
+++ b/docker-api/CMakeLists.txt
@@ -1,4 +1,2 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-install_fat_java_artifact(docker-api)
-install(DIRECTORY DESTINATION conf/node-admin-app/components)
-install_symlink(lib/jars/docker-api-jar-with-dependencies.jar conf/node-admin-app/components/docker-api.jar)
+install(FILES target/docker-api-jar-with-dependencies.jar DESTINATION conf/node-admin-app/components)
diff --git a/eval/src/tests/ann/bruteforce-nns.h b/eval/src/tests/ann/bruteforce-nns.h
index 0c7c48654f7..ecac73c0d10 100644
--- a/eval/src/tests/ann/bruteforce-nns.h
+++ b/eval/src/tests/ann/bruteforce-nns.h
@@ -47,7 +47,7 @@ public:
TopK bruteforce_nns(const PointVector &query) {
TopK result;
BfHitHeap heap(result.K);
- for (uint32_t docid = 0; docid < NUM_DOCS; ++docid) {
+ for (uint32_t docid = 0; docid < EFFECTIVE_DOCS; ++docid) {
const PointVector &docvector = generatedDocs[docid];
double d = l2distCalc.l2sq_dist(query, docvector);
Hit h(docid, d);
@@ -64,7 +64,7 @@ void verifyBF(uint32_t qid) {
const PointVector &query = generatedQueries[qid];
TopK &result = bruteforceResults[qid];
double min_distance = result.hits[0].distance;
- for (uint32_t i = 0; i < NUM_DOCS; ++i) {
+ for (uint32_t i = 0; i < EFFECTIVE_DOCS; ++i) {
double dist = computeDistance(query, i);
if (dist < min_distance) {
fprintf(stderr, "WARN dist %.9g < mindist %.9g\n", dist, min_distance);
diff --git a/eval/src/tests/ann/gist_benchmark.cpp b/eval/src/tests/ann/gist_benchmark.cpp
index de8bff877e6..5a317e77e72 100644
--- a/eval/src/tests/ann/gist_benchmark.cpp
+++ b/eval/src/tests/ann/gist_benchmark.cpp
@@ -11,6 +11,7 @@
#define NUM_DIMS 960
#define NUM_DOCS 200000
+#define EFFECTIVE_DOCS NUM_DOCS
#define NUM_REACH 10000
#define NUM_Q 1000
diff --git a/eval/src/tests/ann/sift_benchmark.cpp b/eval/src/tests/ann/sift_benchmark.cpp
index b2fa66cd0f1..4bbe8f61ef1 100644
--- a/eval/src/tests/ann/sift_benchmark.cpp
+++ b/eval/src/tests/ann/sift_benchmark.cpp
@@ -12,6 +12,7 @@
#define NUM_DIMS 128
#define NUM_DOCS 1000000
+#define EFFECTIVE_DOCS NUM_DOCS
#define NUM_Q 1000
#define NUM_REACH 10000
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 57228dfe49d..c97edf9ef3d 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -192,7 +192,19 @@ public class Flags {
"Regularly issue a small write to disk and fail the host if it is not successful",
"Takes effect on next node agent tick (but does not clear existing failure reports)",
HOSTNAME);
-
+
+ public static final UnboundBooleanFlag RESTRICT_ACQUIRING_NEW_PRIVILEGES = defineFeatureFlag(
+ "restrict-acquiring-new-privileges", false,
+ "Whether docker daemon should restrict containers from acquiring new privileges",
+ "Takes effect on next host admin tick",
+ HOSTNAME);
+
+ public static final UnboundListFlag<String> AUDITED_PATHS = defineListFlag(
+ "audited-paths", List.of(), String.class,
+ "List of paths that should audited",
+ "Takes effect on next host admin tick",
+ HOSTNAME);
+
public static final UnboundBooleanFlag GENERATE_L4_ROUTING_CONFIG = defineFeatureFlag(
"generate-l4-routing-config", false,
"Whether routing nodes should generate L4 routing config",
@@ -213,8 +225,7 @@ public class Flags {
public static final UnboundStringFlag ENDPOINT_CERTIFICATE_BACKFILL = defineStringFlag(
"endpoint-certificate-backfill", "disable",
"Whether the endpoint certificate maintainer should backfill missing certificate data from cameo",
- "Takes effect on next scheduled run of maintainer - set to \"disable\", \"dryrun\" or \"enable\""
- );
+ "Takes effect on next scheduled run of maintainer - set to \"disable\", \"dryrun\" or \"enable\"");
public static final UnboundBooleanFlag USE_NEW_ATHENZ_FILTER = defineFeatureFlag(
"use-new-athenz-filter", false,
@@ -238,6 +249,12 @@ public class Flags {
"Whether to provision and use endpoint certs for apps in shared routing zones",
"Takes effect on next deployment of the application", APPLICATION_ID);
+ public static final UnboundBooleanFlag PHRASE_SEGMENTING = defineFeatureFlag(
+ "phrase-segmenting", true,
+ "Should 'implicit phrases' in queries we parsed to a phrase or and?",
+ "Takes effect on redeploy",
+ ZONE_ID, APPLICATION_ID);
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, String description,
String modificationEffect, FetchVector.Dimension... dimensions) {
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
index f1486ae7117..ec3baf30eae 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
@@ -17,6 +17,7 @@ import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.ObjectTraverser;
import com.yahoo.slime.Slime;
+import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -37,6 +38,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.OptionalLong;
import java.util.concurrent.Callable;
+import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -76,6 +78,11 @@ public abstract class ControllerHttpClient {
return new SigningControllerHttpClient(endpoint, privateKeyFile, id);
}
+ /** Creates an HTTP client against the given endpoint, which uses the given SSL context for authentication. */
+ public static ControllerHttpClient withSSLContext(URI endpoint, SSLContext sslContext) {
+ return new MutualTlsControllerHttpClient(endpoint, sslContext);
+ }
+
/** Creates an HTTP client against the given endpoint, which uses the given private key and certificate identity. */
public static ControllerHttpClient withKeyAndCertificate(URI endpoint, Path privateKeyFile, Path certificateFile) {
var privateKey = unchecked(() -> KeyUtils.fromPemEncodedPrivateKey(Files.readString(privateKeyFile, UTF_8)));
@@ -141,10 +148,36 @@ public abstract class ControllerHttpClient {
/** Returns the sorted list of log entries after the given after from the deployment job of the given ids. */
public DeploymentLog deploymentLog(ApplicationId id, ZoneId zone, long run, long after) {
return toDeploymentLog(send(request(HttpRequest.newBuilder(runPath(id, zone, run, after))
- .timeout(Duration.ofSeconds(10)),
+ .timeout(Duration.ofMinutes(2)),
GET)));
}
+ /** Follows the given deployment job until it is done, or this thread is interrupted, at which point the current status is returned. */
+ public DeploymentLog followDeploymentUntilDone(ApplicationId id, ZoneId zone, long run,
+ Consumer<DeploymentLog.Entry> out) {
+ long last = -1;
+ DeploymentLog log = null;
+ while (true) {
+ DeploymentLog update = deploymentLog(id, zone, run, last);
+ for (DeploymentLog.Entry entry : update.entries())
+ out.accept(entry);
+ log = (log == null ? update : log.updatedWith(update));
+ last = log.last().orElse(last);
+
+ if ( ! log.isActive())
+ break;
+
+ try {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ return log;
+ }
+
/** Returns the sorted list of log entries from the deployment job of the given ids. */
public DeploymentLog deploymentLog(ApplicationId id, ZoneId zone, long run) {
return deploymentLog(id, zone, run, -1);
@@ -384,14 +417,17 @@ public abstract class ControllerHttpClient {
/** Client that uses a given key / certificate identity to authenticate to the remote controller. */
private static class MutualTlsControllerHttpClient extends ControllerHttpClient {
+ private MutualTlsControllerHttpClient(URI endpoint, SSLContext sslContext) {
+ super(endpoint, HttpClient.newBuilder().sslContext(sslContext));
+ }
+
private MutualTlsControllerHttpClient(URI endpoint, PrivateKey privateKey, List<X509Certificate> certs) {
- super(endpoint,
- HttpClient.newBuilder()
- .sslContext(new SslContextBuilder().withKeyStore(privateKey, certs).build()));
+ this(endpoint, new SslContextBuilder().withKeyStore(privateKey, certs).build());
}
}
+
private static DeploymentLog.Status valueOf(String status) {
switch (status) {
case "running": return DeploymentLog.Status.running;
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java
index 177c72107e0..9eae9a33cff 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java
@@ -4,6 +4,7 @@ package ai.vespa.hosted.api;
import java.time.Instant;
import java.util.List;
import java.util.OptionalLong;
+import java.util.stream.Stream;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toUnmodifiableList;
@@ -27,6 +28,14 @@ public class DeploymentLog {
this.last = last;
}
+ /** Returns this log updated with the content of the other. */
+ public DeploymentLog updatedWith(DeploymentLog other) {
+ return new DeploymentLog(Stream.concat(entries.stream(), other.entries.stream()).collect(toUnmodifiableList()),
+ other.active,
+ other.status,
+ other.last);
+ }
+
public List<Entry> entries() {
return entries;
}
diff --git a/metrics-proxy/src/main/resources/configdefinitions/telegraf.def b/metrics-proxy/src/main/resources/configdefinitions/telegraf.def
index cb03ce6f1f6..8a87b92cd60 100644
--- a/metrics-proxy/src/main/resources/configdefinitions/telegraf.def
+++ b/metrics-proxy/src/main/resources/configdefinitions/telegraf.def
@@ -19,3 +19,4 @@ cloudWatch[].secretKeyName string default=""
# Only valid and optional for self-hosted Vespa
cloudWatch[].profile string default=""
+cloudWatch[].file string default=""
diff --git a/node-admin/CMakeLists.txt b/node-admin/CMakeLists.txt
index 03bf09121c3..8dbb32c7de1 100644
--- a/node-admin/CMakeLists.txt
+++ b/node-admin/CMakeLists.txt
@@ -1,2 +1,6 @@
# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
install(DIRECTORY DESTINATION logs/vespa/node-admin)
+install(FILES target/node-admin-jar-with-dependencies.jar DESTINATION conf/node-admin-app/components)
+install_symlink(lib/jars/flags-jar-with-dependencies.jar conf/node-admin-app/components/flags-jar-with-dependencies.jar)
+install(FILES src/main/application/services.xml DESTINATION conf/node-admin-app)
+install(PROGRAMS src/main/sh/node-admin.sh DESTINATION libexec/vespa)
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java
index 71f7dc3701e..31f3ed5fc95 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java
@@ -14,6 +14,7 @@ import com.yahoo.vespa.hosted.provision.provisioning.NodeResourceLimits;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
+import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
@@ -24,6 +25,8 @@ import java.util.stream.Collectors;
*/
public class Autoscaler {
+ private Logger log = Logger.getLogger(Autoscaler.class.getName());
+
/*
TODO:
- Scale group size
@@ -69,20 +72,28 @@ public class Autoscaler {
Optional<Double> cpuLoad = averageLoad(Resource.cpu, cluster, clusterNodes);
Optional<Double> memoryLoad = averageLoad(Resource.memory, cluster, clusterNodes);
Optional<Double> diskLoad = averageLoad(Resource.disk, cluster, clusterNodes);
- if (cpuLoad.isEmpty() || memoryLoad.isEmpty() || diskLoad.isEmpty()) return Optional.empty();
+ if (cpuLoad.isEmpty() || memoryLoad.isEmpty() || diskLoad.isEmpty()) {
+ log.fine("Autoscaling " + applicationId + " " + cluster + ": Insufficient metrics to decide");
+ return Optional.empty();
+ }
Optional<ClusterResourcesWithCost> bestAllocation = findBestAllocation(cpuLoad.get(),
memoryLoad.get(),
diskLoad.get(),
currentAllocation,
cluster);
- if (bestAllocation.isEmpty()) return Optional.empty();
+ if (bestAllocation.isEmpty()) {
+ log.fine("Autoscaling " + applicationId + " " + cluster + ": Could not find a better allocation");
+ return Optional.empty();
+ }
if (closeToIdeal(Resource.cpu, cpuLoad.get()) &&
closeToIdeal(Resource.memory, memoryLoad.get()) &&
closeToIdeal(Resource.disk, diskLoad.get()) &&
- similarCost(bestAllocation.get().cost(), currentAllocation.nodes() * costOf(currentAllocation.nodeResources())))
+ similarCost(bestAllocation.get().cost(), currentAllocation.nodes() * costOf(currentAllocation.nodeResources()))) {
+ log.fine("Autoscaling " + applicationId + " " + cluster + ": Resources are almost ideal and price difference is small");
return Optional.empty(); // Avoid small, unnecessary changes
+ }
return bestAllocation.map(a -> a.clusterResources());
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java
index ed6e7cc71ef..e65b7273b9e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java
@@ -60,8 +60,7 @@ public class MetricsReporter extends Maintainer {
@Override
public void maintain() {
NodeList nodes = nodeRepository().list();
- Map<HostName, List<ServiceInstance>> servicesByHost =
- serviceMonitor.getServiceModelSnapshot().getServiceInstancesByHostName();
+ Map<HostName, List<ServiceInstance>> servicesByHost = serviceMonitor.getServicesByHostname();
nodes.forEach(node -> updateNodeMetrics(node, servicesByHost));
updateStateMetrics(nodes);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java
index ca53b215237..8a5a119f6a7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java
@@ -21,7 +21,6 @@ import com.yahoo.vespa.orchestrator.ApplicationIdNotFoundException;
import com.yahoo.vespa.orchestrator.HostNameNotFoundException;
import com.yahoo.vespa.orchestrator.Orchestrator;
import com.yahoo.vespa.orchestrator.status.ApplicationInstanceStatus;
-import com.yahoo.vespa.orchestrator.status.HostStatus;
import com.yahoo.vespa.service.monitor.ServiceMonitor;
import com.yahoo.yolean.Exceptions;
@@ -187,7 +186,7 @@ public class NodeFailer extends Maintainer {
Map<String, Node> activeNodesByHostname = nodeRepository().getNodes(Node.State.active).stream()
.collect(Collectors.toMap(Node::hostname, node -> node));
- serviceMonitor.getServiceModelSnapshot().getServiceInstancesByHostName()
+ serviceMonitor.getServicesByHostname()
.forEach((hostName, serviceInstances) -> {
Node node = activeNodesByHostname.get(hostName.s());
if (node == null) return;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java
index 178e8385008..6ae086146c4 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java
@@ -39,6 +39,7 @@ public class NodeMetricsDbMaintainer extends Maintainer {
nodeMetricsDb.add(nodeMetrics.fetchMetrics(application));
}
catch (Exception e) {
+ // TODO: Don't warn if this only happens occasionally
if (warnings++ < maxWarningsPerInvocation)
log.log(Level.WARNING, "Could not update metrics for " + application, e);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java
index 26f8cffa519..1d3fe114e23 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java
@@ -74,7 +74,7 @@ public class LoadBalancerProvisioner {
if (!cluster.type().isContainer()) return; // Nothing to provision for this cluster type
if (application.instance().isTester()) return; // Do not provision for tester instances
try (var lock = db.lockLoadBalancers(application)) {
- provision(application, cluster.id(), false, lock);
+ provision(application, effectiveId(cluster), false, lock);
}
}
@@ -91,7 +91,7 @@ public class LoadBalancerProvisioner {
public void activate(ApplicationId application, Set<ClusterSpec> clusters,
@SuppressWarnings("unused") Mutex applicationLock, NestedTransaction transaction) {
try (var lock = db.lockLoadBalancers(application)) {
- var containerClusters = containerClusterOf(clusters);
+ var containerClusters = containerClustersOf(clusters);
for (var clusterId : containerClusters) {
// Provision again to ensure that load balancer instance is re-configured with correct nodes
provision(application, clusterId, true, lock);
@@ -183,7 +183,7 @@ public class LoadBalancerProvisioner {
.owner(application)
.filter(node -> node.state().isAllocated())
.container()
- .filter(node -> node.allocation().get().membership().cluster().id().equals(clusterId))
+ .filter(node -> effectiveId(node.allocation().get().membership().cluster()).equals(clusterId))
.asList();
}
@@ -202,11 +202,16 @@ public class LoadBalancerProvisioner {
return reachable;
}
- private static Set<ClusterSpec.Id> containerClusterOf(Set<ClusterSpec> clusters) {
+ /** Returns the container cluster IDs of the given clusters */
+ private static Set<ClusterSpec.Id> containerClustersOf(Set<ClusterSpec> clusters) {
return clusters.stream()
.filter(c -> c.type().isContainer())
- .map(ClusterSpec::id)
+ .map(LoadBalancerProvisioner::effectiveId)
.collect(Collectors.toUnmodifiableSet());
}
+ private static ClusterSpec.Id effectiveId(ClusterSpec cluster) {
+ return cluster.combinedId().orElse(cluster.id());
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
index a2579bee0a1..43d4c731759 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
@@ -141,28 +141,28 @@ public class MockNodeRepository extends NodeRepository {
ClusterSpec zoneCluster = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("node-admin"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
activate(provisioner.prepare(zoneApp, zoneCluster, Capacity.fromRequiredNodeType(NodeType.host), 1, null), zoneApp, provisioner);
ApplicationId app1 = ApplicationId.from(TenantName.from("tenant1"), ApplicationName.from("application1"), InstanceName.from("instance1"));
ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("id1"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
provisioner.prepare(app1, cluster1, Capacity.fromCount(2, new NodeResources(2, 8, 50, 1)), 1, null);
ApplicationId app2 = ApplicationId.from(TenantName.from("tenant2"), ApplicationName.from("application2"), InstanceName.from("instance2"));
ClusterSpec cluster2 = ClusterSpec.request(ClusterSpec.Type.content,
ClusterSpec.Id.from("id2"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
activate(provisioner.prepare(app2, cluster2, Capacity.fromCount(2, new NodeResources(2, 8, 50, 1)), 1, null), app2, provisioner);
ApplicationId app3 = ApplicationId.from(TenantName.from("tenant3"), ApplicationName.from("application3"), InstanceName.from("instance3"));
ClusterSpec cluster3 = ClusterSpec.request(ClusterSpec.Type.content,
ClusterSpec.Id.from("id3"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
activate(provisioner.prepare(app3, cluster3, Capacity.fromCount(2, new NodeResources(1, 4, 100, 1), false, true), 1, null), app3, provisioner);
List<Node> largeNodes = new ArrayList<>();
@@ -176,7 +176,7 @@ public class MockNodeRepository extends NodeRepository {
ClusterSpec cluster4 = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("id4"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
activate(provisioner.prepare(app4, cluster4, Capacity.fromCount(2, new NodeResources(10, 48, 500, 1), false, true), 1, null), app4, provisioner);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
index f15b7e4220b..66a883aec2e 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
@@ -78,7 +78,8 @@ class AutoscalingTester {
return ClusterSpec.request(type,
ClusterSpec.Id.from(clusterId),
Version.fromString("7"),
- false);
+ false,
+ Optional.empty());
}
public void deploy(ApplicationId application, ClusterSpec cluster, ClusterResources resources) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
index c293a3436b8..d0179cdda00 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
@@ -57,7 +57,7 @@ public class FailedExpirerTest {
ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("node-admin"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
private static final Capacity tenantHostApplicationCapacity = Capacity.fromRequiredNodeType(NodeType.host);
@@ -144,7 +144,7 @@ public class FailedExpirerTest {
.withNode(NodeType.proxy, FailureScenario.defaultFlavor, "proxy3")
.setReady("proxy1", "proxy2", "proxy3")
.allocate( ApplicationId.from("vespa", "zone-app", "default"),
- ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("routing"), Version.fromString("6.42"), false),
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("routing"), Version.fromString("6.42"), false, Optional.empty()),
Capacity.fromRequiredNodeType(NodeType.proxy))
.failNode(1, "proxy1");
@@ -326,8 +326,8 @@ public class FailedExpirerTest {
ClusterSpec clusterSpec = ClusterSpec.request(clusterType,
ClusterSpec.Id.from("test"),
Version.fromString("6.42"),
- false
- );
+ false,
+ Optional.empty());
Capacity capacity = Capacity.fromCount(hostname.length, Optional.of(flavor), false, true);
return allocate(applicationId, clusterSpec, capacity);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
index f9c2ef90bbd..b617a2dcf85 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
@@ -28,6 +28,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
@@ -54,7 +55,7 @@ public class InactiveAndFailedExpirerTest {
List<Node> nodes = tester.makeReadyNodes(2, nodeResources);
// Allocate then deallocate 2 nodes
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
List<HostSpec> preparedNodes = tester.prepare(applicationId, cluster, Capacity.fromCount(2, nodeResources), 1);
tester.activate(applicationId, new HashSet<>(preparedNodes));
assertEquals(2, tester.getNodes(applicationId, Node.State.active).size());
@@ -95,7 +96,7 @@ public class InactiveAndFailedExpirerTest {
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content,
ClusterSpec.Id.from("test"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
List<HostSpec> preparedNodes = tester.prepare(applicationId, cluster, Capacity.fromCount(2, nodeResources), 1);
tester.activate(applicationId, new HashSet<>(preparedNodes));
assertEquals(2, tester.getNodes(applicationId, Node.State.active).size());
@@ -122,7 +123,7 @@ public class InactiveAndFailedExpirerTest {
public void node_that_wants_to_retire_is_moved_to_parked() throws OrchestrationException {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build();
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"),
- Version.fromString("6.42"), false);
+ Version.fromString("6.42"), false, Optional.empty());
tester.makeReadyNodes(5, nodeResources);
// Allocate two nodes
@@ -178,7 +179,7 @@ public class InactiveAndFailedExpirerTest {
// Allocate then deallocate a node
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build();
tester.makeReadyNodes(1, nodeResources);
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
List<HostSpec> preparedNodes = tester.prepare(testerId, cluster, Capacity.fromCount(2, nodeResources), 1);
tester.activate(testerId, new HashSet<>(preparedNodes));
assertEquals(1, tester.getNodes(testerId, Node.State.active).size());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java
index 7e8fcddb1ae..a3c95e1a825 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java
@@ -17,6 +17,7 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -159,7 +160,7 @@ public class LoadBalancerExpirerTest {
List<HostSpec> hosts = new ArrayList<>();
for (var cluster : clusters) {
hosts.addAll(tester.prepare(application, ClusterSpec.request(ClusterSpec.Type.container, cluster,
- Vtag.currentVersion, false),
+ Vtag.currentVersion, false, Optional.empty()),
2, 1,
new NodeResources(1, 4, 10, 0.3)));
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java
index 321812497bd..8daec1d641e 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java
@@ -26,7 +26,6 @@ import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver;
import com.yahoo.vespa.orchestrator.Orchestrator;
import com.yahoo.vespa.orchestrator.status.HostInfo;
import com.yahoo.vespa.orchestrator.status.HostStatus;
-import com.yahoo.vespa.service.monitor.ServiceModel;
import com.yahoo.vespa.service.monitor.ServiceMonitor;
import org.junit.Test;
@@ -97,9 +96,7 @@ public class MetricsReporterTest {
when(orchestrator.getHostResolver()).thenReturn(hostName ->
Optional.of(HostInfo.createSuspended(HostStatus.ALLOWED_TO_BE_DOWN, Instant.ofEpochSecond(1)))
);
- ServiceModel serviceModel = mock(ServiceModel.class);
- when(serviceMonitor.getServiceModelSnapshot()).thenReturn(serviceModel);
- when(serviceModel.getServiceInstancesByHostName()).thenReturn(Map.of());
+ when(serviceMonitor.getServicesByHostname()).thenReturn(Map.of());
TestMetric metric = new TestMetric();
MetricsReporter metricsReporter = new MetricsReporter(
@@ -146,9 +143,7 @@ public class MetricsReporterTest {
Orchestrator orchestrator = mock(Orchestrator.class);
ServiceMonitor serviceMonitor = mock(ServiceMonitor.class);
when(orchestrator.getHostResolver()).thenReturn(hostName -> Optional.of(HostInfo.createNoRemarks()));
- ServiceModel serviceModel = mock(ServiceModel.class);
- when(serviceMonitor.getServiceModelSnapshot()).thenReturn(serviceModel);
- when(serviceModel.getServiceInstancesByHostName()).thenReturn(Map.of());
+ when(serviceMonitor.getServicesByHostname()).thenReturn(Map.of());
TestMetric metric = new TestMetric();
ManualClock clock = new ManualClock();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
index 033ddcd827e..e0e0a320763 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
@@ -89,8 +89,8 @@ public class NodeFailTester {
tester.createHostNodes(3);
// Create applications
- ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
- ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
+ ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
Capacity capacity1 = Capacity.fromCount(5, nodeResources, false, true);
Capacity capacity2 = Capacity.fromCount(7, nodeResources, false, true);
@@ -120,9 +120,9 @@ public class NodeFailTester {
}
// Create applications
- ClusterSpec clusterNodeAdminApp = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("node-admin"), Version.fromString("6.42"), false);
- ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false);
- ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false);
+ ClusterSpec clusterNodeAdminApp = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("node-admin"), Version.fromString("6.42"), false, Optional.empty());
+ ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false, Optional.empty());
+ ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false, Optional.empty());
Capacity allHosts = Capacity.fromRequiredNodeType(NodeType.host);
Capacity capacity1 = Capacity.fromCount(3, new NodeResources(1, 4, 10, 0.3), false, true);
Capacity capacity2 = Capacity.fromCount(5, new NodeResources(1, 4, 10, 0.3), false, true);
@@ -154,7 +154,7 @@ public class NodeFailTester {
ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("test"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
tester.activate(app1, clusterApp1, allNodes);
assertEquals(count, tester.nodeRepository.getNodes(nodeType, Node.State.active).size());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
index 22d7f03c449..e2745760637 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
@@ -134,9 +134,9 @@ public class OperatorChangeApplicationMaintainerTest {
final ApplicationId app1 = ApplicationId.from(TenantName.from("foo1"), ApplicationName.from("bar"), InstanceName.from("fuz"));
final ApplicationId app2 = ApplicationId.from(TenantName.from("foo2"), ApplicationName.from("bar"), InstanceName.from("fuz"));
final ApplicationId app3 = ApplicationId.from(TenantName.from("vespa-hosted"), ApplicationName.from("routing"), InstanceName.from("default"));
- final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
- final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
- final ClusterSpec clusterApp3 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("routing"), Version.fromString("6.42"), false);
+ final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
+ final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
+ final ClusterSpec clusterApp3 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("routing"), Version.fromString("6.42"), false, Optional.empty());
final int wantedNodesApp1 = 5;
final int wantedNodesApp2 = 7;
final int wantedNodesApp3 = 2;
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
index 913b8b53c46..5c87684da76 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
@@ -244,8 +244,8 @@ public class PeriodicApplicationMaintainerTest {
final NodeResources nodeResources = new NodeResources(2, 8, 50, 1);
final ApplicationId app1 = ApplicationId.from(TenantName.from("foo1"), ApplicationName.from("bar"), InstanceName.from("fuz"));
final ApplicationId app2 = ApplicationId.from(TenantName.from("foo2"), ApplicationName.from("bar"), InstanceName.from("fuz"));
- final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
- final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
+ final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
final int wantedNodesApp1 = 5;
final int wantedNodesApp2 = 7;
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java
index d0c678bdf45..d80aad7cc25 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java
@@ -129,7 +129,7 @@ public class RebalancerTest {
}
private ClusterSpec clusterSpec(String clusterId) {
- return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from(clusterId), Version.fromString("6.42"), false);
+ return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from(clusterId), Version.fromString("6.42"), false, Optional.empty());
}
private ApplicationId makeApplicationId(String tenant, String appName) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java
index 96c3cc09b6b..83e41211da9 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java
@@ -61,7 +61,7 @@ public class ReservationExpirerTest {
assertEquals(2, nodeRepository.getNodes(NodeType.tenant, Node.State.dirty).size());
nodeRepository.setReady(nodes, Agent.system, getClass().getSimpleName());
ApplicationId applicationId = new ApplicationId.Builder().tenant("foo").applicationName("bar").instanceName("fuz").build();
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
provisioner.prepare(applicationId, cluster, Capacity.fromCount(2, new NodeResources(2, 8, 50, 1)), 1, null);
assertEquals(2, nodeRepository.getNodes(NodeType.tenant, Node.State.reserved).size());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
index bdbe046fbdf..c82cbafaaa1 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
@@ -86,7 +86,7 @@ public class RetiredExpirerTest {
// Allocate content cluster of sizes 7 -> 2 -> 3:
// Should end up with 3 nodes in the cluster (one previously retired), and 4 retired
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
int wantedNodes;
activate(applicationId, cluster, wantedNodes=7, 1, provisioner);
activate(applicationId, cluster, wantedNodes=2, 1, provisioner);
@@ -117,7 +117,7 @@ public class RetiredExpirerTest {
ApplicationId applicationId = ApplicationId.from(TenantName.from("foo"), ApplicationName.from("bar"), InstanceName.from("fuz"));
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
activate(applicationId, cluster, 8, 8, provisioner);
activate(applicationId, cluster, 2, 2, provisioner);
assertEquals(8, nodeRepository.getNodes(applicationId, Node.State.active).size());
@@ -148,7 +148,7 @@ public class RetiredExpirerTest {
// Allocate content cluster of sizes 7 -> 2 -> 3:
// Should end up with 3 nodes in the cluster (one previously retired), and 4 retired
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty());
int wantedNodes;
activate(applicationId, cluster, wantedNodes=7, 1, provisioner);
activate(applicationId, cluster, wantedNodes=2, 1, provisioner);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
index 1e59add8304..cb2e3f65c40 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
@@ -52,7 +52,7 @@ public class DockerProvisioningTest {
Version wantedVespaVersion = Version.fromString("6.39");
int nodeCount = 7;
List<HostSpec> hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false, Optional.empty()),
nodeCount, 1, dockerFlavor);
tester.activate(application1, new HashSet<>(hosts));
@@ -63,7 +63,7 @@ public class DockerProvisioningTest {
// Upgrade Vespa version on nodes
Version upgradedWantedVespaVersion = Version.fromString("6.40");
List<HostSpec> upgradedHosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), upgradedWantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), upgradedWantedVespaVersion, false, Optional.empty()),
nodeCount, 1, dockerFlavor);
tester.activate(application1, new HashSet<>(upgradedHosts));
NodeList upgradedNodes = tester.getNodes(application1, Node.State.active);
@@ -85,7 +85,7 @@ public class DockerProvisioningTest {
Version wantedVespaVersion = Version.fromString("6.39");
int nodeCount = 7;
List<HostSpec> nodes = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false, Optional.empty()),
nodeCount, 1, dockerFlavor);
try {
tester.activate(application1, new HashSet<>(nodes));
@@ -94,13 +94,13 @@ public class DockerProvisioningTest {
// Activate the zone-app, thereby allocating the parents
List<HostSpec> hosts = tester.prepare(zoneApplication,
- ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("zone-app"), wantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("zone-app"), wantedVespaVersion, false, Optional.empty()),
Capacity.fromRequiredNodeType(NodeType.host), 1);
tester.activate(zoneApplication, hosts);
// Try allocating tenants again
nodes = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false, Optional.empty()),
nodeCount, 1, dockerFlavor);
tester.activate(application1, new HashSet<>(nodes));
@@ -124,14 +124,14 @@ public class DockerProvisioningTest {
Version wantedVespaVersion = Version.fromString("6.39");
List<HostSpec> nodes = tester.prepare(application2_1,
- ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false, Optional.empty()),
6, 1, resources);
assertHostSpecParentReservation(nodes, Optional.empty(), tester); // We do not get nodes on hosts reserved to tenant1
tester.activate(application2_1, nodes);
try {
tester.prepare(application2_2,
- ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false, Optional.empty()),
5, 1, resources);
fail("Expected exception");
}
@@ -140,7 +140,7 @@ public class DockerProvisioningTest {
}
nodes = tester.prepare(application1_1,
- ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false),
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false, Optional.empty()),
10, 1, resources);
assertHostSpecParentReservation(nodes, Optional.of(tenant1), tester);
tester.activate(application1_1, nodes);
@@ -262,7 +262,7 @@ public class DockerProvisioningTest {
ApplicationId application1 = tester.makeApplicationId();
tester.makeReadyVirtualDockerNodes(1, dockerFlavor, "dockerHost");
- List<HostSpec> hosts = tester.prepare(application1, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false), 1, 1, dockerFlavor);
+ List<HostSpec> hosts = tester.prepare(application1, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false, Optional.empty()), 1, 1, dockerFlavor);
tester.activate(application1, new HashSet<>(hosts));
NodeList nodes = tester.getNodes(application1, Node.State.active);
@@ -280,7 +280,7 @@ public class DockerProvisioningTest {
tester.makeReadyVirtualDockerNodes(1, dockerFlavor, "dockerHost2");
List<HostSpec> hosts = tester.prepare(application1, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"),
- Version.fromString("6.42"), false), 2, 1,
+ Version.fromString("6.42"), false, Optional.empty()), 2, 1,
dockerFlavor.with(NodeResources.StorageType.remote));
}
catch (OutOfCapacityException e) {
@@ -294,7 +294,7 @@ public class DockerProvisioningTest {
private void prepareAndActivate(ApplicationId application, int nodeCount, boolean exclusive, ProvisioningTester tester) {
Set<HostSpec> hosts = new HashSet<>(tester.prepare(application,
- ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.39"), exclusive),
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.39"), exclusive, Optional.empty()),
Capacity.fromCount(nodeCount, Optional.of(dockerFlavor), false, true),
1));
tester.activate(application, hosts);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java
index 53fecbf6095..7eb379aa404 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java
@@ -343,7 +343,7 @@ public class DynamicDockerAllocationTest {
tester.deployZoneApp();
ApplicationId application = tester.makeApplicationId();
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false, Optional.empty());
NodeResources resources = new NodeResources(1, 4, 10, 1, NodeResources.DiskSpeed.any);
List<HostSpec> hosts = tester.prepare(application, cluster, 2, 1, resources);
@@ -360,7 +360,7 @@ public class DynamicDockerAllocationTest {
tester.deployZoneApp();
ApplicationId application = tester.makeApplicationId();
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false, Optional.empty());
NodeResources resources = new NodeResources(1, 4, 10, 1, requestDiskSpeed);
try {
@@ -382,7 +382,7 @@ public class DynamicDockerAllocationTest {
tester.deployZoneApp();
ApplicationId application = tester.makeApplicationId();
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false, Optional.empty());
NodeResources resources = new NodeResources(1, 4, 10, 1, NodeResources.DiskSpeed.fast);
List<HostSpec> hosts = tester.prepare(application, cluster, 4, 1, resources);
@@ -401,7 +401,7 @@ public class DynamicDockerAllocationTest {
tester.deployZoneApp();
ApplicationId application = tester.makeApplicationId();
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false, Optional.empty());
List<HostSpec> hosts1 = tester.prepare(application, cluster, Capacity.fromCount(2, Optional.of(NodeResources.fromLegacyName("d-2-8-50")), false, true), 1);
tester.activate(application, hosts1);
@@ -466,7 +466,7 @@ public class DynamicDockerAllocationTest {
}
private ClusterSpec clusterSpec(String clusterId) {
- return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from(clusterId), Version.fromString("6.42"), false);
+ return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from(clusterId), Version.fromString("6.42"), false, Optional.empty());
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
index 8706661f261..2868edeaa58 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
@@ -1,10 +1,8 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.provisioning;
-import com.google.common.collect.ImmutableSet;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.Flavor;
@@ -23,6 +21,7 @@ import org.junit.Test;
import java.time.Instant;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -146,7 +145,7 @@ public class DynamicDockerProvisionTest {
}
private static ClusterSpec clusterSpec(String clusterId) {
- return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from(clusterId), Version.fromString("6.42"), false);
+ return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from(clusterId), Version.fromString("6.42"), false, Optional.empty());
}
@SuppressWarnings("unchecked")
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java
index 0ad7d37d13b..d83ed0c12dc 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/InPlaceResizeProvisionTest.java
@@ -18,6 +18,7 @@ import org.junit.Test;
import java.util.Collection;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -51,9 +52,9 @@ public class InPlaceResizeProvisionTest {
private static final NodeResources mediumResources = new NodeResources(4, 8, 16, 1, NodeResources.DiskSpeed.any, NodeResources.StorageType.any);
private static final NodeResources largeResources = new NodeResources(8, 16, 32, 1, NodeResources.DiskSpeed.any, NodeResources.StorageType.any);
- private static final ClusterSpec container1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1"), Version.fromString("7.157.9"), false);
- private static final ClusterSpec container2 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container2"), Version.fromString("7.157.9"), false);
- private static final ClusterSpec content1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("content1"), Version.fromString("7.157.9"), false);
+ private static final ClusterSpec container1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1"), Version.fromString("7.157.9"), false, Optional.empty());
+ private static final ClusterSpec container2 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container2"), Version.fromString("7.157.9"), false, Optional.empty());
+ private static final ClusterSpec content1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("content1"), Version.fromString("7.157.9"), false, Optional.empty());
private final InMemoryFlagSource flagSource = new InMemoryFlagSource();
private final ProvisioningTester tester = new ProvisioningTester.Builder()
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java
index ee9a582c4db..d4037c94a45 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java
@@ -24,6 +24,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -199,8 +200,9 @@ public class LoadBalancerProvisionerTest {
assertEquals(List.of(), tester.nodeRepository().loadBalancers(app1).asList());
}
+ // TODO(mpolden): Remove when ClusterSpec with combined type rejects empty combinedId
@Test
- public void provision_load_balancer_combined_cluster() {
+ public void provision_load_balancer_combined_cluster_without_id() {
Supplier<List<LoadBalancer>> lbs = () -> tester.nodeRepository().loadBalancers(app1).asList();
ClusterSpec.Id cluster = ClusterSpec.Id.from("foo");
@@ -211,6 +213,18 @@ public class LoadBalancerProvisionerTest {
assertSame(LoadBalancer.State.active, lbs.get().get(0).state());
}
+ @Test
+ public void provision_load_balancer_combined_cluster() {
+ Supplier<List<LoadBalancer>> lbs = () -> tester.nodeRepository().loadBalancers(app1).asList();
+ var combinedId = ClusterSpec.Id.from("container1");
+ var nodes = prepare(app1, clusterRequest(ClusterSpec.Type.combined, ClusterSpec.Id.from("content1"), Optional.of(combinedId)));
+ assertEquals(1, lbs.get().size());
+ assertEquals("Prepare provisions load balancer with reserved nodes", 2, lbs.get().get(0).instance().reals().size());
+ tester.activate(app1, nodes);
+ assertSame(LoadBalancer.State.active, lbs.get().get(0).state());
+ assertEquals(combinedId, lbs.get().get(0).id().cluster());
+ }
+
private void dirtyNodesOf(ApplicationId application) {
tester.nodeRepository().setDirty(tester.nodeRepository().getNodes(application), Agent.system, this.getClass().getSimpleName());
}
@@ -254,7 +268,11 @@ public class LoadBalancerProvisionerTest {
}
private static ClusterSpec clusterRequest(ClusterSpec.Type type, ClusterSpec.Id id) {
- return ClusterSpec.request(type, id, Version.fromString("6.42"), false);
+ return clusterRequest(type, id, Optional.empty());
+ }
+
+ private static ClusterSpec clusterRequest(ClusterSpec.Type type, ClusterSpec.Id id, Optional<ClusterSpec.Id> combinedId) {
+ return ClusterSpec.request(type, id, Version.fromString("6.42"), false, combinedId);
}
private static <T> T get(Set<T> set, int position) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
index 6ab027cd143..549441eb9e6 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
@@ -204,7 +204,7 @@ public class MultigroupProvisioningTest {
assertEquals("No additional groups are retained containing retired nodes", wantedGroups, allGroups.size());
}
- private ClusterSpec cluster() { return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false); }
+ private ClusterSpec cluster() { return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Optional.empty()); }
private Set<HostSpec> prepare(ApplicationId application, Capacity capacity, int groupCount, ProvisioningTester tester) {
return new HashSet<>(tester.prepare(application, cluster(), capacity, groupCount));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
index f0bd43b6bae..5cd31f00895 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
@@ -18,6 +18,7 @@ import java.time.Duration;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
@@ -38,7 +39,7 @@ public class NodeTypeProvisioningTest {
private final ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("test"),
Version.fromString("6.42"),
- false);
+ false, Optional.empty());
@Before
public void setup() {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
index 166d5a1f654..1ab94e852ba 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
@@ -3,20 +3,17 @@ package com.yahoo.vespa.hosted.provision.provisioning;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostFilter;
import com.yahoo.config.provision.HostSpec;
-import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.OutOfCapacityException;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.hosted.provision.Node;
@@ -33,7 +30,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.UUID;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -477,7 +473,7 @@ public class ProvisioningTest {
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content,
ClusterSpec.Id.from("music"),
new com.yahoo.component.Version(4, 5, 6),
- false);
+ false, Optional.empty());
tester.prepare(application, cluster, Capacity.fromCount(5, Optional.empty(), false, false), 1);
// No exception; Success
}
@@ -514,7 +510,7 @@ public class ProvisioningTest {
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content,
ClusterSpec.Id.from("music"),
new com.yahoo.component.Version(4, 5, 6),
- false);
+ false, Optional.empty());
tester.activate(application, tester.prepare(application, cluster, capacity, 1));
assertEquals(5, NodeList.copyOf(tester.nodeRepository().getNodes(application, Node.State.active)).not().retired().size());
assertEquals(0, NodeList.copyOf(tester.nodeRepository().getNodes(application, Node.State.active)).retired().size());
@@ -691,10 +687,10 @@ public class ProvisioningTest {
int content1Size, boolean required, NodeResources nodeResources, Version wantedVersion,
ProvisioningTester tester) {
// "deploy prepare" with a two container clusters and a storage cluster having of two groups
- ClusterSpec containerCluster0 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container0"), wantedVersion, false);
- ClusterSpec containerCluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1"), wantedVersion, false);
- ClusterSpec contentCluster0 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content0"), wantedVersion, false);
- ClusterSpec contentCluster1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content1"), wantedVersion, false);
+ ClusterSpec containerCluster0 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container0"), wantedVersion, false, Optional.empty());
+ ClusterSpec containerCluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1"), wantedVersion, false, Optional.empty());
+ ClusterSpec contentCluster0 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content0"), wantedVersion, false, Optional.empty());
+ ClusterSpec contentCluster1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content1"), wantedVersion, false, Optional.empty());
Set<HostSpec> container0 = prepare(application, containerCluster0, container0Size, 1, required, nodeResources, tester);
Set<HostSpec> container1 = prepare(application, containerCluster1, container1Size, 1, required, nodeResources, tester);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
index 85a6ed31073..03d0298900a 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
@@ -158,7 +158,7 @@ public class ProvisioningTester {
}
public void prepareAndActivateInfraApplication(ApplicationId application, NodeType nodeType, Version version) {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from(nodeType.toString()), version, false);
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from(nodeType.toString()), version, false, Optional.empty());
Capacity capacity = Capacity.fromRequiredNodeType(nodeType);
List<HostSpec> hostSpecs = prepare(application, cluster, capacity, 1, true);
activate(application, hostSpecs);
@@ -412,7 +412,7 @@ public class ProvisioningTester {
ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("node-admin"),
Version.fromString("6.42"),
- false),
+ false, Optional.empty()),
Capacity.fromRequiredNodeType(NodeType.host),
1);
activate(applicationId, Set.copyOf(list));
@@ -420,7 +420,7 @@ public class ProvisioningTester {
public List<Node> deploy(ApplicationId application, Capacity capacity) {
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"),
- Version.fromString("6.42"), false);
+ Version.fromString("6.42"), false, Optional.empty());
List<HostSpec> prepared = prepare(application, cluster, capacity, 1);
activate(application, Set.copyOf(prepared));
return getNodes(application, Node.State.active).asList();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java
index f8c6e31cfd7..60616481665 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java
@@ -36,8 +36,8 @@ import static org.junit.Assert.assertNotNull;
public class VirtualNodeProvisioningTest {
private static final NodeResources flavor = new NodeResources(4, 8, 100, 1);
- private static final ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false);
- private static final ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"), false);
+ private static final ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false, Optional.empty());
+ private static final ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"), false, Optional.empty());
private ProvisioningTester tester = new ProvisioningTester.Builder().build();
private ApplicationId applicationId = tester.makeApplicationId();
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/InstanceLookupService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/InstanceLookupService.java
deleted file mode 100644
index 554b6e11501..00000000000
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/InstanceLookupService.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.orchestrator;
-
-import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
-import com.yahoo.vespa.applicationmodel.ApplicationInstance;
-import com.yahoo.vespa.applicationmodel.HostName;
-
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * @author oyving
- */
-public interface InstanceLookupService {
-
- Optional<ApplicationInstance> findInstanceById(ApplicationInstanceReference applicationInstanceReference);
- Optional<ApplicationInstance> findInstanceByHost(HostName hostName);
- Set<ApplicationInstanceReference> knownInstances();
-
-}
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 d0062966a6d..d4b55e92271 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorImpl.java
@@ -31,11 +31,12 @@ import com.yahoo.vespa.orchestrator.policy.HostedVespaClusterPolicy;
import com.yahoo.vespa.orchestrator.policy.HostedVespaPolicy;
import com.yahoo.vespa.orchestrator.policy.Policy;
import com.yahoo.vespa.orchestrator.status.ApplicationInstanceStatus;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
import com.yahoo.vespa.orchestrator.status.HostInfo;
import com.yahoo.vespa.orchestrator.status.HostInfos;
import com.yahoo.vespa.orchestrator.status.HostStatus;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
import com.yahoo.vespa.orchestrator.status.StatusService;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import java.io.IOException;
import java.time.Clock;
@@ -59,7 +60,7 @@ public class OrchestratorImpl implements Orchestrator {
private final Policy policy;
private final StatusService statusService;
- private final InstanceLookupService instanceLookupService;
+ private final ServiceMonitor serviceMonitor;
private final int serviceMonitorConvergenceLatencySeconds;
private final ClusterControllerClientFactory clusterControllerClientFactory;
private final Clock clock;
@@ -70,24 +71,26 @@ public class OrchestratorImpl implements Orchestrator {
public OrchestratorImpl(ClusterControllerClientFactory clusterControllerClientFactory,
StatusService statusService,
OrchestratorConfig orchestratorConfig,
- InstanceLookupService instanceLookupService,
+ ServiceMonitor serviceMonitor,
ConfigserverConfig configServerConfig,
FlagSource flagSource)
{
- this(new HostedVespaPolicy(new HostedVespaClusterPolicy(), clusterControllerClientFactory, new ApplicationApiFactory(configServerConfig.zookeeperserver().size())),
- clusterControllerClientFactory,
- statusService,
- instanceLookupService,
- orchestratorConfig.serviceMonitorConvergenceLatencySeconds(),
- Clock.systemUTC(),
- new ApplicationApiFactory(configServerConfig.zookeeperserver().size()),
- flagSource);
+ this(new HostedVespaPolicy(new HostedVespaClusterPolicy(),
+ clusterControllerClientFactory,
+ new ApplicationApiFactory(configServerConfig.zookeeperserver().size())),
+ clusterControllerClientFactory,
+ statusService,
+ serviceMonitor,
+ orchestratorConfig.serviceMonitorConvergenceLatencySeconds(),
+ Clock.systemUTC(),
+ new ApplicationApiFactory(configServerConfig.zookeeperserver().size()),
+ flagSource);
}
public OrchestratorImpl(Policy policy,
ClusterControllerClientFactory clusterControllerClientFactory,
StatusService statusService,
- InstanceLookupService instanceLookupService,
+ ServiceMonitor serviceMonitor,
int serviceMonitorConvergenceLatencySeconds,
Clock clock,
ApplicationApiFactory applicationApiFactory,
@@ -97,7 +100,7 @@ public class OrchestratorImpl implements Orchestrator {
this.clusterControllerClientFactory = clusterControllerClientFactory;
this.statusService = statusService;
this.serviceMonitorConvergenceLatencySeconds = serviceMonitorConvergenceLatencySeconds;
- this.instanceLookupService = instanceLookupService;
+ this.serviceMonitor = serviceMonitor;
this.clock = clock;
this.applicationApiFactory = applicationApiFactory;
this.retireWithPermanentlyDownFlag = Flags.RETIRE_WITH_PERMANENTLY_DOWN.bindTo(flagSource);
@@ -105,7 +108,10 @@ public class OrchestratorImpl implements Orchestrator {
@Override
public Host getHost(HostName hostName) throws HostNameNotFoundException {
- ApplicationInstance applicationInstance = getApplicationInstance(hostName);
+ ApplicationInstance applicationInstance = serviceMonitor
+ .getApplicationNarrowedTo(hostName)
+ .orElseThrow(() -> new HostNameNotFoundException(hostName));
+
List<ServiceInstance> serviceInstances = applicationInstance
.serviceClusters().stream()
.flatMap(cluster -> cluster.serviceInstances().stream())
@@ -113,7 +119,6 @@ public class OrchestratorImpl implements Orchestrator {
.collect(Collectors.toList());
HostInfo hostInfo = statusService.getHostInfo(applicationInstance.reference(), hostName);
- HostStatus hostStatus = getNodeStatus(applicationInstance.reference(), hostName);
return new Host(hostName, hostInfo, applicationInstance.reference(), serviceInstances);
}
@@ -125,17 +130,17 @@ public class OrchestratorImpl implements Orchestrator {
@Override
public Function<HostName, Optional<HostInfo>> getHostResolver() {
- return hostName -> instanceLookupService.findInstanceByHost(hostName)
- .map(application -> statusService.getHostInfo(application.reference(), hostName));
+ return hostName -> serviceMonitor
+ .getApplication(hostName)
+ .map(application -> statusService.getHostInfo(application.reference(), hostName));
}
@Override
public void setNodeStatus(HostName hostName, HostStatus status) throws OrchestrationException {
ApplicationInstanceReference reference = getApplicationInstance(hostName).reference();
OrchestratorContext context = OrchestratorContext.createContextForSingleAppOp(clock);
- try (MutableStatusRegistry statusRegistry = statusService
- .lockApplicationInstance_forCurrentThreadOnly(context, reference)) {
- statusRegistry.setHostState(hostName, status);
+ try (ApplicationLock lock = statusService.lockApplication(context, reference)) {
+ lock.setHostState(hostName, status);
}
}
@@ -163,9 +168,8 @@ public class OrchestratorImpl implements Orchestrator {
ApplicationInstance appInstance = getApplicationInstance(hostName);
OrchestratorContext context = OrchestratorContext.createContextForSingleAppOp(clock);
- try (MutableStatusRegistry statusRegistry = statusService
- .lockApplicationInstance_forCurrentThreadOnly(context, appInstance.reference())) {
- HostStatus currentHostState = statusRegistry.getHostInfos().getOrNoRemarks(hostName).status();
+ try (ApplicationLock lock = statusService.lockApplication(context, appInstance.reference())) {
+ HostStatus currentHostState = lock.getHostInfos().getOrNoRemarks(hostName).status();
if (currentHostState == HostStatus.NO_REMARKS) {
return;
}
@@ -176,11 +180,11 @@ public class OrchestratorImpl implements Orchestrator {
// 2. The whole application is down: the content cluster states are set to maintenance,
// and the host may be taken down manually at any moment.
if (currentHostState == HostStatus.PERMANENTLY_DOWN ||
- statusRegistry.getStatus() == ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN) {
+ lock.getApplicationInstanceStatus() == ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN) {
return;
}
- policy.releaseSuspensionGrant(context.createSubcontextWithinLock(), appInstance, hostName, statusRegistry);
+ policy.releaseSuspensionGrant(context.createSubcontextWithinLock(), appInstance, hostName, lock);
}
}
@@ -200,10 +204,8 @@ public class OrchestratorImpl implements Orchestrator {
.with(FetchVector.Dimension.HOSTNAME, hostName.s())
.value();
OrchestratorContext context = OrchestratorContext.createContextForSingleAppOp(clock, usePermanentlyDownStatus);
- try (MutableStatusRegistry statusRegistry = statusService
- .lockApplicationInstance_forCurrentThreadOnly(context, appInstance.reference())) {
- ApplicationApi applicationApi = applicationApiFactory.create(nodeGroup, statusRegistry,
- clusterControllerClientFactory);
+ try (ApplicationLock lock = statusService.lockApplication(context, appInstance.reference())) {
+ ApplicationApi applicationApi = applicationApiFactory.create(nodeGroup, lock, clusterControllerClientFactory);
policy.acquirePermissionToRemove(context.createSubcontextWithinLock(), applicationApi);
}
@@ -218,23 +220,22 @@ public class OrchestratorImpl implements Orchestrator {
void suspendGroup(OrchestratorContext context, NodeGroup nodeGroup) throws HostStateChangeDeniedException {
ApplicationInstanceReference applicationReference = nodeGroup.getApplicationReference();
- try (MutableStatusRegistry hostStatusRegistry =
- statusService.lockApplicationInstance_forCurrentThreadOnly(context, applicationReference)) {
- ApplicationInstanceStatus appStatus = hostStatusRegistry.getStatus();
+ try (ApplicationLock lock = statusService.lockApplication(context, applicationReference)) {
+ ApplicationInstanceStatus appStatus = lock.getApplicationInstanceStatus();
if (appStatus == ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN) {
return;
}
- ApplicationApi applicationApi = applicationApiFactory.create(nodeGroup, hostStatusRegistry,
- clusterControllerClientFactory);
+ ApplicationApi applicationApi = applicationApiFactory.create(
+ nodeGroup, lock, clusterControllerClientFactory);
policy.grantSuspensionRequest(context.createSubcontextWithinLock(), applicationApi);
}
}
@Override
public ApplicationInstanceStatus getApplicationInstanceStatus(ApplicationId appId) throws ApplicationIdNotFoundException {
- ApplicationInstanceReference appRef = OrchestratorUtil.toApplicationInstanceReference(appId, instanceLookupService);
- return statusService.getApplicationInstanceStatus(appRef);
+ ApplicationInstanceReference reference = OrchestratorUtil.toApplicationInstanceReference(appId, serviceMonitor);
+ return statusService.getApplicationInstanceStatus(reference);
}
@Override
@@ -351,18 +352,17 @@ public class OrchestratorImpl implements Orchestrator {
private void setApplicationStatus(ApplicationId appId, ApplicationInstanceStatus status)
throws ApplicationStateChangeDeniedException, ApplicationIdNotFoundException{
OrchestratorContext context = OrchestratorContext.createContextForSingleAppOp(clock);
- ApplicationInstanceReference appRef = OrchestratorUtil.toApplicationInstanceReference(appId, instanceLookupService);
- try (MutableStatusRegistry statusRegistry =
- statusService.lockApplicationInstance_forCurrentThreadOnly(context, appRef)) {
+ ApplicationInstanceReference reference = OrchestratorUtil.toApplicationInstanceReference(appId, serviceMonitor);
+ try (ApplicationLock lock = statusService.lockApplication(context, reference)) {
// Short-circuit if already in wanted state
- if (status == statusRegistry.getStatus()) return;
+ if (status == lock.getApplicationInstanceStatus()) return;
// Set content clusters for this application in maintenance on suspend
if (status == ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN) {
- ApplicationInstance application = getApplicationInstance(appRef);
+ ApplicationInstance application = getApplicationInstance(reference);
- HostInfos hostInfosSnapshot = statusRegistry.getHostInfos();
+ HostInfos hostInfosSnapshot = lock.getHostInfos();
// Mark it allowed to be down before we manipulate the clustercontroller
OrchestratorUtil.getHostsUsedByApplicationInstance(application)
@@ -370,14 +370,14 @@ public class OrchestratorImpl implements Orchestrator {
// This filter also ensures host status is not modified if a suspended host
// has status != ALLOWED_TO_BE_DOWN.
.filter(hostname -> !hostInfosSnapshot.getOrNoRemarks(hostname).status().isSuspended())
- .forEach(hostname -> statusRegistry.setHostState(hostname, HostStatus.ALLOWED_TO_BE_DOWN));
+ .forEach(hostname -> lock.setHostState(hostname, HostStatus.ALLOWED_TO_BE_DOWN));
// If the clustercontroller throws an error the nodes will be marked as allowed to be down
// and be set back up on next resume invocation.
setClusterStateInController(context.createSubcontextWithinLock(), application, ClusterControllerNodeState.MAINTENANCE);
}
- statusRegistry.setApplicationInstanceStatus(status);
+ lock.setApplicationInstanceStatus(status);
}
}
@@ -417,12 +417,13 @@ public class OrchestratorImpl implements Orchestrator {
}
private ApplicationInstance getApplicationInstance(HostName hostName) throws HostNameNotFoundException{
- return instanceLookupService.findInstanceByHost(hostName).orElseThrow(
+ return serviceMonitor.getApplication(hostName).orElseThrow(
() -> new HostNameNotFoundException(hostName));
}
- private ApplicationInstance getApplicationInstance(ApplicationInstanceReference appRef) throws ApplicationIdNotFoundException {
- return instanceLookupService.findInstanceById(appRef).orElseThrow(ApplicationIdNotFoundException::new);
+ private ApplicationInstance getApplicationInstance(ApplicationInstanceReference reference)
+ throws ApplicationIdNotFoundException {
+ return serviceMonitor.getApplication(reference).orElseThrow(ApplicationIdNotFoundException::new);
}
private static void sleep(long time, TimeUnit timeUnit) {
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorUtil.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorUtil.java
index 91da046840d..40a45627f4d 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorUtil.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/OrchestratorUtil.java
@@ -12,6 +12,7 @@ import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.applicationmodel.ServiceCluster;
import com.yahoo.vespa.applicationmodel.ServiceInstance;
import com.yahoo.vespa.applicationmodel.TenantId;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import java.util.Collection;
import java.util.List;
@@ -65,7 +66,7 @@ public class OrchestratorUtil {
private static final Pattern APPLICATION_INSTANCE_REFERENCE_REST_FORMAT_PATTERN = Pattern.compile("^([^:]+):(.+)$");
/** Returns an ApplicationInstanceReference constructed from the serialized format used in the REST API. */
- public static ApplicationInstanceReference parseAppInstanceReference(String restFormat) {
+ public static ApplicationInstanceReference parseApplicationInstanceReference(String restFormat) {
if (restFormat == null) {
throw new IllegalArgumentException("Could not construct instance id from null string");
}
@@ -84,26 +85,26 @@ public class OrchestratorUtil {
return applicationInstanceReference.tenantId() + ":" + applicationInstanceReference.applicationInstanceId();
}
-
- public static ApplicationInstanceReference toApplicationInstanceReference(ApplicationId appId,
- InstanceLookupService instanceLookupService)
+ public static ApplicationInstanceReference toApplicationInstanceReference(
+ ApplicationId applicationid,
+ ServiceMonitor serviceMonitor)
throws ApplicationIdNotFoundException {
- Set<ApplicationInstanceReference> appRefs = instanceLookupService.knownInstances();
- List<ApplicationInstanceReference> appRefList = appRefs.stream()
- .filter(a -> OrchestratorUtil.toApplicationId(a).equals(appId))
+ Set<ApplicationInstanceReference> references = serviceMonitor.getAllApplicationInstanceReferences();
+ List<ApplicationInstanceReference> referencesWithId = references.stream()
+ .filter(a -> OrchestratorUtil.toApplicationId(a).equals(applicationid))
.collect(Collectors.toList());
- if (appRefList.size() > 1) {
- String msg = String.format("ApplicationId '%s' was not unique but mapped to '%s'", appId, appRefList);
+ if (referencesWithId.size() > 1) {
+ String msg = String.format("ApplicationId '%s' was not unique but mapped to '%s'", applicationid, referencesWithId);
throw new ApplicationIdNotFoundException(msg);
}
- if (appRefList.size() == 0) {
+ if (referencesWithId.size() == 0) {
throw new ApplicationIdNotFoundException();
}
- return appRefList.get(0);
+ return referencesWithId.get(0);
}
public static ApplicationId toApplicationId(ApplicationInstanceReference appRef) {
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/ServiceMonitorInstanceLookupService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/ServiceMonitorInstanceLookupService.java
deleted file mode 100644
index 1a859cfacc5..00000000000
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/ServiceMonitorInstanceLookupService.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.orchestrator;
-
-import com.google.inject.Inject;
-import com.yahoo.vespa.applicationmodel.ApplicationInstance;
-import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
-import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.service.monitor.ServiceMonitor;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Uses slobrok data (a.k.a. heartbeat) to implement {@link InstanceLookupService}.
- *
- * @author bakksjo
- */
-public class ServiceMonitorInstanceLookupService implements InstanceLookupService {
-
- private final ServiceMonitor serviceMonitor;
-
- @Inject
- public ServiceMonitorInstanceLookupService(ServiceMonitor serviceMonitor) {
- this.serviceMonitor = serviceMonitor;
- }
-
- @Override
- public Optional<ApplicationInstance> findInstanceById(ApplicationInstanceReference applicationInstanceReference) {
- return serviceMonitor.getServiceModelSnapshot().getApplicationInstance(applicationInstanceReference);
- }
-
- @Override
- public Optional<ApplicationInstance> findInstanceByHost(HostName hostName) {
- return Optional.ofNullable(serviceMonitor.getServiceModelSnapshot().getApplicationsByHostName().get(hostName));
- }
-
- @Override
- public Set<ApplicationInstanceReference> knownInstances() {
- return serviceMonitor.getServiceModelSnapshot().getAllApplicationInstances().keySet();
- }
-
-}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiFactory.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiFactory.java
index 5e6ec59c28d..fb52eed2048 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiFactory.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiFactory.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.orchestrator.model;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
/**
* @author mpolden
@@ -16,9 +16,9 @@ public class ApplicationApiFactory {
}
public ApplicationApi create(NodeGroup nodeGroup,
- MutableStatusRegistry hostStatusService,
+ ApplicationLock lock,
ClusterControllerClientFactory clusterControllerClientFactory) {
- return new ApplicationApiImpl(nodeGroup, hostStatusService, clusterControllerClientFactory, numberOfConfigServers);
+ return new ApplicationApiImpl(nodeGroup, lock, clusterControllerClientFactory, numberOfConfigServers);
}
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImpl.java
index cf6946fa7f8..ccc89fa9191 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImpl.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/model/ApplicationApiImpl.java
@@ -12,7 +12,7 @@ import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.status.ApplicationInstanceStatus;
import com.yahoo.vespa.orchestrator.status.HostInfos;
import com.yahoo.vespa.orchestrator.status.HostStatus;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
import java.util.Collection;
import java.util.Comparator;
@@ -32,19 +32,19 @@ public class ApplicationApiImpl implements ApplicationApi {
private final ApplicationInstance applicationInstance;
private final NodeGroup nodeGroup;
- private final MutableStatusRegistry hostStatusService;
+ private final ApplicationLock lock;
private final List<ClusterApi> clusterInOrder;
private final HostInfos hostInfos;
public ApplicationApiImpl(NodeGroup nodeGroup,
- MutableStatusRegistry hostStatusService,
+ ApplicationLock lock,
ClusterControllerClientFactory clusterControllerClientFactory,
int numberOfConfigServers) {
this.applicationInstance = nodeGroup.getApplication();
this.nodeGroup = nodeGroup;
- this.hostStatusService = hostStatusService;
+ this.lock = lock;
Collection<HostName> hosts = getHostsUsedByApplicationInstance(applicationInstance);
- this.hostInfos = hostStatusService.getHostInfos();
+ this.hostInfos = lock.getHostInfos();
this.clusterInOrder = makeClustersInOrder(nodeGroup, hostInfos, clusterControllerClientFactory, numberOfConfigServers);
}
@@ -95,12 +95,12 @@ public class ApplicationApiImpl implements ApplicationApi {
@Override
public ApplicationInstanceStatus getApplicationStatus() {
- return hostStatusService.getStatus();
+ return lock.getApplicationInstanceStatus();
}
@Override
public void setHostState(OrchestratorContext context, HostName hostName, HostStatus status) {
- hostStatusService.setHostState(hostName, status);
+ lock.setHostState(hostName, status);
}
@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 f6a1e4f91f0..8b74d8a40ef 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
@@ -13,7 +13,7 @@ import com.yahoo.vespa.orchestrator.model.NodeGroup;
import com.yahoo.vespa.orchestrator.model.StorageNode;
import com.yahoo.vespa.orchestrator.status.ApplicationInstanceStatus;
import com.yahoo.vespa.orchestrator.status.HostStatus;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
/**
* @author oyving
@@ -113,9 +113,9 @@ public class HostedVespaPolicy implements Policy {
OrchestratorContext context,
ApplicationInstance applicationInstance,
HostName hostName,
- MutableStatusRegistry hostStatusService) throws HostStateChangeDeniedException {
+ ApplicationLock lock) throws HostStateChangeDeniedException {
NodeGroup nodeGroup = new NodeGroup(applicationInstance, hostName);
- ApplicationApi applicationApi = applicationApiFactory.create(nodeGroup, hostStatusService, clusterControllerClientFactory);
+ ApplicationApi applicationApi = applicationApiFactory.create(nodeGroup, lock, clusterControllerClientFactory);
releaseSuspensionGrant(context, applicationApi);
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java
index aa7636227c8..c410cda23a8 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/policy/Policy.java
@@ -5,8 +5,7 @@ import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.model.ApplicationApi;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
-import com.yahoo.vespa.orchestrator.status.StatusService;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
/**
* @author oyving
@@ -33,6 +32,6 @@ public interface Policy {
void releaseSuspensionGrant(
OrchestratorContext context, ApplicationInstance applicationInstance,
HostName hostName,
- MutableStatusRegistry hostStatusService) throws HostStateChangeDeniedException;
+ ApplicationLock hostStatusService) throws HostStateChangeDeniedException;
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/resources/InstanceResource.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/resources/InstanceResource.java
index fbb8f445db0..1cf2a2a4965 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/resources/InstanceResource.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/resources/InstanceResource.java
@@ -11,7 +11,6 @@ import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.applicationmodel.ServiceStatusInfo;
import com.yahoo.vespa.applicationmodel.ServiceType;
-import com.yahoo.vespa.orchestrator.InstanceLookupService;
import com.yahoo.vespa.orchestrator.OrchestratorUtil;
import com.yahoo.vespa.orchestrator.restapi.wire.SlobrokEntryResponse;
import com.yahoo.vespa.orchestrator.restapi.wire.WireHostInfo;
@@ -20,6 +19,7 @@ import com.yahoo.vespa.orchestrator.status.HostInfos;
import com.yahoo.vespa.orchestrator.status.StatusService;
import com.yahoo.vespa.service.manager.MonitorManager;
import com.yahoo.vespa.service.manager.UnionMonitorManager;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import com.yahoo.vespa.service.monitor.SlobrokApi;
import javax.inject.Inject;
@@ -33,12 +33,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.time.Instant;
import java.util.List;
-import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import static com.yahoo.vespa.orchestrator.OrchestratorUtil.getHostsUsedByApplicationInstance;
-import static com.yahoo.vespa.orchestrator.OrchestratorUtil.parseAppInstanceReference;
+import static com.yahoo.vespa.orchestrator.OrchestratorUtil.parseApplicationInstanceReference;
/**
* Provides a read-only API for looking into the current state as seen by the Orchestrator.
@@ -55,14 +54,14 @@ public class InstanceResource {
private final StatusService statusService;
private final SlobrokApi slobrokApi;
private final MonitorManager rootManager;
- private final InstanceLookupService instanceLookupService;
+ private final ServiceMonitor serviceMonitor;
@Inject
- public InstanceResource(@Component InstanceLookupService instanceLookupService,
+ public InstanceResource(@Component ServiceMonitor serviceMonitor,
@Component StatusService statusService,
@Component SlobrokApi slobrokApi,
@Component UnionMonitorManager rootManager) {
- this.instanceLookupService = instanceLookupService;
+ this.serviceMonitor = serviceMonitor;
this.statusService = statusService;
this.slobrokApi = slobrokApi;
this.rootManager = rootManager;
@@ -70,8 +69,8 @@ public class InstanceResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
- public Set<ApplicationInstanceReference> getAllInstances() {
- return instanceLookupService.knownInstances();
+ public List<ApplicationInstanceReference> getAllInstances() {
+ return serviceMonitor.getAllApplicationInstanceReferences().stream().sorted().collect(Collectors.toList());
}
@GET
@@ -81,7 +80,7 @@ public class InstanceResource {
ApplicationInstanceReference instanceId = parseInstanceId(instanceIdString);
ApplicationInstance applicationInstance
- = instanceLookupService.findInstanceById(instanceId)
+ = serviceMonitor.getApplication(instanceId)
.orElseThrow(() -> new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()));
HostInfos hostInfos = statusService.getHostInfosByApplicationResolver().apply(applicationInstance.reference());
@@ -153,7 +152,7 @@ public class InstanceResource {
static ApplicationInstanceReference parseInstanceId(String instanceIdString) {
try {
- return parseAppInstanceReference(instanceIdString);
+ return parseApplicationInstanceReference(instanceIdString);
} catch (IllegalArgumentException e) {
throwBadRequest(e.getMessage());
return null; // Necessary for compiler
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ApplicationLock.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ApplicationLock.java
new file mode 100644
index 00000000000..8883f78b693
--- /dev/null
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ApplicationLock.java
@@ -0,0 +1,36 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.orchestrator.status;
+
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
+
+/**
+ * The exclusive lock of an application in the status service, and methods to mutate host status
+ * and data structures guarded by such a lock.
+ *
+ * @author oyving
+ * @author Tony Vaagenes
+ * @author bakksjo
+ */
+public interface ApplicationLock extends AutoCloseable {
+
+ /** The reference of the locked application. */
+ ApplicationInstanceReference getApplicationInstanceReference();
+
+ /** Returns all host infos for this application. */
+ HostInfos getHostInfos();
+
+ /** Sets the state for the given host. */
+ void setHostState(HostName hostName, HostStatus status);
+
+ /** Returns the application status. */
+ ApplicationInstanceStatus getApplicationInstanceStatus();
+
+ /** Sets the orchestration status for the application instance. */
+ void setApplicationInstanceStatus(ApplicationInstanceStatus applicationInstanceStatus);
+
+ /** WARNING: Must not throw an exception. */
+ @Override
+ void close();
+
+}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/HostInfosServiceImpl.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/HostInfosServiceImpl.java
new file mode 100644
index 00000000000..2d6c3eb82a1
--- /dev/null
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/HostInfosServiceImpl.java
@@ -0,0 +1,128 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package com.yahoo.vespa.orchestrator.status;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.jdisc.Timer;
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.orchestrator.OrchestratorUtil;
+import com.yahoo.vespa.orchestrator.status.json.WireHostInfo;
+import org.apache.zookeeper.KeeperException.NoNodeException;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ * Handles all ZooKeeper data structures related to each active application, including HostInfo.
+ * Cache (if any) is above and not visible here.
+ *
+ * @author hakonhall
+ */
+public class HostInfosServiceImpl implements HostInfosService {
+ private static final Logger log = Logger.getLogger(HostInfosServiceImpl.class.getName());
+
+ private final Curator curator;
+ private final Timer timer;
+
+ HostInfosServiceImpl(Curator curator, Timer timer) {
+ this.curator = curator;
+ this.timer = timer;
+ }
+
+ @Override
+ public HostInfos getHostInfos(ApplicationInstanceReference reference) {
+ ApplicationId application = OrchestratorUtil.toApplicationId(reference);
+ String hostsRootPath = hostsPath(application);
+ if (uncheck(() -> curator.framework().checkExists().forPath(hostsRootPath)) == null) {
+ return new HostInfos();
+ } else {
+ List<String> hostnames = uncheck(() -> curator.framework().getChildren().forPath(hostsRootPath));
+ Map<HostName, HostInfo> hostInfos = hostnames.stream().collect(Collectors.toMap(
+ hostname -> new HostName(hostname),
+ hostname -> {
+ byte[] bytes = uncheck(() -> curator.framework().getData().forPath(hostsRootPath + "/" + hostname));
+ return WireHostInfo.deserialize(bytes);
+ }));
+ return new HostInfos(hostInfos);
+ }
+ }
+
+ @Override
+ public boolean setHostStatus(ApplicationInstanceReference reference, HostName hostname, HostStatus status) {
+ ApplicationId application = OrchestratorUtil.toApplicationId(reference);
+ String path = hostPath(application, hostname);
+
+ if (status == HostStatus.NO_REMARKS) {
+ return deleteNode_ignoreNoNodeException(path, "Host already has state NO_REMARKS, path = " + path);
+ }
+
+ Optional<HostInfo> currentHostInfo = uncheck(() -> readBytesFromZk(path)).map(WireHostInfo::deserialize);
+ if (currentHostInfo.isEmpty()) {
+ Instant suspendedSince = timer.currentTime();
+ HostInfo hostInfo = HostInfo.createSuspended(status, suspendedSince);
+ byte[] hostInfoBytes = WireHostInfo.serialize(hostInfo);
+ uncheck(() -> curator.framework().create().creatingParentsIfNeeded().forPath(path, hostInfoBytes));
+ } else if (currentHostInfo.get().status() == status) {
+ return false;
+ } else {
+ Instant suspendedSince = currentHostInfo.get().suspendedSince().orElseGet(timer::currentTime);
+ HostInfo hostInfo = HostInfo.createSuspended(status, suspendedSince);
+ byte[] hostInfoBytes = WireHostInfo.serialize(hostInfo);
+ uncheck(() -> curator.framework().setData().forPath(path, hostInfoBytes));
+ }
+
+ return true;
+ }
+
+ private Optional<byte[]> readBytesFromZk(String path) throws Exception {
+ try {
+ return Optional.of(curator.framework().getData().forPath(path));
+ } catch (NoNodeException e) {
+ return Optional.empty();
+ }
+ }
+
+ private boolean deleteNode_ignoreNoNodeException(String path, String debugLogMessageIfNotExists) {
+ try {
+ curator.framework().delete().forPath(path);
+ return true;
+ } catch (NoNodeException e) {
+ log.log(LogLevel.DEBUG, debugLogMessageIfNotExists, e);
+ return false;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String applicationPath(ApplicationId application) {
+ return "/vespa/host-status/" + application.serializedForm();
+ }
+
+ private static String hostsPath(ApplicationId application) {
+ return applicationPath(application) + "/hosts";
+ }
+
+ private static String hostPath(ApplicationId application, HostName hostname) {
+ return hostsPath(application) + "/" + hostname.s();
+ }
+
+ private <T> T uncheck(SupplierThrowingException<T> supplier) {
+ try {
+ return supplier.get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @FunctionalInterface
+ private interface SupplierThrowingException<T> {
+ T get() throws Exception;
+ }
+}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java
deleted file mode 100644
index 24da83364aa..00000000000
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/MutableStatusRegistry.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.orchestrator.status;
-
-import com.yahoo.vespa.applicationmodel.HostName;
-
-import java.util.Set;
-
-/**
- * Registry of the suspension and host statuses for an application instance.
- *
- * @author oyving
- * @author Tony Vaagenes
- * @author bakksjo
- */
-public interface MutableStatusRegistry extends AutoCloseable {
-
- /** Returns the status of this application. */
- ApplicationInstanceStatus getStatus();
-
- /** Returns a snapshot of all host infos for this application. */
- HostInfos getHostInfos();
-
- /** Sets the state for the given host. */
- void setHostState(HostName hostName, HostStatus status);
-
- /**
- * Sets the orchestration status for the application instance.
- */
- void setApplicationInstanceStatus(ApplicationInstanceStatus applicationInstanceStatus);
-
- /**
- * We don't want {@link AutoCloseable#close()} to throw an exception (what to do about it anyway?),
- * so we override it here to strip the exception from the signature.
- */
- @Override
- void close();
-
-}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java
index e2be5ec7eb6..0d4076f3d59 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/StatusService.java
@@ -24,29 +24,9 @@ public interface StatusService {
* Returns a mutable host status registry for a locked application instance. All operations performed on
* the returned registry are executed in the context of a lock, including read operations. Hence, multi-step
* operations (e.g. read-then-write) are guaranteed to be consistent.
- *
- * Some limitations/caveats apply for certain implementations, and since clients of this API must be aware of
- * these limitations/caveats when using those implementations, they are expressed here, at interface level
- * rather than at implementation level, because the interface represents the lowest common denominator
- * of guarantees offered by implementations. Specifically, it is the zookeeper-based implementation's semantics
- * that "leak through" in this spec. Now, to the specific caveats:
- *
- * Locking this application instance only guarantees that the holder is the only one that can mutate host statuses
- * for the application instance.
- * It is _not_ safe to assume that there is only one entity holding the lock for a given application instance
- * reference at any given time.
- *
- * You cannot have multiple locks in a single thread, even if they are for different application instances,
- * (i.e. different HostStatusRegistry instances). (This is due to a limitation in SessionFailRetryLoop.)
- *
- * While read-then-write-operations are consistent (i.e. the current value doesn't change between the read
- * and the write), it is possible that the lock is lost before it is explicitly released by the code. In
- * this case, subsequent mutating operations will fail, but previous mutating operations are NOT rolled back.
- * This may leave the registry in an inconsistent state (as judged by the client code).
*/
- MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(
- OrchestratorContext context,
- ApplicationInstanceReference applicationInstanceReference) throws UncheckedTimeoutException;
+ ApplicationLock lockApplication(OrchestratorContext context, ApplicationInstanceReference reference)
+ throws UncheckedTimeoutException;
/**
* Returns all application instances that are allowed to be down. The intention is to use this
@@ -68,5 +48,5 @@ public interface StatusService {
ApplicationInstanceStatus getApplicationInstanceStatus(ApplicationInstanceReference application);
/** Get host info for hostname in application. This is consistent if its lock is held. */
- HostInfo getHostInfo(ApplicationInstanceReference applicationInstanceReference, HostName hostName);
+ HostInfo getHostInfo(ApplicationInstanceReference reference, HostName hostName);
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkApplicationLock.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkApplicationLock.java
new file mode 100644
index 00000000000..479dc5062a8
--- /dev/null
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkApplicationLock.java
@@ -0,0 +1,114 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package com.yahoo.vespa.orchestrator.status;
+
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.curator.Curator;
+import org.apache.zookeeper.KeeperException;
+
+import java.util.logging.Logger;
+
+/**
+ * ZooKeeper implementation of {@link ApplicationLock}.
+ *
+ * @author hakonhall
+ */
+class ZkApplicationLock implements ApplicationLock {
+
+ private static final Logger log = Logger.getLogger(ZkApplicationLock.class.getName());
+
+ private final ZkStatusService statusService;
+ private final Curator curator;
+ private final Runnable onClose;
+ private final ApplicationInstanceReference reference;
+ private final boolean probe;
+ private final HostInfosCache hostInfosCache;
+
+ ZkApplicationLock(ZkStatusService statusService,
+ Curator curator,
+ Runnable onClose,
+ ApplicationInstanceReference reference,
+ boolean probe,
+ HostInfosCache hostInfosCache) {
+ this.statusService = statusService;
+ this.curator = curator;
+ this.onClose = onClose;
+ this.reference = reference;
+ this.probe = probe;
+ this.hostInfosCache = hostInfosCache;
+ }
+
+ @Override
+ public ApplicationInstanceReference getApplicationInstanceReference() {
+ return reference;
+ }
+
+ @Override
+ public ApplicationInstanceStatus getApplicationInstanceStatus() {
+ return statusService.getApplicationInstanceStatus(reference);
+ }
+
+ @Override
+ public HostInfos getHostInfos() {
+ return hostInfosCache.getHostInfos(reference);
+ }
+
+ @Override
+ public void setHostState(final HostName hostName, final HostStatus status) {
+ if (probe) return;
+ log.log(LogLevel.INFO, "Setting host " + hostName + " to status " + status);
+ hostInfosCache.setHostStatus(reference, hostName, status);
+ }
+
+ @Override
+ public void setApplicationInstanceStatus(ApplicationInstanceStatus applicationInstanceStatus) {
+ if (probe) return;
+
+ log.log(LogLevel.INFO, "Setting app " + reference.asString() + " to status " + applicationInstanceStatus);
+
+ String path = statusService.applicationInstanceSuspendedPath(reference);
+ switch (applicationInstanceStatus) {
+ case NO_REMARKS:
+ deleteNode_ignoreNoNodeException(path);
+ break;
+ case ALLOWED_TO_BE_DOWN:
+ createNode_ignoreNodeExistsException(path);
+ break;
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ onClose.run();
+ } catch (RuntimeException e) {
+ // We may want to avoid logging some exceptions that may be expected, like when session expires.
+ log.log(LogLevel.WARNING,
+ "Failed close application lock in " +
+ ZkApplicationLock.class.getSimpleName() + ", will ignore and continue",
+ e);
+ }
+ }
+
+ void deleteNode_ignoreNoNodeException(String path) {
+ try {
+ curator.framework().delete().forPath(path);
+ } catch (KeeperException.NoNodeException e) {
+ // ok
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void createNode_ignoreNodeExistsException(String path) {
+ try {
+ curator.framework().create().creatingParentsIfNeeded().forPath(path);
+ } catch (KeeperException.NodeExistsException e) {
+ // ok
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkStatusService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkStatusService.java
new file mode 100644
index 00000000000..c1ca04aea75
--- /dev/null
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZkStatusService.java
@@ -0,0 +1,237 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.orchestrator.status;
+
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.container.jaxrs.annotation.Component;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.jdisc.Timer;
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.Lock;
+import com.yahoo.vespa.orchestrator.OrchestratorContext;
+import com.yahoo.vespa.orchestrator.OrchestratorUtil;
+import org.apache.zookeeper.data.Stat;
+
+import javax.inject.Inject;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.logging.Logger;
+
+/**
+ * Stores instance suspension status and which hosts are allowed to go down in zookeeper.
+ *
+ * TODO: expiry of old application instances
+ * @author Tony Vaagenes
+ */
+public class ZkStatusService implements StatusService {
+
+ private static final Logger log = Logger.getLogger(ZkStatusService.class.getName());
+
+ final static String HOST_STATUS_BASE_PATH = "/vespa/host-status-service";
+ final static String APPLICATION_STATUS_BASE_PATH = "/vespa/application-status-service";
+
+ private final Curator curator;
+ private final HostInfosCache hostInfosCache;
+ private final Metric metric;
+ private final Timer timer;
+
+ /**
+ * A cache of metric contexts for each possible dimension map. In practice, there is one dimension map
+ * for each application, so up to hundreds of elements.
+ */
+ private final ConcurrentHashMap<Map<String, String>, Metric.Context> cachedContexts = new ConcurrentHashMap<>();
+
+ @Inject
+ public ZkStatusService(@Component Curator curator, @Component Metric metric, @Component Timer timer) {
+ this.curator = curator;
+ this.metric = metric;
+ this.timer = timer;
+ this.hostInfosCache = new HostInfosCache(curator, new HostInfosServiceImpl(curator, timer));
+ }
+
+ /** Non-private for testing only. */
+ ZkStatusService(Curator curator, Metric metric, Timer timer, HostInfosCache hostInfosCache) {
+ this.curator = curator;
+ this.metric = metric;
+ this.timer = timer;
+ this.hostInfosCache = hostInfosCache;
+ }
+
+ @Override
+ public Set<ApplicationInstanceReference> getAllSuspendedApplications() {
+ try {
+ Set<ApplicationInstanceReference> resultSet = new HashSet<>();
+
+ // Return empty set if the base path does not exist
+ Stat stat = curator.framework().checkExists().forPath(APPLICATION_STATUS_BASE_PATH);
+ if (stat == null) return resultSet;
+
+ // The path exist and we may have children
+ for (String referenceString : curator.framework().getChildren().forPath(APPLICATION_STATUS_BASE_PATH)) {
+ ApplicationInstanceReference reference = OrchestratorUtil.parseApplicationInstanceReference(referenceString);
+ resultSet.add(reference);
+ }
+
+ return resultSet;
+ } catch (Exception e) {
+ log.log(LogLevel.DEBUG, "Something went wrong while listing out applications in suspend.", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Cache is checked for freshness when this mapping is created, and may be invalidated again later
+ * by other users of the cache. Since this function is backed by the cache, any such invalidation
+ * will be reflected in the returned mapping; all users of the cache collaborate in repopulating it.
+ */
+ @Override
+ public Function<ApplicationInstanceReference, HostInfos> getHostInfosByApplicationResolver() {
+ hostInfosCache.refreshCache();
+ return hostInfosCache::getCachedHostInfos;
+ }
+
+
+ /**
+ * 1) locks the status service for an application instance.
+ * 2) fails all operations in this thread when the session is lost,
+ * since session loss might cause the lock to be lost.
+ * Since it only fails operations in this thread,
+ * all operations depending on a lock, including the locking itself, must be done in this thread.
+ * Note that since it is the thread that fails, all status operations in this thread will fail
+ * even if they're not supposed to be guarded by this lock
+ * (i.e. the request is for another applicationInstanceReference)
+ */
+ @Override
+ public ApplicationLock lockApplication(OrchestratorContext context, ApplicationInstanceReference reference)
+ throws UncheckedTimeoutException {
+
+ Runnable onRegistryClose;
+
+ // A multi-application operation, aka batch suspension, will first issue a probe
+ // then a non-probe. With "large locks", the lock is not release in between -
+ // no lock is taken on the non-probe. Instead, the release is done on the multi-application
+ // context close.
+ if (context.hasLock(reference)) {
+ onRegistryClose = () -> {};
+ } else {
+ Runnable unlock = acquireLock(context, reference);
+ if (context.registerLockAcquisition(reference, unlock)) {
+ onRegistryClose = () -> {};
+ } else {
+ onRegistryClose = unlock;
+ }
+ }
+
+ try {
+ return new ZkApplicationLock(
+ this,
+ curator,
+ onRegistryClose,
+ reference,
+ context.isProbe(),
+ hostInfosCache);
+ } catch (Throwable t) {
+ // In case the constructor throws an exception.
+ onRegistryClose.run();
+ throw t;
+ }
+ }
+
+ private Runnable acquireLock(OrchestratorContext context, ApplicationInstanceReference reference)
+ throws UncheckedTimeoutException {
+ ApplicationId applicationId = OrchestratorUtil.toApplicationId(reference);
+ String app = applicationId.application().value() + "." + applicationId.instance().value();
+ Map<String, String> dimensions = Map.of(
+ "tenantName", applicationId.tenant().value(),
+ "applicationId", applicationId.toFullString(),
+ "app", app);
+ Metric.Context metricContext = cachedContexts.computeIfAbsent(dimensions, metric::createContext);
+
+ Duration duration = context.getTimeLeft();
+ String lockPath = applicationInstanceLock2Path(reference);
+ Lock lock = new Lock(lockPath, curator);
+
+ Instant startTime = timer.currentTime();
+ Instant acquireEndTime;
+ boolean lockAcquired = false;
+ try {
+ lock.acquire(duration);
+ lockAcquired = true;
+ } finally {
+ acquireEndTime = timer.currentTime();
+ double seconds = durationInSeconds(startTime, acquireEndTime);
+ metric.set("orchestrator.lock.acquire-latency", seconds, metricContext);
+ metric.set("orchestrator.lock.acquired", lockAcquired ? 1 : 0, metricContext);
+
+ metric.add("orchestrator.lock.acquire", 1, metricContext);
+ String acquireResultMetricName = lockAcquired ? "orchestrator.lock.acquire-success" : "orchestrator.lock.acquire-timedout";
+ metric.add(acquireResultMetricName, 1, metricContext);
+ }
+
+ return () -> {
+ try {
+ lock.close();
+ } catch (RuntimeException e) {
+ // We may want to avoid logging some exceptions that may be expected, like when session expires.
+ log.log(LogLevel.WARNING,
+ "Failed to close application lock for " +
+ ZkStatusService.class.getSimpleName() + ", will ignore and continue",
+ e);
+ }
+
+ Instant lockReleasedTime = timer.currentTime();
+ double seconds = durationInSeconds(acquireEndTime, lockReleasedTime);
+ metric.set("orchestrator.lock.hold-latency", seconds, metricContext);
+ };
+ }
+
+ private double durationInSeconds(Instant startInstant, Instant endInstant) {
+ return Duration.between(startInstant, endInstant).toMillis() / 1000.0;
+ }
+
+ @Override
+ public HostInfo getHostInfo(ApplicationInstanceReference reference, HostName hostName) {
+ return hostInfosCache.getHostInfos(reference).getOrNoRemarks(hostName);
+ }
+
+ @Override
+ public ApplicationInstanceStatus getApplicationInstanceStatus(ApplicationInstanceReference reference) {
+ try {
+ Stat statOrNull = curator.framework().checkExists().forPath(
+ applicationInstanceSuspendedPath(reference));
+
+ return (statOrNull == null) ? ApplicationInstanceStatus.NO_REMARKS : ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static String applicationInstanceReferencePath(ApplicationInstanceReference reference) {
+ return HOST_STATUS_BASE_PATH + '/' + reference.tenantId() + ":" + reference.applicationInstanceId();
+ }
+
+ private static String hostsAllowedDownPath(ApplicationInstanceReference reference) {
+ return applicationInstanceReferencePath(reference) + "/hosts-allowed-down";
+ }
+
+ private static String applicationInstanceLock2Path(ApplicationInstanceReference reference) {
+ return applicationInstanceReferencePath(reference) + "/lock2";
+ }
+
+ String applicationInstanceSuspendedPath(ApplicationInstanceReference reference) {
+ return APPLICATION_STATUS_BASE_PATH + "/" + OrchestratorUtil.toRestApiFormat(reference);
+ }
+
+ private static String hostAllowedDownPath(ApplicationInstanceReference reference, HostName hostname) {
+ return hostsAllowedDownPath(reference) + '/' + hostname.s();
+ }
+
+}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java
deleted file mode 100644
index 2cdce350377..00000000000
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService.java
+++ /dev/null
@@ -1,418 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.orchestrator.status;
-
-import com.google.common.util.concurrent.UncheckedTimeoutException;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.container.jaxrs.annotation.Component;
-import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.Timer;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
-import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.curator.Lock;
-import com.yahoo.vespa.orchestrator.OrchestratorContext;
-import com.yahoo.vespa.orchestrator.OrchestratorUtil;
-import com.yahoo.vespa.orchestrator.status.json.WireHostInfo;
-import org.apache.zookeeper.KeeperException.NoNodeException;
-import org.apache.zookeeper.KeeperException.NodeExistsException;
-import org.apache.zookeeper.data.Stat;
-
-import javax.inject.Inject;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-/**
- * Stores instance suspension status and which hosts are allowed to go down in zookeeper.
- *
- * TODO: expiry of old application instances
- * @author Tony Vaagenes
- */
-public class ZookeeperStatusService implements StatusService {
-
- private static final Logger log = Logger.getLogger(ZookeeperStatusService.class.getName());
-
- final static String HOST_STATUS_BASE_PATH = "/vespa/host-status-service";
- final static String APPLICATION_STATUS_BASE_PATH = "/vespa/application-status-service";
-
- private final Curator curator;
- private final HostInfosCache hostInfosCache;
- private final Metric metric;
- private final Timer timer;
-
- /**
- * A cache of metric contexts for each possible dimension map. In practice, there is one dimension map
- * for each application, so up to hundreds of elements.
- */
- private final ConcurrentHashMap<Map<String, String>, Metric.Context> cachedContexts = new ConcurrentHashMap<>();
-
- @Inject
- public ZookeeperStatusService(@Component Curator curator, @Component Metric metric, @Component Timer timer) {
- this.curator = curator;
- this.metric = metric;
- this.timer = timer;
-
- // Insert a cache above some ZooKeeper accesses
- this.hostInfosCache = new HostInfosCache(curator, new HostInfosService() {
- @Override
- public HostInfos getHostInfos(ApplicationInstanceReference application) {
- return ZookeeperStatusService.this.getHostInfosFromZk(application);
- }
-
- @Override
- public boolean setHostStatus(ApplicationInstanceReference application, HostName hostName, HostStatus hostStatus) {
- return ZookeeperStatusService.this.setHostInfoInZk(application, hostName, hostStatus);
- }
- });
- }
-
- /** Non-private for testing only. */
- ZookeeperStatusService(Curator curator, Metric metric, Timer timer, HostInfosCache hostInfosCache) {
- this.curator = curator;
- this.metric = metric;
- this.timer = timer;
- this.hostInfosCache = hostInfosCache;
- }
-
- @Override
- public Set<ApplicationInstanceReference> getAllSuspendedApplications() {
- try {
- Set<ApplicationInstanceReference> resultSet = new HashSet<>();
-
- // Return empty set if the base path does not exist
- Stat stat = curator.framework().checkExists().forPath(APPLICATION_STATUS_BASE_PATH);
- if (stat == null) return resultSet;
-
- // The path exist and we may have children
- for (String appRefStr : curator.framework().getChildren().forPath(APPLICATION_STATUS_BASE_PATH)) {
- ApplicationInstanceReference appRef = OrchestratorUtil.parseAppInstanceReference(appRefStr);
- resultSet.add(appRef);
- }
-
- return resultSet;
- } catch (Exception e) {
- log.log(LogLevel.DEBUG, "Something went wrong while listing out applications in suspend.", e);
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Cache is checked for freshness when this mapping is created, and may be invalidated again later
- * by other users of the cache. Since this function is backed by the cache, any such invalidation
- * will be reflected in the returned mapping; all users of the cache collaborate in repopulating it.
- */
- @Override
- public Function<ApplicationInstanceReference, HostInfos> getHostInfosByApplicationResolver() {
- hostInfosCache.refreshCache();
- return hostInfosCache::getCachedHostInfos;
- }
-
-
- /**
- * 1) locks the status service for an application instance.
- * 2) fails all operations in this thread when the session is lost,
- * since session loss might cause the lock to be lost.
- * Since it only fails operations in this thread,
- * all operations depending on a lock, including the locking itself, must be done in this thread.
- * Note that since it is the thread that fails, all status operations in this thread will fail
- * even if they're not supposed to be guarded by this lock
- * (i.e. the request is for another applicationInstanceReference)
- */
- @Override
- public MutableStatusRegistry lockApplicationInstance_forCurrentThreadOnly(
- OrchestratorContext context,
- ApplicationInstanceReference applicationInstanceReference) throws UncheckedTimeoutException {
- Runnable onRegistryClose;
-
- // A multi-application operation, aka batch suspension, will first issue a probe
- // then a non-probe. With "large locks", the lock is not release in between -
- // no lock is taken on the non-probe. Instead, the release is done on the multi-application
- // context close.
- if (context.hasLock(applicationInstanceReference)) {
- onRegistryClose = () -> {};
- } else {
- Runnable unlock = acquireLock(context, applicationInstanceReference);
- if (context.registerLockAcquisition(applicationInstanceReference, unlock)) {
- onRegistryClose = () -> {};
- } else {
- onRegistryClose = unlock;
- }
- }
-
- try {
- return new ZkMutableStatusRegistry(onRegistryClose, applicationInstanceReference, context.isProbe());
- } catch (Throwable t) {
- // In case the constructor throws an exception.
- onRegistryClose.run();
- throw t;
- }
- }
-
- private Runnable acquireLock(OrchestratorContext context,
- ApplicationInstanceReference applicationInstanceReference)
- throws UncheckedTimeoutException {
- ApplicationId applicationId = OrchestratorUtil.toApplicationId(applicationInstanceReference);
- String app = applicationId.application().value() + "." + applicationId.instance().value();
- Map<String, String> dimensions = Map.of(
- "tenantName", applicationId.tenant().value(),
- "applicationId", applicationId.toFullString(),
- "app", app);
- Metric.Context metricContext = cachedContexts.computeIfAbsent(dimensions, metric::createContext);
-
- Duration duration = context.getTimeLeft();
- String lockPath = applicationInstanceLock2Path(applicationInstanceReference);
- Lock lock = new Lock(lockPath, curator);
-
- Instant startTime = timer.currentTime();
- Instant acquireEndTime;
- boolean lockAcquired = false;
- try {
- lock.acquire(duration);
- lockAcquired = true;
- } finally {
- acquireEndTime = timer.currentTime();
- double seconds = durationInSeconds(startTime, acquireEndTime);
- metric.set("orchestrator.lock.acquire-latency", seconds, metricContext);
- metric.set("orchestrator.lock.acquired", lockAcquired ? 1 : 0, metricContext);
-
- metric.add("orchestrator.lock.acquire", 1, metricContext);
- String acquireResultMetricName = lockAcquired ? "orchestrator.lock.acquire-success" : "orchestrator.lock.acquire-timedout";
- metric.add(acquireResultMetricName, 1, metricContext);
- }
-
- return () -> {
- try {
- lock.close();
- } catch (RuntimeException e) {
- // We may want to avoid logging some exceptions that may be expected, like when session expires.
- log.log(LogLevel.WARNING,
- "Failed to close application lock for " +
- ZookeeperStatusService.class.getSimpleName() + ", will ignore and continue",
- e);
- }
-
- Instant lockReleasedTime = timer.currentTime();
- double seconds = durationInSeconds(acquireEndTime, lockReleasedTime);
- metric.set("orchestrator.lock.hold-latency", seconds, metricContext);
- };
- }
-
- private double durationInSeconds(Instant startInstant, Instant endInstant) {
- return Duration.between(startInstant, endInstant).toMillis() / 1000.0;
- }
-
- /** Returns false if no changes were made. */
- private boolean setHostInfoInZk(ApplicationInstanceReference application, HostName hostname, HostStatus status) {
- String path = hostPath(application, hostname);
-
- if (status == HostStatus.NO_REMARKS) {
- return deleteNode_ignoreNoNodeException(path, "Host already has state NO_REMARKS, path = " + path);
- }
-
- Optional<HostInfo> currentHostInfo = uncheck(() -> readBytesFromZk(path)).map(WireHostInfo::deserialize);
- if (currentHostInfo.isEmpty()) {
- Instant suspendedSince = timer.currentTime();
- HostInfo hostInfo = HostInfo.createSuspended(status, suspendedSince);
- byte[] hostInfoBytes = WireHostInfo.serialize(hostInfo);
- uncheck(() -> curator.framework().create().creatingParentsIfNeeded().forPath(path, hostInfoBytes));
- } else if (currentHostInfo.get().status() == status) {
- return false;
- } else {
- Instant suspendedSince = currentHostInfo.get().suspendedSince().orElseGet(timer::currentTime);
- HostInfo hostInfo = HostInfo.createSuspended(status, suspendedSince);
- byte[] hostInfoBytes = WireHostInfo.serialize(hostInfo);
- uncheck(() -> curator.framework().setData().forPath(path, hostInfoBytes));
- }
-
- return true;
- }
-
- private boolean deleteNode_ignoreNoNodeException(String path, String debugLogMessageIfNotExists) {
- try {
- curator.framework().delete().forPath(path);
- return true;
- } catch (NoNodeException e) {
- log.log(LogLevel.DEBUG, debugLogMessageIfNotExists, e);
- return false;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private boolean createNode_ignoreNodeExistsException(String path, String debugLogMessageIfExists) {
- try {
- curator.framework().create()
- .creatingParentsIfNeeded()
- .forPath(path);
- return true;
- } catch (NodeExistsException e) {
- log.log(LogLevel.DEBUG, debugLogMessageIfExists, e);
- return false;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private Optional<byte[]> readBytesFromZk(String path) throws Exception {
- try {
- return Optional.of(curator.framework().getData().forPath(path));
- } catch (NoNodeException e) {
- return Optional.empty();
- }
- }
-
- @Override
- public HostInfo getHostInfo(ApplicationInstanceReference applicationInstanceReference, HostName hostName) {
- return hostInfosCache.getHostInfos(applicationInstanceReference).getOrNoRemarks(hostName);
- }
-
- /** Do not call this directly: should be called behind a cache. */
- private HostInfos getHostInfosFromZk(ApplicationInstanceReference application) {
- String hostsRootPath = hostsPath(application);
- if (uncheck(() -> curator.framework().checkExists().forPath(hostsRootPath)) == null) {
- return new HostInfos();
- } else {
- List<String> hostnames = uncheck(() -> curator.framework().getChildren().forPath(hostsRootPath));
- Map<HostName, HostInfo> hostInfos = hostnames.stream().collect(Collectors.toMap(
- hostname -> new HostName(hostname),
- hostname -> {
- byte[] bytes = uncheck(() -> curator.framework().getData().forPath(hostsRootPath + "/" + hostname));
- return WireHostInfo.deserialize(bytes);
- }));
- return new HostInfos(hostInfos);
- }
- }
-
- private <T> T uncheck(SupplierThrowingException<T> supplier) {
- try {
- return supplier.get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @FunctionalInterface
- interface SupplierThrowingException<T> {
- T get() throws Exception;
- }
-
- @Override
- public ApplicationInstanceStatus getApplicationInstanceStatus(ApplicationInstanceReference applicationInstanceReference) {
- try {
- Stat statOrNull = curator.framework().checkExists().forPath(
- applicationInstanceSuspendedPath(applicationInstanceReference));
-
- return (statOrNull == null) ? ApplicationInstanceStatus.NO_REMARKS : ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- static String applicationInstanceReferencePath(ApplicationInstanceReference applicationInstanceReference) {
- return HOST_STATUS_BASE_PATH + '/' +
- applicationInstanceReference.tenantId() + ":" + applicationInstanceReference.applicationInstanceId();
- }
-
- private static String applicationPath(ApplicationInstanceReference applicationInstanceReference) {
- ApplicationId applicationId = OrchestratorUtil.toApplicationId(applicationInstanceReference);
- return "/vespa/host-status/" + applicationId.serializedForm();
- }
-
- private static String hostsPath(ApplicationInstanceReference applicationInstanceReference) {
- return applicationPath(applicationInstanceReference) + "/hosts";
- }
-
- private static String hostsAllowedDownPath(ApplicationInstanceReference applicationInstanceReference) {
- return applicationInstanceReferencePath(applicationInstanceReference) + "/hosts-allowed-down";
- }
-
- private static String applicationInstanceLock2Path(ApplicationInstanceReference applicationInstanceReference) {
- return applicationInstanceReferencePath(applicationInstanceReference) + "/lock2";
- }
-
- private String applicationInstanceSuspendedPath(ApplicationInstanceReference applicationInstanceReference) {
- return APPLICATION_STATUS_BASE_PATH + "/" + OrchestratorUtil.toRestApiFormat(applicationInstanceReference);
- }
-
- private static String hostAllowedDownPath(ApplicationInstanceReference applicationInstanceReference, HostName hostname) {
- return hostsAllowedDownPath(applicationInstanceReference) + '/' + hostname.s();
- }
-
- private static String hostPath(ApplicationInstanceReference application, HostName hostname) {
- return hostsPath(application) + "/" + hostname.s();
- }
-
- private class ZkMutableStatusRegistry implements MutableStatusRegistry {
-
- private final Runnable onClose;
- private final ApplicationInstanceReference applicationInstanceReference;
- private final boolean probe;
-
- public ZkMutableStatusRegistry(Runnable onClose,
- ApplicationInstanceReference applicationInstanceReference,
- boolean probe) {
- this.onClose = onClose;
- this.applicationInstanceReference = applicationInstanceReference;
- this.probe = probe;
- }
-
- @Override
- public ApplicationInstanceStatus getStatus() {
- return getApplicationInstanceStatus(applicationInstanceReference);
- }
-
- @Override
- public HostInfos getHostInfos() {
- return hostInfosCache.getHostInfos(applicationInstanceReference);
- }
-
- @Override
- public void setHostState(final HostName hostName, final HostStatus status) {
- if (probe) return;
- log.log(LogLevel.INFO, "Setting host " + hostName + " to status " + status);
- hostInfosCache.setHostStatus(applicationInstanceReference, hostName, status);
- }
-
- @Override
- public void setApplicationInstanceStatus(ApplicationInstanceStatus applicationInstanceStatus) {
- if (probe) return;
-
- log.log(LogLevel.INFO, "Setting app " + applicationInstanceReference.asString() + " to status " + applicationInstanceStatus);
-
- String path = applicationInstanceSuspendedPath(applicationInstanceReference);
- switch (applicationInstanceStatus) {
- case NO_REMARKS:
- deleteNode_ignoreNoNodeException(path,
- "Instance is already in state NO_REMARKS, path = " + path);
- break;
- case ALLOWED_TO_BE_DOWN:
- createNode_ignoreNodeExistsException(path,
- "Instance is already in state ALLOWED_TO_BE_DOWN, path = " + path);
- break;
- }
- }
-
- @Override
- public void close() {
- try {
- onClose.run();
- } catch (RuntimeException e) {
- // We may want to avoid logging some exceptions that may be expected, like when session expires.
- log.log(LogLevel.WARNING,
- "Failed close application lock in " +
- ZookeeperStatusService.class.getSimpleName() + ", will ignore and continue",
- e);
- }
- }
- }
-
-}
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyInstanceLookupService.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyServiceMonitor.java
index a54f5284ee0..09fb6296866 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyInstanceLookupService.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/DummyServiceMonitor.java
@@ -15,8 +15,12 @@ import com.yahoo.vespa.applicationmodel.ServiceType;
import com.yahoo.vespa.applicationmodel.TenantId;
import com.yahoo.vespa.orchestrator.model.NodeGroup;
import com.yahoo.vespa.orchestrator.model.VespaModelUtil;
+import com.yahoo.vespa.service.monitor.ServiceModel;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -27,7 +31,7 @@ import java.util.stream.Collectors;
* @author oyving
* @author smorgrav
*/
-public class DummyInstanceLookupService implements InstanceLookupService {
+public class DummyServiceMonitor implements ServiceMonitor {
public static final HostName TEST1_HOST_NAME = new HostName("test1.hostname.tld");
public static final HostName TEST3_HOST_NAME = new HostName("test3.hostname.tld");
@@ -121,26 +125,27 @@ public class DummyInstanceLookupService implements InstanceLookupService {
}
// A node group is tied to an application, so we need to define them after we have populated the above applications.
- public final static NodeGroup TEST1_NODE_GROUP = new NodeGroup(new DummyInstanceLookupService().findInstanceByHost(TEST1_HOST_NAME).get(), TEST1_HOST_NAME);
- public final static NodeGroup TEST3_NODE_GROUP = new NodeGroup(new DummyInstanceLookupService().findInstanceByHost(TEST3_HOST_NAME).get(), TEST3_HOST_NAME);
- public final static NodeGroup TEST6_NODE_GROUP = new NodeGroup(new DummyInstanceLookupService().findInstanceByHost(TEST6_HOST_NAME).get(), TEST6_HOST_NAME);
+ public final static NodeGroup TEST1_NODE_GROUP = new NodeGroup(new DummyServiceMonitor().getApplication(TEST1_HOST_NAME).get(), TEST1_HOST_NAME);
+ public final static NodeGroup TEST3_NODE_GROUP = new NodeGroup(new DummyServiceMonitor().getApplication(TEST3_HOST_NAME).get(), TEST3_HOST_NAME);
+ public final static NodeGroup TEST6_NODE_GROUP = new NodeGroup(new DummyServiceMonitor().getApplication(TEST6_HOST_NAME).get(), TEST6_HOST_NAME);
+ @Override
+ public ServiceModel getServiceModelSnapshot() {
+ throw new UnsupportedOperationException();
+ }
@Override
- public Optional<ApplicationInstance> findInstanceById(
- final ApplicationInstanceReference applicationInstanceReference) {
- for (ApplicationInstance app : apps) {
- if (app.reference().equals(applicationInstanceReference)) return Optional.of(app);
- }
- return Optional.empty();
+ public Set<ApplicationInstanceReference> getAllApplicationInstanceReferences() {
+ return apps.stream().map(a ->
+ new ApplicationInstanceReference(a.tenantId(),a.applicationInstanceId())).collect(Collectors.toSet());
}
@Override
- public Optional<ApplicationInstance> findInstanceByHost(HostName hostName) {
+ public Optional<ApplicationInstance> getApplication(HostName hostname) {
for (ApplicationInstance app : apps) {
for (ServiceCluster cluster : app.serviceClusters()) {
for (ServiceInstance service : cluster.serviceInstances()) {
- if (hostName.equals(service.hostName())) return Optional.of(app);
+ if (hostname.equals(service.hostName())) return Optional.of(app);
}
}
}
@@ -148,10 +153,16 @@ public class DummyInstanceLookupService implements InstanceLookupService {
}
@Override
- public Set<ApplicationInstanceReference> knownInstances() {
- return apps.stream().map(a ->
- new ApplicationInstanceReference(a.tenantId(),a.applicationInstanceId())).collect(Collectors.toSet());
+ public Optional<ApplicationInstance> getApplication(ApplicationInstanceReference reference) {
+ for (ApplicationInstance app : apps) {
+ if (app.reference().equals(reference)) return Optional.of(app);
+ }
+ return Optional.empty();
+ }
+ @Override
+ public Map<HostName, List<ServiceInstance>> getServicesByHostname() {
+ throw new UnsupportedOperationException();
}
public static Set<HostName> getContentHosts(ApplicationInstanceReference appRef) {
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 bfe4b523e4a..9bcbdba074e 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorImplTest.java
@@ -26,11 +26,13 @@ import com.yahoo.vespa.orchestrator.policy.BatchHostStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.policy.HostStateChangeDeniedException;
import com.yahoo.vespa.orchestrator.policy.HostedVespaClusterPolicy;
import com.yahoo.vespa.orchestrator.policy.HostedVespaPolicy;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
import com.yahoo.vespa.orchestrator.status.HostStatus;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
import com.yahoo.vespa.orchestrator.status.StatusService;
-import com.yahoo.vespa.orchestrator.status.ZookeeperStatusService;
+import com.yahoo.vespa.orchestrator.status.ZkStatusService;
+import com.yahoo.vespa.service.model.ServiceModelCache;
import com.yahoo.vespa.service.monitor.ServiceModel;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -85,17 +87,17 @@ public class OrchestratorImplTest {
@Before
public void setUp() {
// Extract applications and hosts from dummy instance lookup service
- Iterator<ApplicationInstance> iterator = DummyInstanceLookupService.getApplications().iterator();
+ Iterator<ApplicationInstance> iterator = DummyServiceMonitor.getApplications().iterator();
ApplicationInstanceReference app1_ref = iterator.next().reference();
app1 = OrchestratorUtil.toApplicationId(app1_ref);
- app1_host1 = DummyInstanceLookupService.getContentHosts(app1_ref).iterator().next();
+ app1_host1 = DummyServiceMonitor.getContentHosts(app1_ref).iterator().next();
app2 = OrchestratorUtil.toApplicationId(iterator.next().reference());
clustercontroller = new ClusterControllerClientFactoryMock();
orchestrator = new OrchestratorImpl(new HostedVespaPolicy(new HostedVespaClusterPolicy(), clustercontroller, applicationApiFactory),
clustercontroller,
- new ZookeeperStatusService(new MockCurator(), mock(Metric.class), new TestTimer()),
- new DummyInstanceLookupService(),
+ new ZkStatusService(new MockCurator(), mock(Metric.class), new TestTimer()),
+ new DummyServiceMonitor(),
0,
new ManualClock(),
applicationApiFactory,
@@ -241,8 +243,8 @@ public class OrchestratorImplTest {
@Test
public void applicationReferenceHasTenantAndAppInstance() {
- InstanceLookupService service = new DummyInstanceLookupService();
- String applicationInstanceId = service.findInstanceByHost(DummyInstanceLookupService.TEST1_HOST_NAME).get()
+ ServiceMonitor service = new DummyServiceMonitor();
+ String applicationInstanceId = service.getApplication(DummyServiceMonitor.TEST1_HOST_NAME).get()
.reference().toString();
assertEquals("test-tenant-id:application:prod:utopia-1:instance", applicationInstanceId);
}
@@ -264,21 +266,21 @@ public class OrchestratorImplTest {
orchestrator.suspendAll(
new HostName("parentHostname"),
Arrays.asList(
- DummyInstanceLookupService.TEST1_HOST_NAME,
- DummyInstanceLookupService.TEST3_HOST_NAME,
- DummyInstanceLookupService.TEST6_HOST_NAME));
+ DummyServiceMonitor.TEST1_HOST_NAME,
+ DummyServiceMonitor.TEST3_HOST_NAME,
+ DummyServiceMonitor.TEST6_HOST_NAME));
// As of 2016-06-07 the order of the node groups are as follows:
// TEST3: mediasearch:imagesearch:default
// TEST6: tenant-id-3:application-instance-3:default
// TEST1: test-tenant-id:application:instance
InOrder order = inOrder(orchestrator);
- verifySuspendGroup(order, orchestrator, DummyInstanceLookupService.TEST3_NODE_GROUP, true);
- verifySuspendGroup(order, orchestrator, DummyInstanceLookupService.TEST6_NODE_GROUP, true);
- verifySuspendGroup(order, orchestrator, DummyInstanceLookupService.TEST1_NODE_GROUP, true);
- verifySuspendGroup(order, orchestrator, DummyInstanceLookupService.TEST3_NODE_GROUP, false);
- verifySuspendGroup(order, orchestrator, DummyInstanceLookupService.TEST6_NODE_GROUP, false);
- verifySuspendGroup(order, orchestrator, DummyInstanceLookupService.TEST1_NODE_GROUP, false);
+ verifySuspendGroup(order, orchestrator, DummyServiceMonitor.TEST3_NODE_GROUP, true);
+ verifySuspendGroup(order, orchestrator, DummyServiceMonitor.TEST6_NODE_GROUP, true);
+ verifySuspendGroup(order, orchestrator, DummyServiceMonitor.TEST1_NODE_GROUP, true);
+ verifySuspendGroup(order, orchestrator, DummyServiceMonitor.TEST3_NODE_GROUP, false);
+ verifySuspendGroup(order, orchestrator, DummyServiceMonitor.TEST6_NODE_GROUP, false);
+ verifySuspendGroup(order, orchestrator, DummyServiceMonitor.TEST1_NODE_GROUP, false);
order.verifyNoMoreInteractions();
}
@@ -295,18 +297,18 @@ public class OrchestratorImplTest {
OrchestratorImpl orchestrator = spy(this.orchestrator);
Throwable supensionFailure = new HostStateChangeDeniedException(
- DummyInstanceLookupService.TEST6_HOST_NAME,
+ DummyServiceMonitor.TEST6_HOST_NAME,
"some-constraint",
"error message");
- doThrow(supensionFailure).when(orchestrator).suspendGroup(any(), eq(DummyInstanceLookupService.TEST6_NODE_GROUP));
+ doThrow(supensionFailure).when(orchestrator).suspendGroup(any(), eq(DummyServiceMonitor.TEST6_NODE_GROUP));
try {
orchestrator.suspendAll(
new HostName("parentHostname"),
Arrays.asList(
- DummyInstanceLookupService.TEST1_HOST_NAME,
- DummyInstanceLookupService.TEST3_HOST_NAME,
- DummyInstanceLookupService.TEST6_HOST_NAME));
+ DummyServiceMonitor.TEST1_HOST_NAME,
+ DummyServiceMonitor.TEST3_HOST_NAME,
+ DummyServiceMonitor.TEST6_HOST_NAME));
fail();
} catch (BatchHostStateChangeDeniedException e) {
assertEquals("Failed to suspend NodeGroup{application=tenant-id-3:application-instance-3:prod:utopia-1:default, " +
@@ -317,8 +319,8 @@ public class OrchestratorImplTest {
}
InOrder order = inOrder(orchestrator);
- order.verify(orchestrator).suspendGroup(any(), eq(DummyInstanceLookupService.TEST3_NODE_GROUP));
- order.verify(orchestrator).suspendGroup(any(), eq(DummyInstanceLookupService.TEST6_NODE_GROUP));
+ order.verify(orchestrator).suspendGroup(any(), eq(DummyServiceMonitor.TEST3_NODE_GROUP));
+ order.verify(orchestrator).suspendGroup(any(), eq(DummyServiceMonitor.TEST6_NODE_GROUP));
order.verifyNoMoreInteractions();
}
@@ -329,25 +331,24 @@ public class OrchestratorImplTest {
var applicationInstanceReference = new ApplicationInstanceReference(tenantId, applicationInstanceId);
var policy = mock(HostedVespaPolicy.class);
- var zookeeperStatusService = mock(ZookeeperStatusService.class);
- var instanceLookupService = mock(InstanceLookupService.class);
+ var zookeeperStatusService = mock(ZkStatusService.class);
+ var serviceMonitor = mock(ServiceMonitor.class);
var applicationInstance = mock(ApplicationInstance.class);
var clusterControllerClientFactory = mock(ClusterControllerClientFactory.class);
var clock = new ManualClock();
var applicationApiFactory = mock(ApplicationApiFactory.class);
- var hostStatusRegistry = mock(MutableStatusRegistry.class);
+ var lock = mock(ApplicationLock.class);
- when(instanceLookupService.findInstanceByHost(any())).thenReturn(Optional.of(applicationInstance));
+ when(serviceMonitor.getApplication(any(HostName.class))).thenReturn(Optional.of(applicationInstance));
when(applicationInstance.reference()).thenReturn(applicationInstanceReference);
- when(zookeeperStatusService.lockApplicationInstance_forCurrentThreadOnly(any(), any()))
- .thenReturn(hostStatusRegistry);
- when(hostStatusRegistry.getStatus()).thenReturn(NO_REMARKS);
+ when(zookeeperStatusService.lockApplication(any(), any())).thenReturn(lock);
+ when(lock.getApplicationInstanceStatus()).thenReturn(NO_REMARKS);
var orchestrator = new OrchestratorImpl(
policy,
clusterControllerClientFactory,
zookeeperStatusService,
- instanceLookupService,
+ serviceMonitor,
20,
clock,
applicationApiFactory,
@@ -358,7 +359,7 @@ public class OrchestratorImplTest {
orchestrator.suspendAll(parentHostname, List.of(parentHostname));
ArgumentCaptor<OrchestratorContext> contextCaptor = ArgumentCaptor.forClass(OrchestratorContext.class);
- verify(zookeeperStatusService, times(2)).lockApplicationInstance_forCurrentThreadOnly(contextCaptor.capture(), any());
+ verify(zookeeperStatusService, times(2)).lockApplication(contextCaptor.capture(), any());
List<OrchestratorContext> contexts = contextCaptor.getAllValues();
// First invocation is probe, second is not.
@@ -370,26 +371,26 @@ public class OrchestratorImplTest {
verify(applicationApiFactory, times(2)).create(any(), any(), any());
verify(policy, times(2)).grantSuspensionRequest(any(), any());
- verify(instanceLookupService, atLeastOnce()).findInstanceByHost(any());
- verify(hostStatusRegistry, times(2)).getStatus();
+ verify(serviceMonitor, atLeastOnce()).getApplication(any(HostName.class));
+ verify(lock, times(2)).getApplicationInstanceStatus();
// Each zookeeperStatusService that is created, is closed.
- verify(zookeeperStatusService, times(2)).lockApplicationInstance_forCurrentThreadOnly(any(), any());
- verify(hostStatusRegistry, times(2)).close();
+ verify(zookeeperStatusService, times(2)).lockApplication(any(), any());
+ verify(lock, times(2)).close();
verifyNoMoreInteractions(
policy,
clusterControllerClientFactory,
zookeeperStatusService,
- hostStatusRegistry,
- instanceLookupService,
+ lock,
+ serviceMonitor,
applicationApiFactory);
}
@Test
public void testGetHost() throws Exception {
ClusterControllerClientFactory clusterControllerClientFactory = new ClusterControllerClientFactoryMock();
- StatusService statusService = new ZookeeperStatusService(new MockCurator(), mock(Metric.class), new TestTimer());
+ StatusService statusService = new ZkStatusService(new MockCurator(), mock(Metric.class), new TestTimer());
HostName hostName = new HostName("host.yahoo.com");
TenantId tenantId = new TenantId("tenant");
@@ -415,13 +416,14 @@ public class OrchestratorImplTest {
hostName,
ServiceStatus.NOT_CHECKED)))));
- InstanceLookupService lookupService = new ServiceMonitorInstanceLookupService(
- () -> new ServiceModel(Map.of(reference, applicationInstance)));
+ ServiceMonitor serviceMonitor = new ServiceModelCache(
+ () -> new ServiceModel(Map.of(reference, applicationInstance)),
+ new TestTimer());
orchestrator = new OrchestratorImpl(new HostedVespaPolicy(new HostedVespaClusterPolicy(), clusterControllerClientFactory, applicationApiFactory),
clusterControllerClientFactory,
statusService,
- lookupService,
+ serviceMonitor,
0,
new ManualClock(),
applicationApiFactory,
@@ -438,8 +440,8 @@ public class OrchestratorImplTest {
}
private boolean isInMaintenance(ApplicationId appId, HostName hostName) throws ApplicationIdNotFoundException {
- for (ApplicationInstance app : DummyInstanceLookupService.getApplications()) {
- if (app.reference().equals(OrchestratorUtil.toApplicationInstanceReference(appId, new DummyInstanceLookupService()))) {
+ for (ApplicationInstance app : DummyServiceMonitor.getApplications()) {
+ if (app.reference().equals(OrchestratorUtil.toApplicationInstanceReference(appId, new DummyServiceMonitor()))) {
return clustercontroller.isInMaintenance(app, hostName);
}
}
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorUtilTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorUtilTest.java
index 230048f505d..76fbb5c9fe2 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorUtilTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/OrchestratorUtilTest.java
@@ -39,14 +39,14 @@ public class OrchestratorUtilTest {
public void applicationid_conversion_are_symmetric() throws Exception {
// From appId to appRef and back
- ApplicationInstanceReference appRef = OrchestratorUtil.toApplicationInstanceReference(APPID_1, new DummyInstanceLookupService());
+ ApplicationInstanceReference appRef = OrchestratorUtil.toApplicationInstanceReference(APPID_1, new DummyServiceMonitor());
ApplicationId appIdRoundTrip = OrchestratorUtil.toApplicationId(appRef);
Assert.assertEquals(APPID_1, appIdRoundTrip);
// From appRef to appId and back
ApplicationId appId = OrchestratorUtil.toApplicationId(APPREF_1);
- ApplicationInstanceReference appRefRoundTrip = OrchestratorUtil.toApplicationInstanceReference(appId, new DummyInstanceLookupService());
+ ApplicationInstanceReference appRefRoundTrip = OrchestratorUtil.toApplicationInstanceReference(appId, new DummyServiceMonitor());
Assert.assertEquals(APPREF_1, appRefRoundTrip);
}
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 5c5ee7d2260..502e42481aa 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
@@ -4,7 +4,7 @@ package com.yahoo.vespa.orchestrator.controller;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.orchestrator.DummyInstanceLookupService;
+import com.yahoo.vespa.orchestrator.DummyServiceMonitor;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.model.VespaModelUtil;
@@ -37,8 +37,8 @@ public class ClusterControllerClientFactoryMock implements ClusterControllerClie
}
public void setAllDummyNodesAsUp() {
- for (ApplicationInstance app : DummyInstanceLookupService.getApplications()) {
- Set<HostName> hosts = DummyInstanceLookupService.getContentHosts(app.reference());
+ for (ApplicationInstance app : DummyServiceMonitor.getApplications()) {
+ Set<HostName> hosts = DummyServiceMonitor.getContentHosts(app.reference());
for (HostName host : hosts) {
ClusterId clusterName = VespaModelUtil.getContentClusterName(app, host);
int storageNodeIndex = VespaModelUtil.getStorageNodeIndex(app, host);
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 eff222bc074..3d0746e5a36 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
@@ -21,18 +21,19 @@ import com.yahoo.vespa.orchestrator.OrchestrationException;
import com.yahoo.vespa.orchestrator.Orchestrator;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
import com.yahoo.vespa.orchestrator.OrchestratorImpl;
-import com.yahoo.vespa.orchestrator.ServiceMonitorInstanceLookupService;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactory;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactoryMock;
import com.yahoo.vespa.orchestrator.policy.HostedVespaClusterPolicy;
import com.yahoo.vespa.orchestrator.policy.HostedVespaPolicy;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
import com.yahoo.vespa.orchestrator.status.HostInfo;
import com.yahoo.vespa.orchestrator.status.HostInfos;
import com.yahoo.vespa.orchestrator.status.HostStatus;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
import com.yahoo.vespa.orchestrator.status.StatusService;
-import com.yahoo.vespa.orchestrator.status.ZookeeperStatusService;
+import com.yahoo.vespa.orchestrator.status.ZkStatusService;
+import com.yahoo.vespa.service.model.ServiceModelCache;
import com.yahoo.vespa.service.monitor.ServiceModel;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import com.yahoo.yolean.Exceptions;
import java.time.Clock;
@@ -57,11 +58,13 @@ class ModelTestUtils {
private final Map<ApplicationInstanceReference, ApplicationInstance> applications = new HashMap<>();
private final ClusterControllerClientFactory clusterControllerClientFactory = new ClusterControllerClientFactoryMock();
private final Map<HostName, HostStatus> hostStatusMap = new HashMap<>();
- private final StatusService statusService = new ZookeeperStatusService(new MockCurator(), mock(Metric.class), new TestTimer());
+ private final StatusService statusService = new ZkStatusService(new MockCurator(), mock(Metric.class), new TestTimer());
+ private final TestTimer timer = new TestTimer();
+ private final ServiceMonitor serviceMonitor = new ServiceModelCache(() -> new ServiceModel(applications), timer);
private final Orchestrator orchestrator = new OrchestratorImpl(new HostedVespaPolicy(new HostedVespaClusterPolicy(), clusterControllerClientFactory, applicationApiFactory()),
clusterControllerClientFactory,
statusService,
- new ServiceMonitorInstanceLookupService(() -> new ServiceModel(applications)),
+ serviceMonitor,
0,
new ManualClock(),
applicationApiFactory(),
@@ -110,10 +113,10 @@ class ModelTestUtils {
ApplicationInstance applicationInstance,
HostName... hostnames) {
NodeGroup nodeGroup = new NodeGroup(applicationInstance, hostnames);
- MutableStatusRegistry registry = statusService.lockApplicationInstance_forCurrentThreadOnly(
+ ApplicationLock lock = statusService.lockApplication(
OrchestratorContext.createContextForSingleAppOp(Clock.systemUTC()),
applicationInstance.reference());
- return applicationApiFactory().create(nodeGroup, registry, clusterControllerClientFactory);
+ return applicationApiFactory().create(nodeGroup, lock, clusterControllerClientFactory);
}
ApplicationInstance createApplicationInstance(
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionResourceTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionResourceTest.java
index 80d0af09792..1fff8f976bb 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionResourceTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionResourceTest.java
@@ -161,8 +161,8 @@ public class ApplicationSuspensionResourceTest {
" </config>\n" +
" <component id=\"com.yahoo.vespa.flags.InMemoryFlagSource\" bundle=\"flags\" />\n" +
" <component id=\"com.yahoo.vespa.curator.mock.MockCurator\" bundle=\"zkfacade\" />\n" +
- " <component id=\"com.yahoo.vespa.orchestrator.status.ZookeeperStatusService\" bundle=\"orchestrator\" />\n" +
- " <component id=\"com.yahoo.vespa.orchestrator.DummyInstanceLookupService\" bundle=\"orchestrator\" />\n" +
+ " <component id=\"com.yahoo.vespa.orchestrator.status.ZkStatusService\" bundle=\"orchestrator\" />\n" +
+ " <component id=\"com.yahoo.vespa.orchestrator.DummyServiceMonitor\" bundle=\"orchestrator\" />\n" +
" <component id=\"com.yahoo.vespa.orchestrator.OrchestratorImpl\" bundle=\"orchestrator\" />\n" +
" <component id=\"com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactoryMock\" bundle=\"orchestrator\" />\n" +
"\n" +
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java
index bfa68145828..b620b0798be 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostResourceTest.java
@@ -21,7 +21,6 @@ import com.yahoo.vespa.orchestrator.BatchHostNameNotFoundException;
import com.yahoo.vespa.orchestrator.BatchInternalErrorException;
import com.yahoo.vespa.orchestrator.Host;
import com.yahoo.vespa.orchestrator.HostNameNotFoundException;
-import com.yahoo.vespa.orchestrator.InstanceLookupService;
import com.yahoo.vespa.orchestrator.OrchestrationException;
import com.yahoo.vespa.orchestrator.Orchestrator;
import com.yahoo.vespa.orchestrator.OrchestratorContext;
@@ -37,11 +36,13 @@ import com.yahoo.vespa.orchestrator.restapi.wire.GetHostResponse;
import com.yahoo.vespa.orchestrator.restapi.wire.PatchHostRequest;
import com.yahoo.vespa.orchestrator.restapi.wire.PatchHostResponse;
import com.yahoo.vespa.orchestrator.restapi.wire.UpdateHostResponse;
+import com.yahoo.vespa.orchestrator.status.ApplicationLock;
import com.yahoo.vespa.orchestrator.status.HostInfo;
import com.yahoo.vespa.orchestrator.status.HostStatus;
-import com.yahoo.vespa.orchestrator.status.MutableStatusRegistry;
import com.yahoo.vespa.orchestrator.status.StatusService;
-import com.yahoo.vespa.orchestrator.status.ZookeeperStatusService;
+import com.yahoo.vespa.orchestrator.status.ZkStatusService;
+import com.yahoo.vespa.service.monitor.ServiceModel;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import org.junit.Before;
import org.junit.Test;
@@ -56,8 +57,8 @@ import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import static com.yahoo.vespa.orchestrator.TestUtil.makeServiceClusterSet;
import static org.junit.Assert.assertEquals;
@@ -78,13 +79,13 @@ public class HostResourceTest {
private static final int SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS = 0;
private static final TenantId TENANT_ID = new TenantId("tenantId");
private static final ApplicationInstanceId APPLICATION_INSTANCE_ID = new ApplicationInstanceId("applicationId");
- private static final StatusService EVERY_HOST_IS_UP_HOST_STATUS_SERVICE = new ZookeeperStatusService(
+ private static final StatusService EVERY_HOST_IS_UP_HOST_STATUS_SERVICE = new ZkStatusService(
new MockCurator(), mock(Metric.class), new TestTimer());
private static final ApplicationApiFactory applicationApiFactory = new ApplicationApiFactory(3);
- private static final InstanceLookupService mockInstanceLookupService = mock(InstanceLookupService.class);
+ private static final ServiceMonitor serviceMonitor = mock(ServiceMonitor.class);
static {
- when(mockInstanceLookupService.findInstanceByHost(any()))
+ when(serviceMonitor.getApplication(any(HostName.class)))
.thenReturn(Optional.of(
new ApplicationInstance(
TENANT_ID,
@@ -94,21 +95,12 @@ public class HostResourceTest {
private final InMemoryFlagSource flagSource = new InMemoryFlagSource();
- private static final InstanceLookupService alwaysEmptyInstanceLookUpService = new InstanceLookupService() {
- @Override
- public Optional<ApplicationInstance> findInstanceById(
- final ApplicationInstanceReference applicationInstanceReference) {
- return Optional.empty();
- }
-
- @Override
- public Optional<ApplicationInstance> findInstanceByHost(final HostName hostName) {
- return Optional.empty();
- }
+ private static final ServiceMonitor alwaysEmptyServiceMonitor = new ServiceMonitor() {
+ private final ServiceModel emptyServiceModel = new ServiceModel(Map.of());
@Override
- public Set<ApplicationInstanceReference> knownInstances() {
- return Collections.emptySet();
+ public ServiceModel getServiceModelSnapshot() {
+ return emptyServiceModel;
}
};
@@ -129,14 +121,14 @@ public class HostResourceTest {
public void releaseSuspensionGrant(
OrchestratorContext context, ApplicationInstance applicationInstance,
HostName hostName,
- MutableStatusRegistry hostStatusRegistry) {
+ ApplicationLock hostStatusRegistry) {
}
}
private final OrchestratorImpl alwaysAllowOrchestrator = new OrchestratorImpl(
new AlwaysAllowPolicy(),
new ClusterControllerClientFactoryMock(),
- EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, mockInstanceLookupService,
+ EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, serviceMonitor,
SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
clock,
applicationApiFactory,
@@ -145,7 +137,7 @@ public class HostResourceTest {
private final OrchestratorImpl hostNotFoundOrchestrator = new OrchestratorImpl(
new AlwaysAllowPolicy(),
new ClusterControllerClientFactoryMock(),
- EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, alwaysEmptyInstanceLookUpService,
+ EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, alwaysEmptyServiceMonitor,
SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
clock,
applicationApiFactory,
@@ -231,7 +223,7 @@ public class HostResourceTest {
public void releaseSuspensionGrant(
OrchestratorContext context, ApplicationInstance applicationInstance,
HostName hostName,
- MutableStatusRegistry hostStatusRegistry) throws HostStateChangeDeniedException {
+ ApplicationLock hostStatusRegistry) throws HostStateChangeDeniedException {
doThrow();
}
@@ -248,7 +240,7 @@ public class HostResourceTest {
final OrchestratorImpl alwaysRejectResolver = new OrchestratorImpl(
new AlwaysFailPolicy(),
new ClusterControllerClientFactoryMock(),
- EVERY_HOST_IS_UP_HOST_STATUS_SERVICE,mockInstanceLookupService,
+ EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, serviceMonitor,
SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
clock,
applicationApiFactory,
@@ -269,7 +261,7 @@ public class HostResourceTest {
new AlwaysFailPolicy(),
new ClusterControllerClientFactoryMock(),
EVERY_HOST_IS_UP_HOST_STATUS_SERVICE,
- mockInstanceLookupService,
+ serviceMonitor,
SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS,
clock,
applicationApiFactory,
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService2Test.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZkStatusService2Test.java
index 8f530f4abf3..c5d390050ee 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusService2Test.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZkStatusService2Test.java
@@ -27,12 +27,12 @@ import static org.mockito.Mockito.when;
/**
* @author hakonhall
*/
-public class ZookeeperStatusService2Test {
+public class ZkStatusService2Test {
private final Curator curator = mock(Curator.class);
private final Timer timer = new TestTimer();
private final Metric metric = mock(Metric.class);
private final HostInfosCache cache = mock(HostInfosCache.class);
- private final ZookeeperStatusService zookeeperStatusService = new ZookeeperStatusService(curator, metric, timer, cache);
+ private final ZkStatusService zkStatusService = new ZkStatusService(curator, metric, timer, cache);
private final OrchestratorContext context = mock(OrchestratorContext.class);
private final InterProcessMutex mutex = mock(InterProcessMutex.class);
@@ -50,7 +50,7 @@ public class ZookeeperStatusService2Test {
when(context.getTimeLeft()).thenReturn(Duration.ofSeconds(12));
- try (MutableStatusRegistry registry = zookeeperStatusService.lockApplicationInstance_forCurrentThreadOnly(context, reference)) {
+ try (ApplicationLock lock = zkStatusService.lockApplication(context, reference)) {
// nothing
}
@@ -65,7 +65,7 @@ public class ZookeeperStatusService2Test {
when(context.isProbe()).thenReturn(false);
- try (MutableStatusRegistry registry = zookeeperStatusService.lockApplicationInstance_forCurrentThreadOnly(context, reference)) {
+ try (ApplicationLock lock = zkStatusService.lockApplication(context, reference)) {
// nothing
}
@@ -88,7 +88,7 @@ public class ZookeeperStatusService2Test {
when(context.getTimeLeft()).thenReturn(Duration.ofSeconds(12));
- try (MutableStatusRegistry registry = zookeeperStatusService.lockApplicationInstance_forCurrentThreadOnly(context, reference)) {
+ try (ApplicationLock lock = zkStatusService.lockApplication(context, reference)) {
// nothing
}
@@ -105,7 +105,7 @@ public class ZookeeperStatusService2Test {
when(context.hasLock(any())).thenReturn(true);
when(context.registerLockAcquisition(any(), any())).thenReturn(false);
- try (MutableStatusRegistry registry = zookeeperStatusService.lockApplicationInstance_forCurrentThreadOnly(context, reference)) {
+ try (ApplicationLock lock = zkStatusService.lockApplication(context, reference)) {
// nothing
}
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusServiceTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZkStatusServiceTest.java
index 687ea951f88..a6d7f09d69b 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZookeeperStatusServiceTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/status/ZkStatusServiceTest.java
@@ -53,9 +53,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
-public class ZookeeperStatusServiceTest {
+public class ZkStatusServiceTest {
private TestingServer testingServer;
- private ZookeeperStatusService zookeeperStatusService;
+ private ZkStatusService zkStatusService;
private Curator curator;
private final Timer timer = mock(Timer.class);
private final Metric metric = mock(Metric.class);
@@ -70,7 +70,7 @@ public class ZookeeperStatusServiceTest {
testingServer = new TestingServer();
curator = createConnectedCurator(testingServer);
- zookeeperStatusService = new ZookeeperStatusService(curator, metric, timer);
+ zkStatusService = new ZkStatusService(curator, metric, timer);
when(context.getTimeLeft()).thenReturn(Duration.ofSeconds(10));
when(context.isProbe()).thenReturn(false);
when(timer.currentTime()).thenReturn(Instant.ofEpochMilli(1));
@@ -96,7 +96,7 @@ public class ZookeeperStatusServiceTest {
@Test
public void host_state_for_unknown_hosts_is_no_remarks() {
assertThat(
- zookeeperStatusService.getHostInfo(TestIds.APPLICATION_INSTANCE_REFERENCE, TestIds.HOST_NAME1).status(),
+ zkStatusService.getHostInfo(TestIds.APPLICATION_INSTANCE_REFERENCE, TestIds.HOST_NAME1).status(),
is(HostStatus.NO_REMARKS));
}
@@ -107,17 +107,14 @@ public class ZookeeperStatusServiceTest {
Instant.ofEpochMilli((3)),
Instant.ofEpochMilli(6));
- try (MutableStatusRegistry statusRegistry = zookeeperStatusService
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
+ try (ApplicationLock lock = zkStatusService.lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
//shuffling to catch "clean database" failures for all cases.
for (HostStatus hostStatus: shuffledList(HostStatus.NO_REMARKS, HostStatus.ALLOWED_TO_BE_DOWN)) {
for (int i = 0; i < 2; i++) {
- statusRegistry.setHostState(
- TestIds.HOST_NAME1,
- hostStatus);
+ lock.setHostState(TestIds.HOST_NAME1, hostStatus);
- assertThat(statusRegistry.getHostInfos().getOrNoRemarks(TestIds.HOST_NAME1).status(),
+ assertThat(lock.getHostInfos().getOrNoRemarks(TestIds.HOST_NAME1).status(),
is(hostStatus));
}
}
@@ -141,15 +138,15 @@ public class ZookeeperStatusServiceTest {
@Test
public void locks_are_exclusive() throws Exception {
- ZookeeperStatusService zookeeperStatusService2 = new ZookeeperStatusService(curator, mock(Metric.class), new TestTimer());
+ ZkStatusService zkStatusService2 = new ZkStatusService(curator, mock(Metric.class), new TestTimer());
final CompletableFuture<Void> lockedSuccessfullyFuture;
- try (MutableStatusRegistry statusRegistry = zookeeperStatusService
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
+ try (ApplicationLock lock = zkStatusService
+ .lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
lockedSuccessfullyFuture = CompletableFuture.runAsync(() -> {
- try (MutableStatusRegistry statusRegistry2 = zookeeperStatusService2
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE))
+ try (ApplicationLock lock2 = zkStatusService2
+ .lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE))
{
}
});
@@ -166,15 +163,15 @@ public class ZookeeperStatusServiceTest {
@Test
public void failing_to_get_lock_closes_SessionFailRetryLoop() throws Exception {
- ZookeeperStatusService zookeeperStatusService2 = new ZookeeperStatusService(curator, mock(Metric.class), new TestTimer());
+ ZkStatusService zkStatusService2 = new ZkStatusService(curator, mock(Metric.class), new TestTimer());
- try (MutableStatusRegistry statusRegistry = zookeeperStatusService
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
+ try (ApplicationLock lock = zkStatusService
+ .lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
//must run in separate thread, since having 2 locks in the same thread fails
CompletableFuture<Void> resultOfZkOperationAfterLockFailure = CompletableFuture.runAsync(() -> {
try {
- zookeeperStatusService2.lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE);
+ zkStatusService2.lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE);
fail("Both zookeeper host status services locked simultaneously for the same application instance");
} catch (RuntimeException e) {
}
@@ -182,7 +179,7 @@ public class ZookeeperStatusServiceTest {
killSession(curator.framework(), testingServer);
//Throws SessionFailedException if the SessionFailRetryLoop has not been closed.
- statusRegistry.getHostInfos().getOrNoRemarks(TestIds.HOST_NAME1);
+ lock.getHostInfos().getOrNoRemarks(TestIds.HOST_NAME1);
});
assertThat(resultOfZkOperationAfterLockFailure, notHoldsException());
@@ -239,29 +236,29 @@ public class ZookeeperStatusServiceTest {
// Initial state is NO_REMARK
assertThat(
- zookeeperStatusService
+ zkStatusService
.getApplicationInstanceStatus(TestIds.APPLICATION_INSTANCE_REFERENCE),
is(ApplicationInstanceStatus.NO_REMARKS));
// Suspend
- try (MutableStatusRegistry statusRegistry = zookeeperStatusService
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
- statusRegistry.setApplicationInstanceStatus(ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN);
+ try (ApplicationLock lock = zkStatusService
+ .lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
+ lock.setApplicationInstanceStatus(ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN);
}
assertThat(
- zookeeperStatusService
+ zkStatusService
.getApplicationInstanceStatus(TestIds.APPLICATION_INSTANCE_REFERENCE),
is(ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN));
// Resume
- try (MutableStatusRegistry statusRegistry = zookeeperStatusService
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
- statusRegistry.setApplicationInstanceStatus(ApplicationInstanceStatus.NO_REMARKS);
+ try (ApplicationLock lock = zkStatusService
+ .lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
+ lock.setApplicationInstanceStatus(ApplicationInstanceStatus.NO_REMARKS);
}
assertThat(
- zookeeperStatusService
+ zkStatusService
.getApplicationInstanceStatus(TestIds.APPLICATION_INSTANCE_REFERENCE),
is(ApplicationInstanceStatus.NO_REMARKS));
}
@@ -269,20 +266,20 @@ public class ZookeeperStatusServiceTest {
@Test
public void suspending_two_applications_returns_two_applications() {
Set<ApplicationInstanceReference> suspendedApps
- = zookeeperStatusService.getAllSuspendedApplications();
+ = zkStatusService.getAllSuspendedApplications();
assertThat(suspendedApps.size(), is(0));
- try (MutableStatusRegistry statusRegistry = zookeeperStatusService
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
+ try (ApplicationLock statusRegistry = zkStatusService
+ .lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE)) {
statusRegistry.setApplicationInstanceStatus(ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN);
}
- try (MutableStatusRegistry statusRegistry = zookeeperStatusService
- .lockApplicationInstance_forCurrentThreadOnly(context, TestIds.APPLICATION_INSTANCE_REFERENCE2)) {
- statusRegistry.setApplicationInstanceStatus(ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN);
+ try (ApplicationLock lock = zkStatusService
+ .lockApplication(context, TestIds.APPLICATION_INSTANCE_REFERENCE2)) {
+ lock.setApplicationInstanceStatus(ApplicationInstanceStatus.ALLOWED_TO_BE_DOWN);
}
- suspendedApps = zookeeperStatusService.getAllSuspendedApplications();
+ suspendedApps = zkStatusService.getAllSuspendedApplications();
assertThat(suspendedApps.size(), is(2));
assertThat(suspendedApps, hasItem(TestIds.APPLICATION_INSTANCE_REFERENCE));
assertThat(suspendedApps, hasItem(TestIds.APPLICATION_INSTANCE_REFERENCE2));
diff --git a/parent/pom.xml b/parent/pom.xml
index 81a7e3360b5..c2b82ff7b46 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -744,7 +744,7 @@
<apache.httpcore.version>4.4.13</apache.httpcore.version>
<asm.version>7.0</asm.version>
<!-- Athenz dependencies. Make sure these dependencies match those in Vespa's internal repositories -->
- <athenz.version>1.8.44</athenz.version>
+ <athenz.version>1.8.49</athenz.version>
<aws.sdk.version>1.11.542</aws.sdk.version>
<!-- WARNING: If you change curator version, you also need to update
zkfacade/src/main/java/org/apache/curator/**/package-info.java
diff --git a/searchcore/src/vespa/searchcore/config/proton.def b/searchcore/src/vespa/searchcore/config/proton.def
index eb4f1f6dc89..6040fc651c2 100644
--- a/searchcore/src/vespa/searchcore/config/proton.def
+++ b/searchcore/src/vespa/searchcore/config/proton.def
@@ -117,6 +117,10 @@ indexing.read.io enum {NORMAL, DIRECTIO} default=DIRECTIO restart
## Control number of threads used for indexing
indexing.threads int default=1 restart
+## Option to specify what is most important during indexing.
+## This is experimental and will most likely be temporary.
+indexing.optimize enum {LATENCY, THROUGHPUT} default=LATENCY restart
+
## Maximum number of pending operations for each of the internal
## indexing threads. Only used when visibility delay is zero.
indexing.tasklimit int default=1000 restart
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
index 302ffc93f6a..1ee1b703ea9 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
@@ -140,7 +140,8 @@ DocumentDB::DocumentDB(const vespalib::string &baseDir,
_writeService(sharedExecutor,
_writeServiceConfig.indexingThreads(),
indexing_thread_stack_size,
- _writeServiceConfig.defaultTaskLimit()),
+ _writeServiceConfig.defaultTaskLimit(),
+ _writeServiceConfig.optimize()),
_initializeThreads(std::move(initializeThreads)),
_initConfigSnapshot(),
_initConfigSerialNum(0u),
diff --git a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp
index 058197f0271..6e7b4967f6d 100644
--- a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.cpp
@@ -10,7 +10,8 @@ using search::SequencedTaskExecutor;
namespace proton {
ExecutorThreadingService::ExecutorThreadingService(vespalib::ThreadStackExecutorBase & sharedExecutor,
- uint32_t threads, uint32_t stackSize, uint32_t taskLimit)
+ uint32_t threads, uint32_t stackSize, uint32_t taskLimit,
+ OptimizeFor optimize)
: _sharedExecutor(sharedExecutor),
_masterExecutor(1, stackSize),
@@ -21,7 +22,7 @@ ExecutorThreadingService::ExecutorThreadingService(vespalib::ThreadStackExecutor
_summaryService(_summaryExecutor),
_indexFieldInverter(SequencedTaskExecutor::create(threads, taskLimit)),
_indexFieldWriter(SequencedTaskExecutor::create(threads, taskLimit)),
- _attributeFieldWriter(SequencedTaskExecutor::create(threads, taskLimit))
+ _attributeFieldWriter(SequencedTaskExecutor::create(threads, taskLimit, optimize))
{
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h
index 7bbe1cb162a..2e4dd2035f3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h
+++ b/searchcore/src/vespa/searchcore/proton/server/executorthreadingservice.h
@@ -29,6 +29,7 @@ private:
std::unique_ptr<search::ISequencedTaskExecutor> _attributeFieldWriter;
public:
+ using OptimizeFor = vespalib::Executor::OptimizeFor;
/**
* Constructor.
*
@@ -38,7 +39,8 @@ public:
ExecutorThreadingService(vespalib::ThreadStackExecutorBase &sharedExecutor,
uint32_t threads = 1,
uint32_t stackSize = 128 * 1024,
- uint32_t taskLimit = 1000);
+ uint32_t taskLimit = 1000,
+ OptimizeFor optimize = OptimizeFor::LATENCY);
~ExecutorThreadingService() override;
/**
diff --git a/searchcore/src/vespa/searchcore/proton/server/threading_service_config.cpp b/searchcore/src/vespa/searchcore/proton/server/threading_service_config.cpp
index 55aa1a20ef6..5bc6ef543f3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/threading_service_config.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/threading_service_config.cpp
@@ -7,13 +7,16 @@
namespace proton {
using ProtonConfig = ThreadingServiceConfig::ProtonConfig;
+using OptimizeFor = vespalib::Executor::OptimizeFor;
ThreadingServiceConfig::ThreadingServiceConfig(uint32_t indexingThreads_,
uint32_t defaultTaskLimit_,
- uint32_t semiUnboundTaskLimit_)
+ uint32_t semiUnboundTaskLimit_,
+ OptimizeFor optimize)
: _indexingThreads(indexingThreads_),
_defaultTaskLimit(defaultTaskLimit_),
- _semiUnboundTaskLimit(semiUnboundTaskLimit_)
+ _semiUnboundTaskLimit(semiUnboundTaskLimit_),
+ _optimize(optimize)
{
}
@@ -30,6 +33,16 @@ calculateIndexingThreads(uint32_t cfgIndexingThreads, double concurrency, const
return std::max(indexingThreads, 1u);
}
+OptimizeFor
+selectOptimization(ProtonConfig::Indexing::Optimize optimize) {
+ using CfgOptimize = ProtonConfig::Indexing::Optimize;
+ switch (optimize) {
+ case CfgOptimize::LATENCY: return OptimizeFor::LATENCY;
+ case CfgOptimize::THROUGHPUT: return OptimizeFor::THROUGHPUT;
+ }
+ return OptimizeFor::LATENCY;
+}
+
}
ThreadingServiceConfig
@@ -37,7 +50,8 @@ ThreadingServiceConfig::make(const ProtonConfig &cfg, double concurrency, const
{
uint32_t indexingThreads = calculateIndexingThreads(cfg.indexing.threads, concurrency, cpuInfo);
return ThreadingServiceConfig(indexingThreads, cfg.indexing.tasklimit,
- (cfg.indexing.semiunboundtasklimit / indexingThreads));
+ (cfg.indexing.semiunboundtasklimit / indexingThreads),
+ selectOptimization(cfg.indexing.optimize));
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/threading_service_config.h b/searchcore/src/vespa/searchcore/proton/server/threading_service_config.h
index be39f516598..149215f97dc 100644
--- a/searchcore/src/vespa/searchcore/proton/server/threading_service_config.h
+++ b/searchcore/src/vespa/searchcore/proton/server/threading_service_config.h
@@ -2,6 +2,7 @@
#pragma once
#include <vespa/searchcore/proton/common/hw_info.h>
+#include <vespa/vespalib/util/executor.h>
#include <cstdint>
namespace vespa::config::search::core::internal { class InternalProtonType; }
@@ -13,14 +14,16 @@ namespace proton {
class ThreadingServiceConfig {
public:
using ProtonConfig = const vespa::config::search::core::internal::InternalProtonType;
+ using OptimizeFor = vespalib::Executor::OptimizeFor;
private:
- uint32_t _indexingThreads;
- uint32_t _defaultTaskLimit;
- uint32_t _semiUnboundTaskLimit;
+ uint32_t _indexingThreads;
+ uint32_t _defaultTaskLimit;
+ uint32_t _semiUnboundTaskLimit;
+ OptimizeFor _optimize;
private:
- ThreadingServiceConfig(uint32_t indexingThreads_, uint32_t defaultTaskLimit_, uint32_t semiUnboundTaskLimit_);
+ ThreadingServiceConfig(uint32_t indexingThreads_, uint32_t defaultTaskLimit_, uint32_t semiUnboundTaskLimit_, OptimizeFor optimize);
public:
static ThreadingServiceConfig make(const ProtonConfig &cfg, double concurrency, const HwInfo::Cpu &cpuInfo);
@@ -28,6 +31,7 @@ public:
uint32_t indexingThreads() const { return _indexingThreads; }
uint32_t defaultTaskLimit() const { return _defaultTaskLimit; }
uint32_t semiUnboundTaskLimit() const { return _semiUnboundTaskLimit; }
+ OptimizeFor optimize() const { return _optimize;}
};
}
diff --git a/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp b/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp
index 24be21f65ec..47728c9785c 100644
--- a/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp
+++ b/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp
@@ -295,7 +295,7 @@ public:
request_ctx.set_query_tensor("query_tensor", tensor_spec);
}
Blueprint::UP create_blueprint() {
- query::NearestNeighborTerm term("query_tensor", attr_name, 0, Weight(0), 7);
+ query::NearestNeighborTerm term("query_tensor", attr_name, 0, Weight(0), 7, true, 33);
return source.createBlueprint(request_ctx, FieldSpec(attr_name, 0, 0), term);
}
};
diff --git a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
index a0538952ad9..3ef0ab20cdd 100644
--- a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
+++ b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
@@ -226,7 +226,6 @@ struct Fixture
while (_attr->getNumDocs() <= docId) {
uint32_t newDocId = 0u;
_attr->addDoc(newDocId);
- _attr->commit();
}
}
@@ -326,7 +325,6 @@ Fixture::testSetTensorValue()
{
ensureSpace(4);
EXPECT_EQUAL(5u, _attr->getNumDocs());
- EXPECT_EQUAL(5u, _attr->getCommittedDocIdLimit());
TEST_DO(assertGetNoTensor(4));
EXPECT_EXCEPTION(set_tensor(4, TensorSpec("double")),
WrongTensorTypeException,
diff --git a/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_benchmark.cpp b/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_benchmark.cpp
index b2b15ded274..9491617c135 100644
--- a/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_benchmark.cpp
+++ b/searchlib/src/tests/common/sequencedtaskexecutor/sequencedtaskexecutor_benchmark.cpp
@@ -10,13 +10,19 @@ using ExecutorId = search::ISequencedTaskExecutor::ExecutorId;
int main(int argc, char *argv[]) {
unsigned long numTasks = 1000000;
unsigned numThreads = 4;
+ unsigned taskLimit = 1000;
+ vespalib::Executor::OptimizeFor optimize = vespalib::Executor::OptimizeFor::LATENCY;
std::atomic<long> counter(0);
if (argc > 1)
numTasks = atol(argv[1]);
if (argc > 2)
numThreads = atoi(argv[2]);
+ if (argc > 3)
+ taskLimit = atoi(argv[3]);
+ if (argc > 4)
+ optimize = vespalib::Executor::OptimizeFor::THROUGHPUT;
- auto executor = SequencedTaskExecutor::create(numThreads);
+ auto executor = SequencedTaskExecutor::create(numThreads, taskLimit, optimize);
for (unsigned long tid(0); tid < numTasks; tid++) {
executor->executeTask(ExecutorId(tid%numThreads), vespalib::makeLambdaTask([&counter] { counter++; }));
}
diff --git a/searchlib/src/tests/query/query_visitor_test.cpp b/searchlib/src/tests/query/query_visitor_test.cpp
index 39e381c0942..edbc29be784 100644
--- a/searchlib/src/tests/query/query_visitor_test.cpp
+++ b/searchlib/src/tests/query/query_visitor_test.cpp
@@ -99,7 +99,7 @@ void Test::requireThatAllNodesCanBeVisited() {
checkVisit<SuffixTerm>(new SimpleSuffixTerm("t", "field", 0, Weight(0)));
checkVisit<PredicateQuery>(new SimplePredicateQuery(PredicateQueryTerm::UP(), "field", 0, Weight(0)));
checkVisit<RegExpTerm>(new SimpleRegExpTerm("t", "field", 0, Weight(0)));
- checkVisit<NearestNeighborTerm>(new SimpleNearestNeighborTerm("query_tensor", "doc_tensor", 0, Weight(0), 123));
+ checkVisit<NearestNeighborTerm>(new SimpleNearestNeighborTerm("query_tensor", "doc_tensor", 0, Weight(0), 123, true, 321));
}
} // namespace
diff --git a/searchlib/src/tests/query/querybuilder_test.cpp b/searchlib/src/tests/query/querybuilder_test.cpp
index 7f496b3493c..8560cb0e091 100644
--- a/searchlib/src/tests/query/querybuilder_test.cpp
+++ b/searchlib/src/tests/query/querybuilder_test.cpp
@@ -111,7 +111,7 @@ Node::UP createQueryTree() {
builder.addStringTerm(str[5], view[5], id[5], weight[6]);
builder.addStringTerm(str[6], view[6], id[6], weight[7]);
}
- builder.add_nearest_neighbor_term("query_tensor", "doc_tensor", id[3], weight[5], 7);
+ builder.add_nearest_neighbor_term("query_tensor", "doc_tensor", id[3], weight[5], 7, true, 33);
}
Node::UP node = builder.build();
ASSERT_TRUE(node.get());
@@ -395,8 +395,9 @@ struct MyRegExpTerm : RegExpTerm {
};
struct MyNearestNeighborTerm : NearestNeighborTerm {
MyNearestNeighborTerm(vespalib::stringref query_tensor_name, vespalib::stringref field_name,
- int32_t i, Weight w, uint32_t target_num_hits)
- : NearestNeighborTerm(query_tensor_name, field_name, i, w, target_num_hits)
+ int32_t i, Weight w, uint32_t target_num_hits,
+ bool allow_approximate, uint32_t explore_additional_hits)
+ : NearestNeighborTerm(query_tensor_name, field_name, i, w, target_num_hits, allow_approximate, explore_additional_hits)
{}
};
diff --git a/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp b/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp
index 2516950b0cc..1604a39eaf3 100644
--- a/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp
+++ b/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp
@@ -5,6 +5,7 @@
#include <vespa/searchlib/tensor/doc_vector_access.h>
#include <vespa/searchlib/tensor/hnsw_index.h>
#include <vespa/searchlib/tensor/random_level_generator.h>
+#include <vespa/searchlib/tensor/inv_log_level_generator.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/vespalib/util/generationhandler.h>
#include <vector>
@@ -65,6 +66,9 @@ public:
.set(4, {1, 2}).set(5, {8, 3}).set(6, {7, 2})
.set(7, {3, 5}).set(8, {0, 3}).set(9, {4, 5});
}
+
+ ~HnswIndexTest() {}
+
void init(bool heuristic_select_neighbors) {
auto generator = std::make_unique<LevelGenerator>();
level_generator = generator.get();
@@ -441,5 +445,36 @@ TEST_F(HnswIndexTest, shrink_called_heuristic)
EXPECT_TRUE(index->check_link_symmetry());
}
+TEST(LevelGeneratorTest, gives_various_levels)
+{
+ InvLogLevelGenerator generator(4);
+ EXPECT_EQ(2u, generator.max_level());
+ EXPECT_EQ(1u, generator.max_level());
+ EXPECT_EQ(0u, generator.max_level());
+ EXPECT_EQ(1u, generator.max_level());
+ EXPECT_EQ(0u, generator.max_level());
+ EXPECT_EQ(1u, generator.max_level());
+ EXPECT_EQ(0u, generator.max_level());
+ EXPECT_EQ(0u, generator.max_level());
+ EXPECT_EQ(0u, generator.max_level());
+
+ uint32_t left = 1000000;
+ std::vector<uint32_t> hist;
+ for (uint32_t i = 0; i < left; ++i) {
+ uint32_t l = generator.max_level();
+ if (hist.size() <= l) {
+ hist.resize(l+1);
+ }
+ hist[l]++;
+ }
+ for (uint32_t l = 0; l < hist.size(); ++l) {
+ double expected = left * 0.75;
+ EXPECT_TRUE(hist[l] < expected*1.01 + 100);
+ EXPECT_TRUE(hist[l] > expected*0.99 - 100);
+ left *= 0.25;
+ }
+ EXPECT_TRUE(hist.size() < 14);
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
index 8595b0eff7f..9af05059bef 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
@@ -646,7 +646,9 @@ public:
query_tensor.release();
setResult(std::make_unique<queryeval::NearestNeighborBlueprint>(_field, *dense_attr_tensor,
std::move(dense_query_tensor_up),
- n.get_target_num_hits()));
+ n.get_target_num_hits(),
+ n.get_allow_approximate(),
+ n.get_explore_additional_hits()));
}
};
diff --git a/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.cpp b/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.cpp
index 30723a6eb2a..7b0c30ec9d8 100644
--- a/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.cpp
+++ b/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.cpp
@@ -2,8 +2,10 @@
#include "sequencedtaskexecutor.h"
#include <vespa/vespalib/util/blockingthreadstackexecutor.h>
+#include <vespa/vespalib/util/singleexecutor.h>
using vespalib::BlockingThreadStackExecutor;
+using vespalib::SingleExecutor;
namespace search {
@@ -15,12 +17,16 @@ constexpr uint32_t stackSize = 128 * 1024;
std::unique_ptr<ISequencedTaskExecutor>
-SequencedTaskExecutor::create(uint32_t threads, uint32_t taskLimit)
+SequencedTaskExecutor::create(uint32_t threads, uint32_t taskLimit, OptimizeFor optimize)
{
auto executors = std::make_unique<std::vector<std::unique_ptr<vespalib::SyncableThreadExecutor>>>();
executors->reserve(threads);
for (uint32_t id = 0; id < threads; ++id) {
- executors->push_back(std::make_unique<BlockingThreadStackExecutor>(1, stackSize, taskLimit));
+ if (optimize == OptimizeFor::THROUGHPUT) {
+ executors->push_back(std::make_unique<SingleExecutor>(taskLimit));
+ } else {
+ executors->push_back(std::make_unique<BlockingThreadStackExecutor>(1, stackSize, taskLimit));
+ }
}
return std::unique_ptr<ISequencedTaskExecutor>(new SequencedTaskExecutor(std::move(executors)));
}
diff --git a/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.h b/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.h
index a29e3d5226c..8568901006f 100644
--- a/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.h
+++ b/searchlib/src/vespa/searchlib/common/sequencedtaskexecutor.h
@@ -23,6 +23,7 @@ class SequencedTaskExecutor final : public ISequencedTaskExecutor
SequencedTaskExecutor(std::unique_ptr<std::vector<std::unique_ptr<vespalib::SyncableThreadExecutor>>> executor);
public:
using ISequencedTaskExecutor::getExecutorId;
+ using OptimizeFor = vespalib::Executor::OptimizeFor;
~SequencedTaskExecutor();
@@ -30,7 +31,12 @@ public:
void executeTask(ExecutorId id, vespalib::Executor::Task::UP task) override;
void sync() override;
Stats getStats() override;
- static std::unique_ptr<ISequencedTaskExecutor> create(uint32_t threads, uint32_t taskLimit = 1000);
+
+ /*
+ * Note that if you choose Optimize::THROUGHPUT, you must ensure only a single producer, or synchronize on the outside.
+ */
+ static std::unique_ptr<ISequencedTaskExecutor>
+ create(uint32_t threads, uint32_t taskLimit = 1000, OptimizeFor optimize = OptimizeFor::LATENCY);
};
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
index 70a3097ae05..c42cf8fc370 100644
--- a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
+++ b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
@@ -21,9 +21,11 @@ SimpleQueryStackDumpIterator::SimpleQueryStackDumpIterator(vespalib::stringref b
_currUniqueId(0),
_currFlags(0),
_currArity(0),
- _currArg1(0),
- _currArg2(0),
- _currArg3(0),
+ _extraIntArg1(0),
+ _extraIntArg2(0),
+ _extraIntArg3(0),
+ _extraDoubleArg4(0),
+ _extraDoubleArg5(0),
_predicate_query_term(),
_curr_index_name(),
_curr_term(),
@@ -138,7 +140,6 @@ SimpleQueryStackDumpIterator::next()
case ParseItem::ITEM_ANY:
try {
_currArity = readCompressedPositiveInt(p);
- _currArg1 = 0;
_curr_index_name = vespalib::stringref();
_curr_term = vespalib::stringref();
} catch (...) {
@@ -150,7 +151,7 @@ SimpleQueryStackDumpIterator::next()
case ParseItem::ITEM_ONEAR:
try {
_currArity = readCompressedPositiveInt(p);
- _currArg1 = readCompressedPositiveInt(p);
+ _extraIntArg1 = readCompressedPositiveInt(p);
_curr_index_name = vespalib::stringref();
_curr_term = vespalib::stringref();
} catch (...) {
@@ -161,7 +162,7 @@ SimpleQueryStackDumpIterator::next()
case ParseItem::ITEM_WEAK_AND:
try {
_currArity = readCompressedPositiveInt(p);
- _currArg1 = readCompressedPositiveInt(p);
+ _extraIntArg1 = readCompressedPositiveInt(p); // targetNumHits
_curr_index_name = read_stringref(p);
_curr_term = vespalib::stringref();
} catch (...) {
@@ -171,7 +172,6 @@ SimpleQueryStackDumpIterator::next()
case ParseItem::ITEM_SAME_ELEMENT:
try {
_currArity = readCompressedPositiveInt(p);
- _currArg1 = 0;
_curr_index_name = read_stringref(p);
_curr_term = vespalib::stringref();
} catch (...) {
@@ -182,7 +182,6 @@ SimpleQueryStackDumpIterator::next()
case ParseItem::ITEM_PURE_WEIGHTED_STRING:
try {
_curr_term = read_stringref(p);
- _currArg1 = 0;
_currArity = 0;
} catch (...) {
return false;
@@ -196,7 +195,6 @@ SimpleQueryStackDumpIterator::next()
p += sizeof(int64_t);
if (p > _bufEnd) return false;
- _currArg1 = 0;
_currArity = 0;
break;
case ParseItem::ITEM_WORD_ALTERNATIVES:
@@ -218,7 +216,6 @@ SimpleQueryStackDumpIterator::next()
try {
_curr_index_name = read_stringref(p);
_curr_term = read_stringref(p);
- _currArg1 = 0;
_currArity = 0;
} catch (...) {
return false;
@@ -258,11 +255,9 @@ SimpleQueryStackDumpIterator::next()
_currArity = readCompressedPositiveInt(p);
_curr_index_name = read_stringref(p);
if (_currType == ParseItem::ITEM_WAND) {
- _currArg1 = readCompressedPositiveInt(p); // targetNumHits
- _currArg2 = read_double(p); // scoreThreshold
- _currArg3 = read_double(p); // thresholdBoostFactor
- } else {
- _currArg1 = 0;
+ _extraIntArg1 = readCompressedPositiveInt(p); // targetNumHits
+ _extraDoubleArg4 = read_double(p); // scoreThreshold
+ _extraDoubleArg5 = read_double(p); // thresholdBoostFactor
}
_curr_term = vespalib::stringref();
} catch (...) {
@@ -274,7 +269,9 @@ SimpleQueryStackDumpIterator::next()
try {
_curr_index_name = read_stringref(p);
_curr_term = read_stringref(p); // query_tensor_name
- _currArg1 = readCompressedPositiveInt(p); // target_num_hits;
+ _extraIntArg1 = readCompressedPositiveInt(p); // targetNumHits
+ _extraIntArg2 = readCompressedPositiveInt(p); // allow_approximate
+ _extraIntArg3 = readCompressedPositiveInt(p); // explore_additional_hits
_currArity = 0;
} catch (...) {
return false;
diff --git a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.h b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.h
index 6eb3fb7777d..73c97bb5fb3 100644
--- a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.h
+++ b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.h
@@ -42,12 +42,13 @@ private:
/** The arity of the current item */
uint32_t _currArity;
- /** The first argument of the current item (length of NEAR/ONEAR area for example) */
- uint32_t _currArg1;
- /** The second argument of the current item (score threshold of WAND for example) */
- double _currArg2;
- /** The third argument of the current item (threshold boost factor of WAND for example) */
- double _currArg3;
+
+ /* extra arguments */
+ uint32_t _extraIntArg1;
+ uint32_t _extraIntArg2;
+ uint32_t _extraIntArg3;
+ double _extraDoubleArg4;
+ double _extraDoubleArg5;
/** The predicate query specification */
query::PredicateQueryTerm::UP _predicate_query_term;
/** The index name (field name) in the current item */
@@ -118,11 +119,12 @@ public:
uint32_t getArity() const { return _currArity; }
- uint32_t getArg1() const { return _currArg1; }
-
- double getArg2() const { return _currArg2; }
-
- double getArg3() const { return _currArg3; }
+ uint32_t getNearDistance() const { return _extraIntArg1; }
+ uint32_t getTargetNumHits() const { return _extraIntArg1; }
+ double getScoreThreshold() const { return _extraDoubleArg4; }
+ double getThresholdBoostFactor() const { return _extraDoubleArg5; }
+ bool getAllowApproximate() const { return (_extraIntArg2 != 0); }
+ uint32_t getExploreAdditionalHits() const { return _extraIntArg3; }
query::PredicateQueryTerm::UP getPredicateQueryTerm()
{ return std::move(_predicate_query_term); }
diff --git a/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp b/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
index 24c458c7e32..3db6c8e68c8 100644
--- a/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
+++ b/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
@@ -41,7 +41,7 @@ QueryNode::Build(const QueryNode * parent, const QueryNodeResultFactory & factor
QueryConnector * qc = dynamic_cast<QueryConnector *> (qn.get());
NearQueryNode * nqn = dynamic_cast<NearQueryNode *> (qc);
if (nqn) {
- nqn->distance(queryRep.getArg1());
+ nqn->distance(queryRep.getNearDistance());
}
if ((type == ParseItem::ITEM_WEAK_AND) ||
(type == ParseItem::ITEM_WEIGHTED_SET) ||
diff --git a/searchlib/src/vespa/searchlib/query/tree/querybuilder.h b/searchlib/src/vespa/searchlib/query/tree/querybuilder.h
index 797defc39f5..8e6f2944ec9 100644
--- a/searchlib/src/vespa/searchlib/query/tree/querybuilder.h
+++ b/searchlib/src/vespa/searchlib/query/tree/querybuilder.h
@@ -205,8 +205,11 @@ createRegExpTerm(vespalib::stringref term, vespalib::stringref view, int32_t id,
template <class NodeTypes>
typename NodeTypes::NearestNeighborTerm *
create_nearest_neighbor_term(vespalib::stringref query_tensor_name, vespalib::stringref field_name,
- int32_t id, Weight weight, uint32_t target_num_hits) {
- return new typename NodeTypes::NearestNeighborTerm(query_tensor_name, field_name, id, weight, target_num_hits);
+ int32_t id, Weight weight, uint32_t target_num_hits,
+ bool allow_approximate, uint32_t explore_additional_hits)
+{
+ return new typename NodeTypes::NearestNeighborTerm(query_tensor_name, field_name, id, weight,
+ target_num_hits, allow_approximate, explore_additional_hits);
}
template <class NodeTypes>
@@ -317,9 +320,10 @@ public:
return addTerm(createRegExpTerm<NodeTypes>(term, view, id, weight));
}
typename NodeTypes::NearestNeighborTerm &add_nearest_neighbor_term(stringref query_tensor_name, stringref field_name,
- int32_t id, Weight weight, uint32_t target_num_hits) {
+ int32_t id, Weight weight, uint32_t target_num_hits,
+ bool allow_approximate, uint32_t explore_additional_hits) {
adjustWeight(weight);
- return addTerm(create_nearest_neighbor_term<NodeTypes>(query_tensor_name, field_name, id, weight, target_num_hits));
+ return addTerm(create_nearest_neighbor_term<NodeTypes>(query_tensor_name, field_name, id, weight, target_num_hits, allow_approximate, explore_additional_hits));
}
};
diff --git a/searchlib/src/vespa/searchlib/query/tree/queryreplicator.h b/searchlib/src/vespa/searchlib/query/tree/queryreplicator.h
index 0bf923960b9..9289df7cbe9 100644
--- a/searchlib/src/vespa/searchlib/query/tree/queryreplicator.h
+++ b/searchlib/src/vespa/searchlib/query/tree/queryreplicator.h
@@ -165,7 +165,8 @@ private:
void visit(NearestNeighborTerm &node) override {
replicate(node, _builder.add_nearest_neighbor_term(node.get_query_tensor_name(), node.getView(),
- node.getId(), node.getWeight(), node.get_target_num_hits()));
+ node.getId(), node.getWeight(), node.get_target_num_hits(),
+ node.get_allow_approximate(), node.get_explore_additional_hits()));
}
};
diff --git a/searchlib/src/vespa/searchlib/query/tree/simplequery.h b/searchlib/src/vespa/searchlib/query/tree/simplequery.h
index 8663bede4d6..4953f1a5b7c 100644
--- a/searchlib/src/vespa/searchlib/query/tree/simplequery.h
+++ b/searchlib/src/vespa/searchlib/query/tree/simplequery.h
@@ -105,8 +105,10 @@ struct SimpleRegExpTerm : RegExpTerm {
};
struct SimpleNearestNeighborTerm : NearestNeighborTerm {
SimpleNearestNeighborTerm(vespalib::stringref query_tensor_name, vespalib::stringref field_name,
- int32_t id, Weight weight, uint32_t target_num_hits)
- : NearestNeighborTerm(query_tensor_name, field_name, id, weight, target_num_hits)
+ int32_t id, Weight weight, uint32_t target_num_hits,
+ bool allow_approximate, uint32_t explore_additional_hits)
+ : NearestNeighborTerm(query_tensor_name, field_name, id, weight,
+ target_num_hits, allow_approximate, explore_additional_hits)
{}
};
diff --git a/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp b/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp
index 63acf532144..aafeaa46a22 100644
--- a/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp
+++ b/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp
@@ -263,6 +263,8 @@ class QueryNodeConverter : public QueryVisitor {
createTermNode(node, ParseItem::ITEM_NEAREST_NEIGHBOR);
appendString(node.get_query_tensor_name());
appendCompressedPositiveNumber(node.get_target_num_hits());
+ appendCompressedPositiveNumber(node.get_allow_approximate() ? 1 : 0);
+ appendCompressedPositiveNumber(node.get_explore_additional_hits());
}
public:
diff --git a/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h b/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h
index 791da010720..65d6abeeaad 100644
--- a/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h
+++ b/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h
@@ -48,9 +48,6 @@ public:
private:
static Term * createQueryTerm(search::SimpleQueryStackDumpIterator &queryStack, QueryBuilder<NodeTypes> & builder, vespalib::stringref & pureTermView) {
uint32_t arity = queryStack.getArity();
- uint32_t arg1 = queryStack.getArg1();
- double arg2 = queryStack.getArg2();
- double arg3 = queryStack.getArg3();
ParseItem::ItemType type = queryStack.getType();
Node::UP node;
Term *t = 0;
@@ -68,16 +65,19 @@ private:
pureTermView = view;
} else if (type == ParseItem::ITEM_WEAK_AND) {
vespalib::stringref view = queryStack.getIndexName();
- builder.addWeakAnd(arity, arg1, view);
+ uint32_t targetNumHits = queryStack.getTargetNumHits();
+ builder.addWeakAnd(arity, targetNumHits, view);
pureTermView = view;
} else if (type == ParseItem::ITEM_EQUIV) {
int32_t id = queryStack.getUniqueId();
Weight weight = queryStack.GetWeight();
builder.addEquiv(arity, id, weight);
} else if (type == ParseItem::ITEM_NEAR) {
- builder.addNear(arity, arg1);
+ uint32_t nearDistance = queryStack.getNearDistance();
+ builder.addNear(arity, nearDistance);
} else if (type == ParseItem::ITEM_ONEAR) {
- builder.addONear(arity, arg1);
+ uint32_t nearDistance = queryStack.getNearDistance();
+ builder.addONear(arity, nearDistance);
} else if (type == ParseItem::ITEM_PHRASE) {
vespalib::stringref view = queryStack.getIndexName();
int32_t id = queryStack.getUniqueId();
@@ -104,17 +104,24 @@ private:
vespalib::stringref view = queryStack.getIndexName();
int32_t id = queryStack.getUniqueId();
Weight weight = queryStack.GetWeight();
- t = &builder.addWandTerm(arity, view, id, weight, arg1, arg2, arg3);
+ uint32_t targetNumHits = queryStack.getTargetNumHits();
+ double scoreThreshold = queryStack.getScoreThreshold();
+ double thresholdBoostFactor = queryStack.getThresholdBoostFactor();
+ t = &builder.addWandTerm(arity, view, id, weight,
+ targetNumHits, scoreThreshold, thresholdBoostFactor);
pureTermView = vespalib::stringref();
} else if (type == ParseItem::ITEM_NOT) {
builder.addAndNot(arity);
} else if (type == ParseItem::ITEM_NEAREST_NEIGHBOR) {
vespalib::stringref query_tensor_name = queryStack.getTerm();
vespalib::stringref field_name = queryStack.getIndexName();
- uint32_t target_num_hits = queryStack.getArg1();
+ uint32_t target_num_hits = queryStack.getTargetNumHits();
int32_t id = queryStack.getUniqueId();
Weight weight = queryStack.GetWeight();
- builder.add_nearest_neighbor_term(query_tensor_name, field_name, id, weight, target_num_hits);
+ bool allow_approximate = queryStack.getAllowApproximate();
+ uint32_t explore_additional_hits = queryStack.getExploreAdditionalHits();
+ builder.add_nearest_neighbor_term(query_tensor_name, field_name, id, weight,
+ target_num_hits, allow_approximate, explore_additional_hits);
} else {
vespalib::stringref term = queryStack.getTerm();
vespalib::stringref view = queryStack.getIndexName();
diff --git a/searchlib/src/vespa/searchlib/query/tree/termnodes.h b/searchlib/src/vespa/searchlib/query/tree/termnodes.h
index a82b1e14d76..9af424716fb 100644
--- a/searchlib/src/vespa/searchlib/query/tree/termnodes.h
+++ b/searchlib/src/vespa/searchlib/query/tree/termnodes.h
@@ -128,17 +128,24 @@ class NearestNeighborTerm : public QueryNodeMixin<NearestNeighborTerm, TermNode>
private:
vespalib::string _query_tensor_name;
uint32_t _target_num_hits;
+ bool _allow_approximate;
+ uint32_t _explore_additional_hits;
public:
NearestNeighborTerm(vespalib::stringref query_tensor_name, vespalib::stringref field_name,
- int32_t id, Weight weight, uint32_t target_num_hits)
+ int32_t id, Weight weight, uint32_t target_num_hits,
+ bool allow_approximate, uint32_t explore_additional_hits)
: QueryNodeMixinType(field_name, id, weight),
_query_tensor_name(query_tensor_name),
- _target_num_hits(target_num_hits)
+ _target_num_hits(target_num_hits),
+ _allow_approximate(allow_approximate),
+ _explore_additional_hits(explore_additional_hits)
{}
virtual ~NearestNeighborTerm() {}
const vespalib::string& get_query_tensor_name() const { return _query_tensor_name; }
uint32_t get_target_num_hits() const { return _target_num_hits; }
+ bool get_allow_approximate() const { return _allow_approximate; }
+ uint32_t get_explore_additional_hits() const { return _explore_additional_hits; }
};
diff --git a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp
index d4aa2aaa1d7..d3b2925e075 100644
--- a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.cpp
@@ -13,11 +13,13 @@ namespace search::queryeval {
NearestNeighborBlueprint::NearestNeighborBlueprint(const queryeval::FieldSpec& field,
const tensor::DenseTensorAttribute& attr_tensor,
std::unique_ptr<vespalib::tensor::DenseTensorView> query_tensor,
- uint32_t target_num_hits)
+ uint32_t target_num_hits, bool approximate, uint32_t explore_additional_hits)
: ComplexLeafBlueprint(field),
_attr_tensor(attr_tensor),
_query_tensor(std::move(query_tensor)),
_target_num_hits(target_num_hits),
+ _approximate(approximate),
+ _explore_additional_hits(explore_additional_hits),
_distance_heap(target_num_hits),
_found_hits()
{
@@ -34,15 +36,14 @@ void
NearestNeighborBlueprint::perform_top_k()
{
auto nns_index = _attr_tensor.nearest_neighbor_index();
- if (nns_index) {
+ if (_approximate && nns_index) {
auto lhs_type = _query_tensor->fast_type();
auto rhs_type = _attr_tensor.getTensorType();
// XXX deal with different cell types later
if (lhs_type == rhs_type) {
auto lhs = _query_tensor->cellsRef();
uint32_t k = _target_num_hits;
- uint32_t explore_k = k + 100; // XXX hardcoded for now
- _found_hits = nns_index->find_top_k(k, lhs, explore_k);
+ _found_hits = nns_index->find_top_k(k, lhs, k + _explore_additional_hits);
}
}
}
@@ -73,6 +74,8 @@ NearestNeighborBlueprint::visitMembers(vespalib::ObjectVisitor& visitor) const
visitor.visitString("attribute_tensor", _attr_tensor.getTensorType().to_spec());
visitor.visitString("query_tensor", _query_tensor->type().to_spec());
visitor.visitInt("target_num_hits", _target_num_hits);
+ visitor.visitBool("approximate", _approximate);
+ visitor.visitInt("explore_additional_hits", _explore_additional_hits);
}
bool
diff --git a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h
index ab4413c487a..39165b066be 100644
--- a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_blueprint.h
@@ -21,6 +21,8 @@ private:
const tensor::DenseTensorAttribute& _attr_tensor;
std::unique_ptr<vespalib::tensor::DenseTensorView> _query_tensor;
uint32_t _target_num_hits;
+ bool _approximate;
+ uint32_t _explore_additional_hits;
mutable NearestNeighborDistanceHeap _distance_heap;
std::vector<search::tensor::NearestNeighborIndex::Neighbor> _found_hits;
@@ -29,7 +31,7 @@ public:
NearestNeighborBlueprint(const queryeval::FieldSpec& field,
const tensor::DenseTensorAttribute& attr_tensor,
std::unique_ptr<vespalib::tensor::DenseTensorView> query_tensor,
- uint32_t target_num_hits);
+ uint32_t target_num_hits, bool approximate, uint32_t explore_additional_hits);
NearestNeighborBlueprint(const NearestNeighborBlueprint&) = delete;
NearestNeighborBlueprint& operator=(const NearestNeighborBlueprint&) = delete;
~NearestNeighborBlueprint();
diff --git a/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt b/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt
index 0bdcd53af77..d0f33c6fd0b 100644
--- a/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt
@@ -11,6 +11,7 @@ vespa_add_library(searchlib_tensor OBJECT
hnsw_index.cpp
imported_tensor_attribute_vector.cpp
imported_tensor_attribute_vector_read_guard.cpp
+ inv_log_level_generator.cpp
nearest_neighbor_index.cpp
tensor_attribute.cpp
tensor_store.cpp
diff --git a/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.cpp b/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.cpp
index 68efe6417c0..8b0c5e931a4 100644
--- a/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.cpp
@@ -4,6 +4,7 @@
#include "distance_functions.h"
#include "hnsw_index.h"
#include "random_level_generator.h"
+#include "inv_log_level_generator.h"
#include <vespa/searchcommon/attribute/config.h>
namespace search::tensor {
@@ -27,24 +28,24 @@ make_distance_function(ValueType::CellType cell_type)
}
RandomLevelGenerator::UP
-make_random_level_generator()
+make_random_level_generator(uint32_t m)
{
- // TODO: Make generator that results in hierarchical graph.
- return std::make_unique<LevelZeroGenerator>();
+ return std::make_unique<InvLogLevelGenerator>(m);
}
-}
+} // namespace <unnamed>
std::unique_ptr<NearestNeighborIndex>
DefaultNearestNeighborIndexFactory::make(const DocVectorAccess& vectors,
vespalib::eval::ValueType::CellType cell_type,
const search::attribute::HnswIndexParams& params) const
{
- HnswIndex::Config cfg(params.max_links_per_node() * 2,
- params.max_links_per_node(),
+ uint32_t m = params.max_links_per_node();
+ HnswIndex::Config cfg(m * 2,
+ m,
params.neighbors_to_explore_at_insert(),
true);
- return std::make_unique<HnswIndex>(vectors, make_distance_function(cell_type), make_random_level_generator(), cfg);
+ return std::make_unique<HnswIndex>(vectors, make_distance_function(cell_type), make_random_level_generator(m), cfg);
}
}
diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp
index 229c6c2c34f..1ee54256a03 100644
--- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_attribute.cpp
@@ -207,7 +207,9 @@ vespalib::tensor::TypedCells
DenseTensorAttribute::get_vector(uint32_t docid) const
{
MutableDenseTensorView tensor_view(_denseTensorStore.type());
- getTensor(docid, tensor_view);
+ assert(docid < _refVector.size());
+ EntryRef ref = _refVector[docid];
+ _denseTensorStore.getTensor(ref, tensor_view);
return tensor_view.cellsRef();
}
diff --git a/searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.cpp b/searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.cpp
new file mode 100644
index 00000000000..540edc5e664
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.cpp
@@ -0,0 +1,3 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "inv_log_level_generator.h"
diff --git a/searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.h b/searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.h
new file mode 100644
index 00000000000..2f7f9f4445e
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/tensor/inv_log_level_generator.h
@@ -0,0 +1,35 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "random_level_generator.h"
+#include <random>
+
+namespace search::tensor {
+
+/**
+ * Geometric distribution for level selection in HnswIndex.
+ * Pr(level=k) is (1/M)^k * (1 - 1/M)
+ * Note that the level is theoretically unbounded, but in
+ * practice less than 30.
+ * Generated using floor(ln(U)/ln(1-p)), see
+ * https://en.wikipedia.org/wiki/Geometric_distribution#Related_distributions
+ **/
+
+class InvLogLevelGenerator : public RandomLevelGenerator {
+ std::mt19937_64 _rng;
+ std::uniform_real_distribution<double> _uniform;
+ double _levelMultiplier;
+public:
+ InvLogLevelGenerator(uint32_t m)
+ : _rng(0x1234deadbeef5678uLL),
+ _uniform(0.0, 1.0),
+ _levelMultiplier(1.0 / log(1.0 * m))
+ {}
+
+ uint32_t max_level() override {
+ double unif = _uniform(_rng);
+ double r = -log(1.0-unif) * _levelMultiplier;
+ return (uint32_t) r;
+ }
+};
+
+}
diff --git a/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp b/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp
index 43eaf4b57b8..4872a183358 100644
--- a/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp
+++ b/searchsummary/src/vespa/searchsummary/docsummary/dynamicteaserdfw.cpp
@@ -257,11 +257,11 @@ JuniperQueryAdapter::Traverse(juniper::IQueryVisitor *v) const
rc = SkipItem(&iterator);
break;
case search::ParseItem::ITEM_NEAR:
- if (!v->VisitNEAR(&item, iterator.getArity(),iterator.getArg1()))
+ if (!v->VisitNEAR(&item, iterator.getArity(),iterator.getNearDistance()))
rc = SkipItem(&iterator);
break;
case search::ParseItem::ITEM_ONEAR:
- if (!v->VisitWITHIN(&item, iterator.getArity(),iterator.getArg1()))
+ if (!v->VisitWITHIN(&item, iterator.getArity(),iterator.getNearDistance()))
rc = SkipItem(&iterator);
break;
// Unhandled items are just ignored by juniper
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java
index 1cfb70560b8..1065ed29dc4 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModel.java
@@ -2,14 +2,19 @@
package com.yahoo.vespa.service.duper;
import com.yahoo.config.model.api.ApplicationInfo;
+import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.service.monitor.DuperModelListener;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
+import java.util.Optional;
+import java.util.Set;
import java.util.logging.Logger;
/**
@@ -20,37 +25,106 @@ import java.util.logging.Logger;
public class DuperModel {
private static Logger logger = Logger.getLogger(DuperModel.class.getName());
- private final Map<ApplicationId, ApplicationInfo> applications = new TreeMap<>();
+ private final Map<ApplicationId, ApplicationInfo> applicationsById = new HashMap<>();
+ private final Map<HostName, ApplicationId> idsByHostname = new HashMap<>();
+ private final Map<ApplicationId, Set<HostName>> hostnamesById = new HashMap<>();
+
private final List<DuperModelListener> listeners = new ArrayList<>();
private boolean isComplete = false;
public void registerListener(DuperModelListener listener) {
- applications.values().forEach(listener::applicationActivated);
+ applicationsById.values().forEach(listener::applicationActivated);
listeners.add(listener);
}
- public void setCompleteness(boolean isComplete) { this.isComplete = isComplete; }
+ void setComplete() {
+ if (!isComplete) {
+ logger.log(LogLevel.INFO, "Bootstrap done - duper model is complete");
+ isComplete = true;
+
+ listeners.forEach(DuperModelListener::bootstrapComplete);
+ }
+ }
+
public boolean isComplete() { return isComplete; }
+ public int numberOfApplications() {
+ return applicationsById.size();
+ }
+
+ public int numberOfHosts() {
+ return idsByHostname.size();
+ }
+
public boolean contains(ApplicationId applicationId) {
- return applications.containsKey(applicationId);
+ return applicationsById.containsKey(applicationId);
+ }
+
+ public Optional<ApplicationInfo> getApplicationInfo(ApplicationId applicationId) {
+ return Optional.ofNullable(applicationsById.get(applicationId));
+ }
+
+ public Optional<ApplicationInfo> getApplicationInfo(HostName hostName) {
+ return Optional.ofNullable(idsByHostname.get(hostName)).map(applicationsById::get);
+ }
+
+ public List<ApplicationInfo> getApplicationInfos() {
+ return List.copyOf(applicationsById.values());
}
public void add(ApplicationInfo applicationInfo) {
- applications.put(applicationInfo.getApplicationId(), applicationInfo);
- logger.log(LogLevel.DEBUG, "Added " + applicationInfo.getApplicationId());
+ ApplicationId id = applicationInfo.getApplicationId();
+ ApplicationInfo oldApplicationInfo = applicationsById.put(id, applicationInfo);
+
+ final String logPrefix;
+ if (oldApplicationInfo == null) {
+ logPrefix = isComplete ? "New application " : "Bootstrapped application ";
+ } else {
+ logPrefix = isComplete ? "Reactivated application " : "Rebootstrapped application ";
+ }
+ logger.log(LogLevel.INFO, logPrefix + id);
+
+ Set<HostName> hostnames = hostnamesById.computeIfAbsent(id, k -> new HashSet<>());
+ Set<HostName> removedHosts = new HashSet<>(hostnames);
+
+ applicationInfo.getModel().getHosts().stream()
+ .map(HostInfo::getHostname)
+ .map(HostName::from)
+ .forEach(hostname -> {
+ if (!removedHosts.remove(hostname)) {
+ hostnames.add(hostname);
+ ApplicationId previousId = idsByHostname.put(hostname, id);
+
+ if (previousId != null && !previousId.equals(id)) {
+ // If an activation contains a host that is currently assigned to a
+ // different application we will patch up our data structures to remain
+ // internally consistent. But listeners may be fooled.
+ logger.log(LogLevel.WARNING, hostname + " has been reassigned from " +
+ previousId + " to " + id);
+
+ Set<HostName> previousHostnames = hostnamesById.get(previousId);
+ if (previousHostnames != null) {
+ previousHostnames.remove(hostname);
+ }
+ }
+ }
+ });
+
+ removedHosts.forEach(idsByHostname::remove);
+
listeners.forEach(listener -> listener.applicationActivated(applicationInfo));
}
public void remove(ApplicationId applicationId) {
- if (applications.remove(applicationId) != null) {
- logger.log(LogLevel.DEBUG, "Removed " + applicationId);
- listeners.forEach(listener -> listener.applicationRemoved(applicationId));
+ Set<HostName> hostnames = hostnamesById.remove(applicationId);
+ if (hostnames != null) {
+ hostnames.forEach(idsByHostname::remove);
}
- }
- public List<ApplicationInfo> getApplicationInfos() {
- logger.log(LogLevel.DEBUG, "Applications in duper model: " + applications.values().size());
- return List.copyOf(applications.values());
+ ApplicationInfo application = applicationsById.remove(applicationId);
+ if (application != null) {
+ logger.log(LogLevel.INFO, "Removed application " + applicationId);
+ listeners.forEach(listener -> listener.applicationRemoved(applicationId));
+ }
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
index 15c461c7f59..9c93bc1d390 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/DuperModelManager.java
@@ -173,6 +173,18 @@ public class DuperModelManager implements DuperModelProvider, DuperModelInfraApi
}
}
+ public Optional<ApplicationInfo> getApplicationInfo(ApplicationId applicationId) {
+ synchronized (monitor) {
+ return duperModel.getApplicationInfo(applicationId);
+ }
+ }
+
+ public Optional<ApplicationInfo> getApplicationInfo(HostName hostname) {
+ synchronized (monitor) {
+ return duperModel.getApplicationInfo(hostname);
+ }
+ }
+
public List<ApplicationInfo> getApplicationInfos() {
synchronized (monitor) {
return duperModel.getApplicationInfos();
@@ -181,7 +193,7 @@ public class DuperModelManager implements DuperModelProvider, DuperModelInfraApi
private void maybeSetDuperModelAsComplete() {
if (superModelIsComplete && infraApplicationsIsComplete) {
- duperModel.setCompleteness(true);
+ duperModel.setComplete();
}
}
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java
index 09140423010..7b79adf89d0 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/duper/InfraApplication.java
@@ -26,6 +26,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -74,7 +75,7 @@ public abstract class InfraApplication implements InfraApplicationApi {
@Override
public ClusterSpec getClusterSpecWithVersion(Version version) {
- return ClusterSpec.request(clusterSpecType, clusterSpecId, version, true);
+ return ClusterSpec.request(clusterSpecType, clusterSpecId, version, true, Optional.empty());
}
public ClusterSpec.Type getClusterSpecType() {
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java
index 5cc2d538c24..0116b992e23 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ApplicationInstanceGenerator.java
@@ -5,9 +5,13 @@ import com.yahoo.config.model.api.ApplicationInfo;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ApplicationInstanceId;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.HostName;
@@ -24,7 +28,9 @@ import com.yahoo.vespa.service.monitor.ServiceStatusProvider;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -37,23 +43,73 @@ public class ApplicationInstanceGenerator {
private final ApplicationInfo applicationInfo;
private final Zone zone;
- private ApplicationId configServerApplicationId;
+
+ // This is cheating a bit, but we don't expect DuperModel's config server application ID to be different.
+ // We do this to avoid passing through the ID through multiple levels.
+ private static final ApplicationId configServerApplicationId = new ConfigServerApplication().getApplicationId();
public ApplicationInstanceGenerator(ApplicationInfo applicationInfo, Zone zone) {
this.applicationInfo = applicationInfo;
this.zone = zone;
-
- // This is cheating a bit, but we don't expect DuperModel's config server application ID to be different.
- // We do this to avoid passing through the ID through multiple levels.
- this.configServerApplicationId = new ConfigServerApplication().getApplicationId();
}
public ApplicationInstance makeApplicationInstance(ServiceStatusProvider serviceStatusProvider) {
+ return makeApplicationInstanceLimitedToHosts(serviceStatusProvider, hostname -> true);
+ }
+
+ public ApplicationInstanceReference toApplicationInstanceReference() {
+ TenantId tenantId = new TenantId(applicationInfo.getApplicationId().tenant().toString());
+ ApplicationInstanceId applicationInstanceId = toApplicationInstanceId(applicationInfo.getApplicationId(), zone);
+ return new ApplicationInstanceReference(tenantId, applicationInstanceId);
+ }
+
+ public boolean containsHostname(HostName hostname) {
+ return applicationInfo.getModel().getHosts().stream()
+ .map(HostInfo::getHostname)
+ .anyMatch(hostnameString -> Objects.equals(hostnameString, hostname.s()));
+ }
+
+ public ApplicationInstance makeApplicationInstanceLimitedTo(
+ HostName hostname, ServiceStatusProvider serviceStatusProvider) {
+ return makeApplicationInstanceLimitedToHosts(
+ serviceStatusProvider, candidateHostname -> candidateHostname.equals(hostname));
+ }
+
+ /** Reverse of toApplicationInstanceId, put in this file because it its inverse is. */
+ public static ApplicationId toApplicationId(ApplicationInstanceReference reference) {
+
+ String appNameStr = reference.asString();
+ String[] appNameParts = appNameStr.split(":");
+
+ // Env, region and instance seems to be optional due to the hardcoded config server app
+ // Assume here that first two are tenant and application name.
+ if (appNameParts.length == 2) {
+ return ApplicationId.from(TenantName.from(appNameParts[0]),
+ ApplicationName.from(appNameParts[1]),
+ InstanceName.defaultName());
+ }
+
+ // Other normal application should have 5 parts.
+ if (appNameParts.length != 5) {
+ throw new IllegalArgumentException("Application reference not valid (not 5 parts): " + reference);
+ }
+
+ return ApplicationId.from(TenantName.from(appNameParts[0]),
+ ApplicationName.from(appNameParts[1]),
+ InstanceName.from(appNameParts[4]));
+ }
+
+ private ApplicationInstance makeApplicationInstanceLimitedToHosts(ServiceStatusProvider serviceStatusProvider,
+ Predicate<HostName> includeHostPredicate) {
Map<ServiceClusterKey, Set<ServiceInstance>> groupedServiceInstances = new HashMap<>();
for (HostInfo host : applicationInfo.getModel().getHosts()) {
HostName hostName = new HostName(host.getHostname());
+ if (!includeHostPredicate.test(hostName)) {
+ continue;
+ }
+
for (ServiceInfo serviceInfo : host.getServices()) {
ServiceClusterKey serviceClusterKey = toServiceClusterKey(serviceInfo);
@@ -77,10 +133,8 @@ public class ApplicationInstanceGenerator {
entry.getValue()))
.collect(Collectors.toSet());
- ApplicationInstance applicationInstance = new ApplicationInstance(
- new TenantId(applicationInfo.getApplicationId().tenant().toString()),
- toApplicationInstanceId(applicationInfo, zone),
- serviceClusters);
+ ApplicationInstanceReference reference = toApplicationInstanceReference();
+ ApplicationInstance applicationInstance = new ApplicationInstance(reference, serviceClusters);
// Fill back-references
for (ServiceCluster serviceCluster : applicationInstance.serviceClusters()) {
@@ -106,18 +160,18 @@ public class ApplicationInstanceGenerator {
return new ServiceInstance(configId, hostName, status);
}
- private ApplicationInstanceId toApplicationInstanceId(ApplicationInfo applicationInfo, Zone zone) {
- if (applicationInfo.getApplicationId().equals(configServerApplicationId)) {
+ private static ApplicationInstanceId toApplicationInstanceId(ApplicationId applicationId, Zone zone) {
+ if (applicationId.equals(configServerApplicationId)) {
// Removing this historical discrepancy would break orchestration during rollout.
// An alternative may be to use a feature flag and flip it between releases,
// once that's available.
- return new ApplicationInstanceId(applicationInfo.getApplicationId().application().value());
+ return new ApplicationInstanceId(applicationId.application().value());
} else {
return new ApplicationInstanceId(String.format("%s:%s:%s:%s",
- applicationInfo.getApplicationId().application().value(),
+ applicationId.application().value(),
zone.environment().value(),
zone.region().value(),
- applicationInfo.getApplicationId().instance().value()));
+ applicationId.instance().value()));
}
}
@@ -129,7 +183,7 @@ public class ApplicationInstanceGenerator {
toConfigId(serviceInfo));
}
- public static ClusterId getClusterId(ServiceInfo serviceInfo) {
+ private static ClusterId getClusterId(ServiceInfo serviceInfo) {
return new ClusterId(serviceInfo.getProperty(CLUSTER_ID_PROPERTY_NAME).orElse(""));
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java
index a6bf41d6c9b..d9378da957e 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ModelGenerator.java
@@ -5,11 +5,13 @@ import com.yahoo.config.model.api.ApplicationInfo;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.service.monitor.ServiceModel;
import com.yahoo.vespa.service.monitor.ServiceStatusProvider;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -19,13 +21,18 @@ import java.util.stream.Collectors;
public class ModelGenerator {
public static final String CLUSTER_ID_PROPERTY_NAME = "clustername";
+ private final Zone zone;
+
+ public ModelGenerator(Zone zone) {
+ this.zone = zone;
+ }
+
/**
* Create service model based primarily on super model.
*
* If the configServerhosts is non-empty, a config server application is added.
*/
public ServiceModel toServiceModel(List<ApplicationInfo> allApplicationInfos,
- Zone zone,
ServiceStatusProvider serviceStatusProvider) {
Map<ApplicationInstanceReference, ApplicationInstance> applicationInstances =
allApplicationInfos.stream()
@@ -36,4 +43,27 @@ public class ModelGenerator {
return new ServiceModel(applicationInstances);
}
+ public Set<ApplicationInstanceReference> toApplicationInstanceReferenceSet(List<ApplicationInfo> infos) {
+ return infos.stream()
+ .map(info -> new ApplicationInstanceGenerator(info, zone).toApplicationInstanceReference())
+ .collect(Collectors.toSet());
+ }
+
+ public ApplicationInstance toApplicationInstance(ApplicationInfo applicationInfo,
+ ServiceStatusProvider serviceStatusProvider) {
+ var generator = new ApplicationInstanceGenerator(applicationInfo, zone);
+ return generator.makeApplicationInstance(serviceStatusProvider);
+ }
+
+ /**
+ * Make an application instance that contains all services and clusters present on the host,
+ * but lacking other services and hosts. This is an optimization over
+ * {@link #toApplicationInstance(ApplicationInfo, ServiceStatusProvider)}.
+ */
+ public ApplicationInstance toApplicationNarrowedToHost(ApplicationInfo applicationInfo,
+ HostName hostname,
+ ServiceStatusProvider serviceStatusProvider) {
+ var generator = new ApplicationInstanceGenerator(applicationInfo, zone);
+ return generator.makeApplicationInstanceLimitedTo(hostname, serviceStatusProvider);
+ }
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelCache.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelCache.java
index 1b37555a554..29b2832efd0 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelCache.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelCache.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.service.model;
import com.yahoo.jdisc.Timer;
import com.yahoo.vespa.service.monitor.ServiceModel;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import java.util.function.Supplier;
@@ -12,12 +13,11 @@ import java.util.function.Supplier;
*
* @author hakonhall
*/
-public class ServiceModelCache implements Supplier<ServiceModel> {
+public class ServiceModelCache implements ServiceMonitor {
public static final long EXPIRY_MILLIS = 10000;
private final Supplier<ServiceModel> expensiveSupplier;
private final Timer timer;
- private final boolean useCache;
private volatile ServiceModel snapshot;
private boolean updatePossiblyInProgress = false;
@@ -25,18 +25,13 @@ public class ServiceModelCache implements Supplier<ServiceModel> {
private final Object updateMonitor = new Object();
private long snapshotMillis;
- public ServiceModelCache(Supplier<ServiceModel> expensiveSupplier, Timer timer, boolean useCache) {
+ public ServiceModelCache(Supplier<ServiceModel> expensiveSupplier, Timer timer) {
this.expensiveSupplier = expensiveSupplier;
this.timer = timer;
- this.useCache = useCache;
}
@Override
- public ServiceModel get() {
- if (!useCache) {
- return expensiveSupplier.get();
- }
-
+ public ServiceModel getServiceModelSnapshot() {
if (snapshot == null) {
synchronized (updateMonitor) {
if (snapshot == null) {
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java
index c6947810fa0..45ee38ab560 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceModelProvider.java
@@ -2,33 +2,40 @@
package com.yahoo.vespa.service.model;
import com.yahoo.config.model.api.ApplicationInfo;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.applicationmodel.ApplicationInstance;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.applicationmodel.ServiceInstance;
+import com.yahoo.vespa.service.duper.DuperModelManager;
import com.yahoo.vespa.service.monitor.ServiceModel;
+import com.yahoo.vespa.service.monitor.ServiceMonitor;
import com.yahoo.vespa.service.monitor.ServiceStatusProvider;
-import com.yahoo.vespa.service.manager.MonitorManager;
-import com.yahoo.vespa.service.duper.DuperModelManager;
import java.util.List;
-import java.util.function.Supplier;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
/**
* An uncached supplier of ServiceModel based on the DuperModel and MonitorManager.
*
* @author hakonhall
*/
-public class ServiceModelProvider implements Supplier<ServiceModel> {
+public class ServiceModelProvider implements ServiceMonitor {
private final ServiceMonitorMetrics metrics;
private final DuperModelManager duperModelManager;
private final ModelGenerator modelGenerator;
private final Zone zone;
- private final MonitorManager monitorManager;
+ private final ServiceStatusProvider serviceStatusProvider;
- public ServiceModelProvider(MonitorManager monitorManager,
+ public ServiceModelProvider(ServiceStatusProvider serviceStatusProvider,
ServiceMonitorMetrics metrics,
DuperModelManager duperModelManager,
ModelGenerator modelGenerator,
Zone zone) {
- this.monitorManager = monitorManager;
+ this.serviceStatusProvider = serviceStatusProvider;
this.metrics = metrics;
this.duperModelManager = duperModelManager;
this.modelGenerator = modelGenerator;
@@ -36,13 +43,57 @@ public class ServiceModelProvider implements Supplier<ServiceModel> {
}
@Override
- public ServiceModel get() {
+ public ServiceModel getServiceModelSnapshot() {
try (LatencyMeasurement measurement = metrics.startServiceModelSnapshotLatencyMeasurement()) {
- // WARNING: The monitor manager may be out-of-sync with duper model (no locking)
- List<ApplicationInfo> applicationInfos = duperModelManager.getApplicationInfos();
+ return modelGenerator.toServiceModel(duperModelManager.getApplicationInfos(), serviceStatusProvider);
+ }
+ }
+
+ @Override
+ public Set<ApplicationInstanceReference> getAllApplicationInstanceReferences() {
+ return modelGenerator.toApplicationInstanceReferenceSet(duperModelManager.getApplicationInfos());
+ }
- return modelGenerator.toServiceModel(applicationInfos, zone, (ServiceStatusProvider) monitorManager);
+ @Override
+ public Optional<ApplicationInstance> getApplication(HostName hostname) {
+ Optional<ApplicationInfo> applicationInfo = getApplicationInfo(hostname);
+ if (applicationInfo.isEmpty()) {
+ return Optional.empty();
}
+
+ return Optional.of(modelGenerator.toApplicationInstance(applicationInfo.get(), serviceStatusProvider));
+ }
+
+ @Override
+ public Optional<ApplicationInstance> getApplication(ApplicationInstanceReference reference) {
+ return getApplicationInfo(reference)
+ .map(applicationInfo -> modelGenerator.toApplicationInstance(applicationInfo, serviceStatusProvider));
}
+ @Override
+ public Optional<ApplicationInstance> getApplicationNarrowedTo(HostName hostname) {
+ Optional<ApplicationInfo> applicationInfo = getApplicationInfo(hostname);
+ if (applicationInfo.isEmpty()) {
+ return Optional.empty();
+ }
+
+ return Optional.of(modelGenerator.toApplicationNarrowedToHost(
+ applicationInfo.get(), hostname, serviceStatusProvider));
+ }
+
+ @Override
+ public Map<HostName, List<ServiceInstance>> getServicesByHostname() {
+ return getServiceModelSnapshot().getServiceInstancesByHostName();
+ }
+
+ private Optional<ApplicationInfo> getApplicationInfo(ApplicationInstanceReference reference) {
+ ApplicationId applicationId = ApplicationInstanceGenerator.toApplicationId(reference);
+ return duperModelManager.getApplicationInfo(applicationId);
+ }
+
+ private Optional<ApplicationInfo> getApplicationInfo(HostName hostname) {
+ // The duper model uses HostName from config.provision, which is more natural than applicationmodel.
+ var configProvisionHostname = com.yahoo.config.provision.HostName.from(hostname.s());
+ return duperModelManager.getApplicationInfo(configProvisionHostname);
+ }
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceMonitorImpl.java b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceMonitorImpl.java
index 67b4e890c29..d3297d711ff 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceMonitorImpl.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/model/ServiceMonitorImpl.java
@@ -5,6 +5,10 @@ import com.google.inject.Inject;
import com.yahoo.config.provision.Zone;
import com.yahoo.jdisc.Metric;
import com.yahoo.jdisc.Timer;
+import com.yahoo.vespa.applicationmodel.ApplicationInstance;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.applicationmodel.ServiceInstance;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.service.duper.DuperModelManager;
@@ -12,9 +16,14 @@ import com.yahoo.vespa.service.manager.UnionMonitorManager;
import com.yahoo.vespa.service.monitor.ServiceModel;
import com.yahoo.vespa.service.monitor.ServiceMonitor;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
public class ServiceMonitorImpl implements ServiceMonitor {
- private final ServiceModelCache serviceModelProvider;
+ private final ServiceMonitor delegate;
@Inject
public ServiceMonitorImpl(DuperModelManager duperModelManager,
@@ -25,19 +34,47 @@ public class ServiceMonitorImpl implements ServiceMonitor {
FlagSource flagSource) {
duperModelManager.registerListener(monitorManager);
- ServiceModelProvider uncachedServiceModelProvider = new ServiceModelProvider(
+ ServiceMonitor serviceMonitor = new ServiceModelProvider(
monitorManager,
new ServiceMonitorMetrics(metric, timer),
duperModelManager,
- new ModelGenerator(),
+ new ModelGenerator(zone),
zone);
- boolean cache = Flags.SERVICE_MODEL_CACHE.bindTo(flagSource).value();
- serviceModelProvider = new ServiceModelCache(uncachedServiceModelProvider, timer, cache);
+
+ if (Flags.SERVICE_MODEL_CACHE.bindTo(flagSource).value()) {
+ delegate = new ServiceModelCache(serviceMonitor::getServiceModelSnapshot, timer);
+ } else {
+ delegate = serviceMonitor;
+ }
}
@Override
public ServiceModel getServiceModelSnapshot() {
- return serviceModelProvider.get();
+ return delegate.getServiceModelSnapshot();
+ }
+
+ @Override
+ public Set<ApplicationInstanceReference> getAllApplicationInstanceReferences() {
+ return delegate.getAllApplicationInstanceReferences();
+ }
+
+ @Override
+ public Optional<ApplicationInstance> getApplication(HostName hostname) {
+ return delegate.getApplication(hostname);
}
+ @Override
+ public Optional<ApplicationInstance> getApplication(ApplicationInstanceReference reference) {
+ return delegate.getApplication(reference);
+ }
+
+ @Override
+ public Optional<ApplicationInstance> getApplicationNarrowedTo(HostName hostname) {
+ return delegate.getApplicationNarrowedTo(hostname);
+ }
+
+ @Override
+ public Map<HostName, List<ServiceInstance>> getServicesByHostname() {
+ return delegate.getServicesByHostname();
+ }
}
diff --git a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceMonitor.java b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceMonitor.java
index 49539c61e5d..deafaaace0e 100644
--- a/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceMonitor.java
+++ b/service-monitor/src/main/java/com/yahoo/vespa/service/monitor/ServiceMonitor.java
@@ -1,6 +1,16 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.service.monitor;
+import com.yahoo.vespa.applicationmodel.ApplicationInstance;
+import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference;
+import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.applicationmodel.ServiceInstance;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
/**
* The service monitor interface. A service monitor provides up to date information about the liveness status
* (up, down or not known) of each service instance in a Vespa zone
@@ -12,7 +22,29 @@ public interface ServiceMonitor {
/**
* Returns a ServiceModel which contains the current liveness status (up, down or unknown) of all instances
* of all services of all clusters of all applications in a zone.
+ *
+ * <p>Please use the more specific methods below to avoid the cost of this method.</p>
*/
ServiceModel getServiceModelSnapshot();
+ default Set<ApplicationInstanceReference> getAllApplicationInstanceReferences() {
+ return getServiceModelSnapshot().getAllApplicationInstances().keySet();
+ }
+
+ default Optional<ApplicationInstance> getApplication(HostName hostname) {
+ return Optional.ofNullable(getServiceModelSnapshot().getApplicationsByHostName().get(hostname));
+ }
+
+ default Optional<ApplicationInstance> getApplication(ApplicationInstanceReference reference) {
+ return getServiceModelSnapshot().getApplicationInstance(reference);
+ }
+
+ default Optional<ApplicationInstance> getApplicationNarrowedTo(HostName hostname) {
+ return getApplication(hostname);
+ }
+
+ default Map<HostName, List<ServiceInstance>> getServicesByHostname() {
+ return getServiceModelSnapshot().getServiceInstancesByHostName();
+ }
+
}
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelManagerTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelManagerTest.java
index 67508f14e5a..3448ce46ff9 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelManagerTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelManagerTest.java
@@ -71,7 +71,6 @@ public class DuperModelManagerTest {
verify(duperModel, times(0)).add(any());
manager.infraApplicationActivated(id, proxyHostHosts);
verify(duperModel, times(1)).add(any());
- when(duperModel.contains(id)).thenReturn(true);
verify(duperModel, times(0)).remove(any());
manager.infraApplicationRemoved(id);
@@ -98,7 +97,6 @@ public class DuperModelManagerTest {
List<HostName> hostnames1 = Stream.of("node11", "node12").map(HostName::from).collect(Collectors.toList());
manager.infraApplicationActivated(firstId, hostnames1);
verify(duperModel, times(1)).add(any());
- when(duperModel.contains(firstId)).thenReturn(true);
// Adding the second config server like application will be ignored
List<HostName> hostnames2 = Stream.of("node21", "node22").map(HostName::from).collect(Collectors.toList());
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java
index dc90035be71..69b6d3d59f3 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/duper/DuperModelTest.java
@@ -2,16 +2,20 @@
package com.yahoo.vespa.service.duper;
import com.yahoo.config.model.api.ApplicationInfo;
+import com.yahoo.config.model.api.HostInfo;
+import com.yahoo.config.model.api.Model;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.service.monitor.DuperModelListener;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -23,37 +27,60 @@ import static org.mockito.Mockito.when;
*/
public class DuperModelTest {
private final DuperModel duperModel = new DuperModel();
- private final ApplicationInfo application1 = mock(ApplicationInfo.class);
+
private final ApplicationId id1 = ApplicationId.fromSerializedForm("tenant:app1:default");
+ private final ApplicationInfo application1 = mock(ApplicationInfo.class);
+ private final HostName hostname1_1 = HostName.from("hostname1-1");
+ private final HostName hostname1_2 = HostName.from("hostname1-2");
+
private final ApplicationId id2 = ApplicationId.fromSerializedForm("tenant:app2:default");
private final ApplicationInfo application2 = mock(ApplicationInfo.class);
+ private final HostName hostname2_1 = HostName.from("hostname2-1");
+
private final DuperModelListener listener1 = mock(DuperModelListener.class);
@Before
public void setUp() {
- when(application1.getApplicationId()).thenReturn(id1);
- when(application2.getApplicationId()).thenReturn(id2);
+ setUpApplication(id1, application1, hostname1_1, hostname1_2);
+ setUpApplication(id2, application2, hostname2_1);
+ }
+
+ private void setUpApplication(ApplicationId id, ApplicationInfo info, HostName... hostnames) {
+ when(info.getApplicationId()).thenReturn(id);
+
+ Model model = mock(Model.class);
+ when(info.getModel()).thenReturn(model);
+
+ List<HostInfo> hostInfos = Arrays.stream(hostnames)
+ .map(hostname -> new HostInfo(hostname.value(), List.of()))
+ .collect(Collectors.toList());
+ when(model.getHosts()).thenReturn(hostInfos);
}
@Test
- public void test() {
+ public void testListeners() {
+ assertEquals(0, duperModel.numberOfApplications());
+
duperModel.add(application1);
- assertTrue(duperModel.contains(id1));
+ assertEquals(Optional.of(application1), duperModel.getApplicationInfo(id1));
assertEquals(Arrays.asList(application1), duperModel.getApplicationInfos());
+ assertEquals(1, duperModel.numberOfApplications());
duperModel.registerListener(listener1);
verify(listener1, times(1)).applicationActivated(application1);
verifyNoMoreInteractions(listener1);
duperModel.remove(id2);
+ assertEquals(1, duperModel.numberOfApplications());
verifyNoMoreInteractions(listener1);
duperModel.add(application2);
+ assertEquals(2, duperModel.numberOfApplications());
verify(listener1, times(1)).applicationActivated(application2);
verifyNoMoreInteractions(listener1);
duperModel.remove(id1);
- assertFalse(duperModel.contains(id1));
+ assertEquals(Optional.empty(), duperModel.getApplicationInfo(id1));
verify(listener1, times(1)).applicationRemoved(id1);
verifyNoMoreInteractions(listener1);
assertEquals(Arrays.asList(application2), duperModel.getApplicationInfos());
@@ -61,4 +88,31 @@ public class DuperModelTest {
duperModel.remove(id1);
verifyNoMoreInteractions(listener1);
}
+
+ @Test
+ public void hostIndices() {
+ assertEquals(0, duperModel.numberOfHosts());
+
+ duperModel.add(application1);
+ assertEquals(2, duperModel.numberOfHosts());
+ assertEquals(Optional.of(application1), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.empty(), duperModel.getApplicationInfo(hostname2_1));
+
+ duperModel.add(application2);
+ assertEquals(3, duperModel.numberOfHosts());
+ assertEquals(Optional.of(application1), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.of(application2), duperModel.getApplicationInfo(hostname2_1));
+
+ duperModel.remove(application1.getApplicationId());
+ assertEquals(1, duperModel.numberOfHosts());
+ assertEquals(Optional.empty(), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.of(application2), duperModel.getApplicationInfo(hostname2_1));
+
+ // Remove hostname2_1 and add hostname1_1 added to id2
+ setUpApplication(id2, application2, hostname1_1);
+ duperModel.add(application2);
+ assertEquals(1, duperModel.numberOfHosts());
+ assertEquals(Optional.of(application2), duperModel.getApplicationInfo(hostname1_1));
+ assertEquals(Optional.empty(), duperModel.getApplicationInfo(hostname2_1));
+ }
} \ No newline at end of file
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java
index ce13cf60082..7be4ff38a7f 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ModelGeneratorTest.java
@@ -38,9 +38,9 @@ public class ModelGeneratorTest {
@Test
public void toApplicationModel() throws Exception {
- ModelGenerator modelGenerator = new ModelGenerator();
-
Zone zone = new Zone(Environment.from(ENVIRONMENT), RegionName.from(REGION));
+ ModelGenerator modelGenerator = new ModelGenerator(zone);
+
SlobrokMonitorManagerImpl slobrokMonitorManager = mock(SlobrokMonitorManagerImpl.class);
when(slobrokMonitorManager.getStatus(any(), any(), any(), any()))
@@ -49,7 +49,6 @@ public class ModelGeneratorTest {
ServiceModel serviceModel =
modelGenerator.toServiceModel(
getExampleApplicationInfos(),
- zone,
slobrokMonitorManager);
Map<ApplicationInstanceReference,
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelCacheTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelCacheTest.java
index c2314be1e0f..21c6a49cc18 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelCacheTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelCacheTest.java
@@ -16,27 +16,27 @@ import static org.mockito.Mockito.when;
public class ServiceModelCacheTest {
@SuppressWarnings("unchecked")
- private final Supplier<ServiceModel> rawSupplier = mock(Supplier.class);
+ private final Supplier<ServiceModel> expensiveServiceMonitor = mock(Supplier.class);
private final Timer timer = mock(Timer.class);
- private final ServiceModelCache cache = new ServiceModelCache(rawSupplier, timer, true);
+ private final ServiceModelCache cache = new ServiceModelCache(expensiveServiceMonitor, timer);
@Test
public void sanityCheck() {
ServiceModel serviceModel = mock(ServiceModel.class);
- when(rawSupplier.get()).thenReturn(serviceModel);
+ when(expensiveServiceMonitor.get()).thenReturn(serviceModel);
long timeMillis = 0;
when(timer.currentTimeMillis()).thenReturn(timeMillis);
// Will always populate cache the first time
- ServiceModel actualServiceModel = cache.get();
+ ServiceModel actualServiceModel = cache.getServiceModelSnapshot();
assertTrue(actualServiceModel == serviceModel);
- verify(rawSupplier, times(1)).get();
+ verify(expensiveServiceMonitor, times(1)).get();
// Cache hit
timeMillis += ServiceModelCache.EXPIRY_MILLIS / 2;
when(timer.currentTimeMillis()).thenReturn(timeMillis);
- actualServiceModel = cache.get();
+ actualServiceModel = cache.getServiceModelSnapshot();
assertTrue(actualServiceModel == serviceModel);
// Cache expired
@@ -44,17 +44,17 @@ public class ServiceModelCacheTest {
when(timer.currentTimeMillis()).thenReturn(timeMillis);
ServiceModel serviceModel2 = mock(ServiceModel.class);
- when(rawSupplier.get()).thenReturn(serviceModel2);
+ when(expensiveServiceMonitor.get()).thenReturn(serviceModel2);
- actualServiceModel = cache.get();
+ actualServiceModel = cache.getServiceModelSnapshot();
assertTrue(actualServiceModel == serviceModel2);
// '2' because it's cumulative with '1' from the first times(1).
- verify(rawSupplier, times(2)).get();
+ verify(expensiveServiceMonitor, times(2)).get();
// Cache hit #2
timeMillis += 1;
when(timer.currentTimeMillis()).thenReturn(timeMillis);
- actualServiceModel = cache.get();
+ actualServiceModel = cache.getServiceModelSnapshot();
assertTrue(actualServiceModel == serviceModel2);
}
} \ No newline at end of file
diff --git a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelProviderTest.java b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelProviderTest.java
index 13f6da1534d..f34d61970ef 100644
--- a/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelProviderTest.java
+++ b/service-monitor/src/test/java/com/yahoo/vespa/service/model/ServiceModelProviderTest.java
@@ -37,8 +37,8 @@ public class ServiceModelProviderTest {
.collect(Collectors.toList());
when(duperModelManager.getApplicationInfos()).thenReturn(applications);
- ServiceModel serviceModel = provider.get();
+ ServiceModel serviceModel = provider.getServiceModelSnapshot();
verify(duperModelManager, times(1)).getApplicationInfos();
- verify(modelGenerator).toServiceModel(applications, zone, slobrokMonitorManager);
+ verify(modelGenerator).toServiceModel(applications, slobrokMonitorManager);
}
} \ No newline at end of file
diff --git a/staging_vespalib/CMakeLists.txt b/staging_vespalib/CMakeLists.txt
index 3c4e2a9f444..9fb48cbd4fc 100644
--- a/staging_vespalib/CMakeLists.txt
+++ b/staging_vespalib/CMakeLists.txt
@@ -30,6 +30,7 @@ vespa_define_module(
src/tests/shutdownguard
src/tests/state_server
src/tests/stllike
+ src/tests/singleexecutor
src/tests/timer
src/tests/util/process_memory_stats
src/tests/xmlserializable
diff --git a/staging_vespalib/src/tests/singleexecutor/CMakeLists.txt b/staging_vespalib/src/tests/singleexecutor/CMakeLists.txt
new file mode 100644
index 00000000000..c5d42d2c8c5
--- /dev/null
+++ b/staging_vespalib/src/tests/singleexecutor/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(staging_vespalib_singleexecutor_test_app TEST
+ SOURCES
+ singleexecutor_test.cpp
+ DEPENDS
+ staging_vespalib
+)
+vespa_add_test(NAME staging_vespalib_singleexecutor_test_app COMMAND staging_vespalib_singleexecutor_test_app)
diff --git a/staging_vespalib/src/tests/singleexecutor/singleexecutor_test.cpp b/staging_vespalib/src/tests/singleexecutor/singleexecutor_test.cpp
new file mode 100644
index 00000000000..5dacaa5d204
--- /dev/null
+++ b/staging_vespalib/src/tests/singleexecutor/singleexecutor_test.cpp
@@ -0,0 +1,80 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/testkit/testapp.h>
+
+#include <vespa/vespalib/util/singleexecutor.h>
+#include <vespa/vespalib/util/lambdatask.h>
+#include <atomic>
+
+using namespace vespalib;
+
+TEST("test that all tasks are executed") {
+
+ std::atomic<uint64_t> counter(0);
+ SingleExecutor executor(10);
+
+ for (uint64_t i(0); i < 10; i++) {
+ executor.execute(makeLambdaTask([&counter] {counter++;}));
+ }
+ executor.sync();
+ EXPECT_EQUAL(10u, counter);
+
+ counter = 0;
+ for (uint64_t i(0); i < 10000; i++) {
+ executor.execute(makeLambdaTask([&counter] {counter++;}));
+ }
+ executor.sync();
+ EXPECT_EQUAL(10000u, counter);
+}
+
+void verifyResizeTaskLimit(bool up) {
+ Monitor lock;
+ std::atomic<uint64_t> started(0);
+ std::atomic<uint64_t> allowed(0);
+ SingleExecutor executor(10);
+
+ uint32_t targetTaskLimit = up ? 20 : 5;
+ uint32_t roundedTaskLimit = roundUp2inN(targetTaskLimit);
+ EXPECT_NOT_EQUAL(16u, roundedTaskLimit);
+
+ for (uint64_t i(0); i < 10; i++) {
+ executor.execute(makeLambdaTask([&lock, &started, &allowed] {
+ started++;
+ MonitorGuard guard(lock);
+ while (allowed < started) {
+ guard.wait(1ms);
+ }
+ }));
+ }
+ while (started < 1);
+ EXPECT_EQUAL(1u, started);
+ executor.setTaskLimit(targetTaskLimit);
+ EXPECT_EQUAL(16u, executor.getTaskLimit());
+ allowed = 5;
+ while (started < 6);
+ EXPECT_EQUAL(6u, started);
+ EXPECT_EQUAL(16u, executor.getTaskLimit());
+ allowed = 10;
+ while (started < 10);
+ EXPECT_EQUAL(10u, started);
+ EXPECT_EQUAL(16u, executor.getTaskLimit());
+ executor.execute(makeLambdaTask([&lock, &started, &allowed] {
+ started++;
+ MonitorGuard guard(lock);
+ while (allowed < started) {
+ guard.wait(1ms);
+ }
+ }));
+ while (started < 11);
+ EXPECT_EQUAL(11u, started);
+ EXPECT_EQUAL(roundedTaskLimit, executor.getTaskLimit());
+ allowed = 11;
+}
+TEST("test that resizing up and down works") {
+ TEST_DO(verifyResizeTaskLimit(true));
+ TEST_DO(verifyResizeTaskLimit(false));
+
+
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt b/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt
index 71364a813f6..ba03b77c941 100644
--- a/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt
+++ b/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt
@@ -17,6 +17,7 @@ vespa_add_library(staging_vespalib_vespalib_util OBJECT
rusage.cpp
shutdownguard.cpp
scheduledexecutor.cpp
+ singleexecutor.cpp
xmlserializable.cpp
xmlstream.cpp
DEPENDS
diff --git a/staging_vespalib/src/vespa/vespalib/util/singleexecutor.cpp b/staging_vespalib/src/vespa/vespalib/util/singleexecutor.cpp
new file mode 100644
index 00000000000..791b0876c19
--- /dev/null
+++ b/staging_vespalib/src/vespa/vespalib/util/singleexecutor.cpp
@@ -0,0 +1,131 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "singleexecutor.h"
+#include <vespa/vespalib/util/time.h>
+
+namespace vespalib {
+
+SingleExecutor::SingleExecutor(uint32_t taskLimit)
+ : _taskLimit(vespalib::roundUp2inN(taskLimit)),
+ _wantedTaskLimit(_taskLimit.load()),
+ _rp(0),
+ _tasks(std::make_unique<Task::UP[]>(_taskLimit)),
+ _consumerMonitor(),
+ _producerMonitor(),
+ _thread(*this),
+ _lastAccepted(0),
+ _maxPending(0),
+ _wakeupConsumerAt(0),
+ _producerNeedWakeup(false),
+ _wp(0)
+{
+ _thread.start();
+}
+SingleExecutor::~SingleExecutor() {
+ sync();
+ _thread.stop().join();
+}
+
+size_t
+SingleExecutor::getNumThreads() const {
+ return 1;
+}
+
+uint64_t
+SingleExecutor::addTask(Task::UP task) {
+ MonitorGuard guard(_producerMonitor);
+ wait_for_room(guard);
+ uint64_t wp = _wp.load(std::memory_order_relaxed);
+ _tasks[index(wp)] = std::move(task);
+ _wp.store(wp + 1, std::memory_order_release);
+ return wp;
+}
+
+Executor::Task::UP
+SingleExecutor::execute(Task::UP task) {
+ uint64_t wp = addTask(std::move(task));
+ if (wp == _wakeupConsumerAt.load(std::memory_order_relaxed)) {
+ MonitorGuard guard(_consumerMonitor);
+ guard.signal();
+ }
+ return task;
+}
+
+void
+SingleExecutor::setTaskLimit(uint32_t taskLimit) {
+ _wantedTaskLimit = vespalib::roundUp2inN(taskLimit);
+}
+
+SingleExecutor &
+SingleExecutor::sync() {
+ uint64_t wp = _wp.load(std::memory_order_relaxed);
+ while (wp > _rp.load(std::memory_order_relaxed)) {
+ std::this_thread::sleep_for(1ms);
+ }
+ return *this;
+}
+
+void
+SingleExecutor::run() {
+ while (!_thread.stopped()) {
+ drain_tasks();
+ _wakeupConsumerAt.store(_wp.load(std::memory_order_relaxed) + (_taskLimit.load(std::memory_order_relaxed) >> 2), std::memory_order_relaxed);
+ MonitorGuard guard(_consumerMonitor);
+ guard.wait(10ms);
+ _wakeupConsumerAt.store(0, std::memory_order_relaxed);
+ }
+}
+
+void
+SingleExecutor::drain_tasks() {
+ while (numTasks() > 0) {
+ run_tasks_till(_wp.load(std::memory_order_acquire));
+ }
+}
+
+void
+SingleExecutor::run_tasks_till(uint64_t available) {
+ uint64_t consumed = _rp.load(std::memory_order_relaxed);
+ uint64_t left = available - consumed;
+ if (_maxPending.load(std::memory_order_relaxed) < left) {
+ _maxPending.store(left, std::memory_order_relaxed);
+ }
+ uint64_t wakeupLimit = _producerNeedWakeup.load(std::memory_order_relaxed)
+ ? (available - (left >> 2))
+ : 0;
+ while (consumed < available) {
+ Task::UP task = std::move(_tasks[index(consumed)]);
+ task->run();
+ _rp.store(++consumed, std::memory_order_release);
+ if (wakeupLimit == consumed) {
+ MonitorGuard guard(_producerMonitor);
+ guard.broadcast();
+ }
+ }
+}
+
+void
+SingleExecutor::wait_for_room(MonitorGuard & producerGuard) {
+ if (_taskLimit.load(std::memory_order_relaxed) != _wantedTaskLimit.load(std::memory_order_relaxed)) {
+ sync();
+ _tasks = std::make_unique<Task::UP[]>(_wantedTaskLimit);
+ _taskLimit = _wantedTaskLimit.load();
+ }
+ while (numTasks() >= _taskLimit.load(std::memory_order_relaxed)) {
+ _producerNeedWakeup.store(true, std::memory_order_relaxed);
+ producerGuard.wait(10ms);
+ _producerNeedWakeup.store(false, std::memory_order_relaxed);
+ }
+}
+
+ThreadExecutor::Stats
+SingleExecutor::getStats() {
+ uint64_t accepted = _wp.load(std::memory_order_relaxed);
+ Stats stats(_maxPending, (accepted - _lastAccepted), 0);
+ _lastAccepted = accepted;
+ _maxPending = 0;
+ return stats;
+}
+
+
+}
diff --git a/staging_vespalib/src/vespa/vespalib/util/singleexecutor.h b/staging_vespalib/src/vespa/vespalib/util/singleexecutor.h
new file mode 100644
index 00000000000..0ab89355566
--- /dev/null
+++ b/staging_vespalib/src/vespa/vespalib/util/singleexecutor.h
@@ -0,0 +1,55 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/util/threadexecutor.h>
+#include <vespa/vespalib/util/thread.h>
+#include <thread>
+#include <atomic>
+
+namespace vespalib {
+
+/**
+ * Has a single thread consuming tasks from a fixed size ringbuffer.
+ * Made for throughput where the producer has no interaction with the consumer and
+ * it is hence very cheap to produce a task. High and low watermark at 25%/75% is used
+ * to reduce ping-pong.
+ */
+class SingleExecutor final : public vespalib::SyncableThreadExecutor, vespalib::Runnable {
+public:
+ explicit SingleExecutor(uint32_t taskLimit);
+ ~SingleExecutor() override;
+ Task::UP execute(Task::UP task) override;
+ void setTaskLimit(uint32_t taskLimit) override;
+ SingleExecutor & sync() override;
+ size_t getNumThreads() const override;
+ uint32_t getTaskLimit() const { return _taskLimit.load(std::memory_order_relaxed); }
+ Stats getStats() override;
+private:
+ uint64_t addTask(Task::UP task);
+ void run() override;
+ void drain_tasks();
+ void run_tasks_till(uint64_t available);
+ void wait_for_room(MonitorGuard & guard);
+ uint64_t index(uint64_t counter) const {
+ return counter & (_taskLimit.load(std::memory_order_relaxed) - 1);
+ }
+
+ uint64_t numTasks() const {
+ return _wp.load(std::memory_order_relaxed) - _rp.load(std::memory_order_acquire);
+ }
+ std::atomic<uint32_t> _taskLimit;
+ std::atomic<uint32_t> _wantedTaskLimit;
+ std::atomic<uint64_t> _rp;
+ std::unique_ptr<Task::UP[]> _tasks;
+ vespalib::Monitor _consumerMonitor;
+ vespalib::Monitor _producerMonitor;
+ vespalib::Thread _thread;
+ uint64_t _lastAccepted;
+ std::atomic<uint64_t> _maxPending;
+ std::atomic<uint64_t> _wakeupConsumerAt;
+ std::atomic<bool> _producerNeedWakeup;
+ std::atomic<uint64_t> _wp;
+};
+
+}
diff --git a/standalone-container/CMakeLists.txt b/standalone-container/CMakeLists.txt
index 83c58e09945..6e8bf49d846 100644
--- a/standalone-container/CMakeLists.txt
+++ b/standalone-container/CMakeLists.txt
@@ -1,2 +1,3 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
install_fat_java_artifact(standalone-container)
+install(PROGRAMS src/main/sh/standalone-container.sh DESTINATION libexec/vespa)
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java
index 28001e8e8d2..13991199a1f 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/zpe/AuthorizationResult.java
@@ -44,7 +44,8 @@ public class AuthorizationResult {
DENY_CERT_MISMATCH_ISSUER(AccessCheckStatus.DENY_CERT_MISMATCH_ISSUER),
DENY_CERT_MISSING_SUBJECT(AccessCheckStatus.DENY_CERT_MISSING_SUBJECT),
DENY_CERT_MISSING_DOMAIN(AccessCheckStatus.DENY_CERT_MISSING_DOMAIN),
- DENY_CERT_MISSING_ROLE_NAME(AccessCheckStatus.DENY_CERT_MISSING_ROLE_NAME);
+ DENY_CERT_MISSING_ROLE_NAME(AccessCheckStatus.DENY_CERT_MISSING_ROLE_NAME),
+ DENY_CERT_HASH_MISMATCH(AccessCheckStatus.DENY_CERT_HASH_MISMATCH);
private final AccessCheckStatus wrappedElement;
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
index 851b1fa214c..cc714f38290 100644
--- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
@@ -60,25 +60,7 @@ public class DeployMojo extends AbstractVespaDeploymentMojo {
}
private void tailLogs(ApplicationId id, ZoneId zone, long run) throws MojoFailureException, MojoExecutionException {
- long last = -1;
- DeploymentLog log;
- while (true) {
- log = controller.deploymentLog(id, zone, run, last);
- for (DeploymentLog.Entry entry : log.entries())
- print(entry);
- last = log.last().orElse(last);
-
- if ( ! log.isActive())
- break;
-
- try {
- Thread.sleep(1000);
- }
- catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- break;
- }
- }
+ DeploymentLog log = controller.followDeploymentUntilDone(id, zone, run, this::print);
switch (log.status()) {
case success: return;
case error: throw new MojoExecutionException("Unexpected error during deployment; see log for details");
diff --git a/vespajlib/src/main/java/com/yahoo/collections/AbstractFilteringList.java b/vespajlib/src/main/java/com/yahoo/collections/AbstractFilteringList.java
index 2e51a4ceafe..176e5044bc2 100644
--- a/vespajlib/src/main/java/com/yahoo/collections/AbstractFilteringList.java
+++ b/vespajlib/src/main/java/com/yahoo/collections/AbstractFilteringList.java
@@ -4,15 +4,14 @@ package com.yahoo.collections;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Optional;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
-import static java.util.stream.Collectors.reducing;
import static java.util.stream.Collectors.toUnmodifiableList;
/**
@@ -20,7 +19,7 @@ import static java.util.stream.Collectors.toUnmodifiableList;
*
* @author jonmv
*/
-public abstract class AbstractFilteringList<Type, ListType extends AbstractFilteringList<Type, ListType>> {
+public abstract class AbstractFilteringList<Type, ListType extends AbstractFilteringList<Type, ListType>> implements Iterable<Type> {
private final List<Type> items;
private final boolean negate;
@@ -84,4 +83,9 @@ public abstract class AbstractFilteringList<Type, ListType extends AbstractFilte
public final int size() { return items.size(); }
+ @Override
+ public Iterator<Type> iterator() {
+ return items.iterator();
+ }
+
}
diff --git a/vespalib/src/vespa/vespalib/util/executor.h b/vespalib/src/vespa/vespalib/util/executor.h
index ef5cb4b84fa..97cde4ffbe2 100644
--- a/vespalib/src/vespa/vespalib/util/executor.h
+++ b/vespalib/src/vespa/vespalib/util/executor.h
@@ -23,6 +23,8 @@ public:
virtual ~Task() {}
};
+ enum class OptimizeFor {LATENCY, THROUGHPUT};
+
/**
* Execute the given task using one of the internal threads some
* time in the future. The task may also be rejected in which case