summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cloud-tenant-base-dependencies-enforcer/pom.xml3
-rw-r--r--clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingCurator.java16
-rw-r--r--clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingMaintainer.java13
-rw-r--r--clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/http/ReindexingV1ApiHandler.java98
-rw-r--r--clustercontroller-reindexer/src/test/java/ai/vespa/reindexing/http/ReindexingV1ApiTest.java80
-rw-r--r--config-model-api/abi-spec.json9
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/ValidationId.java3
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java31
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java9
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java4
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeReindexAction.java4
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java52
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ReindexingController.java48
-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/application/validation/Validation.java22
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidator.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidator.java17
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaRefeedAction.java33
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaReindexAction.java36
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidator.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidator.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java31
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java20
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java26
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidatorTest.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java39
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidatorTest.java8
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java14
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java79
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidatorTest.java9
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java25
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java25
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterResources.java10
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java17
-rw-r--r--configdefinitions/src/vespa/lb-services.def1
-rw-r--r--configdefinitions/src/vespa/zookeeper-server.def4
-rw-r--r--configserver/pom.xml5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java21
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ClusterReindexing.java58
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java211
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverter.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActions.java22
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatter.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActions.java13
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatter.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java80
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java11
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/model/LbServicesProducer.java10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java1
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java32
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java13
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverterTest.java15
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockRefeedAction.java17
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatterTest.java22
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java17
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatterTest.java22
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/configchange/Utils.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java28
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java100
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java12
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/Path.java18
-rw-r--r--container-dependency-versions/pom.xml2
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java6
-rw-r--r--container-search/abi-spec.json1
-rw-r--r--container-search/src/main/java/com/yahoo/search/Query.java14
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java5
-rw-r--r--container/pom.xml4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/RefeedAction.java4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/ReindexAction.java4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ApplicationReindexing.java49
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java29
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java33
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/PrepareResponse.java1
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java11
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java1
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java12
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ScalingEventData.java31
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java3
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java127
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java34
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java29
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java29
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json34
-rw-r--r--document/src/test/resources/tensor/multi_cell_tensor__cppbin107 -> 107 bytes
-rw-r--r--document/src/vespa/document/serialization/vespadocumentdeserializer.h2
-rw-r--r--documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages60TestCase.java3
-rw-r--r--documentapi/src/tests/messages/messages60test.cpp19
-rw-r--r--documentapi/src/tests/replymerger/replymerger_test.cpp1
-rw-r--r--documentapi/src/vespa/documentapi/loadtypes/loadtypeset.cpp2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp93
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.cpp8
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.h26
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/messages/documentreply.cpp6
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/messages/documentreply.h2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/messages/testandsetcondition.h4
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.cpp3
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.h3
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.cpp14
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.cpp16
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.h3
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/loadbalancerpolicy.cpp1
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.cpp4
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/storagepolicy.cpp2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/routablefactories60.cpp9
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/routablefactories60.h14
-rw-r--r--documentapi/test/crosslanguagefiles/6.221-cpp-GetBucketListMessage.datbin29 -> 29 bytes
-rw-r--r--documentapi/test/crosslanguagefiles/6.221-java-GetBucketListMessage.datbin29 -> 29 bytes
-rw-r--r--eval/CMakeLists.txt5
-rw-r--r--eval/src/tests/eval/engine_or_factory/engine_or_factory_override_test.cpp8
-rw-r--r--eval/src/tests/eval/engine_or_factory/engine_or_factory_test.cpp8
-rw-r--r--eval/src/tests/eval/value_type/value_type_test.cpp2
-rw-r--r--eval/src/tests/instruction/generic_concat/generic_concat_test.cpp64
-rw-r--r--eval/src/tests/instruction/generic_create/generic_create_test.cpp24
-rw-r--r--eval/src/tests/instruction/generic_join/generic_join_test.cpp22
-rw-r--r--eval/src/tests/instruction/generic_map/generic_map_test.cpp15
-rw-r--r--eval/src/tests/instruction/generic_merge/generic_merge_test.cpp28
-rw-r--r--eval/src/tests/instruction/generic_peek/generic_peek_test.cpp57
-rw-r--r--eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp43
-rw-r--r--eval/src/tests/instruction/generic_rename/generic_rename_test.cpp19
-rw-r--r--eval/src/tests/streamed/value/CMakeLists.txt9
-rw-r--r--eval/src/tests/streamed/value/streamed_value_test.cpp136
-rw-r--r--eval/src/tests/tensor/default_value_builder_factory/CMakeLists.txt9
-rw-r--r--eval/src/tests/tensor/default_value_builder_factory/default_value_builder_factory_test.cpp64
-rw-r--r--eval/src/tests/tensor/direct_sparse_tensor_builder/direct_sparse_tensor_builder_test.cpp3
-rw-r--r--eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp112
-rw-r--r--eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp6
-rw-r--r--eval/src/tests/tensor/packed_mappings/CMakeLists.txt19
-rw-r--r--eval/src/tests/tensor/packed_mappings/packed_mappings_test.cpp225
-rw-r--r--eval/src/tests/tensor/packed_mappings/packed_mixed_test.cpp158
-rw-r--r--eval/src/tests/tensor/partial_remove/partial_remove_test.cpp19
-rw-r--r--eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp6
-rw-r--r--eval/src/vespa/eval/CMakeLists.txt2
-rw-r--r--eval/src/vespa/eval/eval/cell_type.cpp16
-rw-r--r--eval/src/vespa/eval/eval/cell_type.h24
-rw-r--r--eval/src/vespa/eval/eval/engine_or_factory.cpp10
-rw-r--r--eval/src/vespa/eval/eval/fast_sparse_map.cpp6
-rw-r--r--eval/src/vespa/eval/eval/fast_sparse_map.h3
-rw-r--r--eval/src/vespa/eval/eval/fast_value.hpp24
-rw-r--r--eval/src/vespa/eval/eval/llvm/llvm_wrapper.cpp2
-rw-r--r--eval/src/vespa/eval/eval/simple_tensor.cpp1
-rw-r--r--eval/src/vespa/eval/eval/simple_value.cpp2
-rw-r--r--eval/src/vespa/eval/eval/tensor_function.cpp62
-rw-r--r--eval/src/vespa/eval/eval/tensor_function.h26
-rw-r--r--eval/src/vespa/eval/eval/test/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/eval/test/reference_operations.cpp287
-rw-r--r--eval/src/vespa/eval/eval/test/reference_operations.h37
-rw-r--r--eval/src/vespa/eval/eval/test/tensor_model.hpp1
-rw-r--r--eval/src/vespa/eval/eval/typed_cells.h2
-rw-r--r--eval/src/vespa/eval/eval/value_codec.cpp4
-rw-r--r--eval/src/vespa/eval/eval/value_type.cpp1
-rw-r--r--eval/src/vespa/eval/eval/value_type.h1
-rw-r--r--eval/src/vespa/eval/eval/value_type_spec.cpp4
-rw-r--r--eval/src/vespa/eval/instruction/dense_dot_product_function.cpp6
-rw-r--r--eval/src/vespa/eval/instruction/dense_lambda_peek_function.cpp2
-rw-r--r--eval/src/vespa/eval/instruction/dense_matmul_function.cpp2
-rw-r--r--eval/src/vespa/eval/instruction/dense_multi_matmul_function.cpp8
-rw-r--r--eval/src/vespa/eval/instruction/dense_tensor_peek_function.cpp6
-rw-r--r--eval/src/vespa/eval/instruction/dense_xw_product_function.cpp2
-rw-r--r--eval/src/vespa/eval/instruction/generic_create.h4
-rw-r--r--eval/src/vespa/eval/instruction/generic_lambda.cpp2
-rw-r--r--eval/src/vespa/eval/instruction/generic_merge.cpp14
-rw-r--r--eval/src/vespa/eval/instruction/generic_peek.cpp2
-rw-r--r--eval/src/vespa/eval/instruction/generic_peek.h5
-rw-r--r--eval/src/vespa/eval/streamed/CMakeLists.txt11
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value.cpp28
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value.h48
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_builder.cpp13
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_builder.h66
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_builder_factory.cpp36
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_builder_factory.h24
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_index.cpp100
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_index.h36
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_utils.cpp9
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_utils.h76
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_view.cpp9
-rw-r--r--eval/src/vespa/eval/streamed/streamed_value_view.h45
-rw-r--r--eval/src/vespa/eval/tensor/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/tensor/default_tensor_engine.cpp3
-rw-r--r--eval/src/vespa/eval/tensor/default_value_builder_factory.cpp57
-rw-r--r--eval/src/vespa/eval/tensor/default_value_builder_factory.h24
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_lambda_function.cpp4
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_number_join_function.cpp5
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_simple_join_function.cpp7
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_simple_map_function.cpp2
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp4
-rw-r--r--eval/src/vespa/eval/tensor/dense/onnx_wrapper.cpp13
-rw-r--r--eval/src/vespa/eval/tensor/dense/typed_cells_dispatch.h4
-rw-r--r--eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp2
-rw-r--r--eval/src/vespa/eval/tensor/mixed/CMakeLists.txt11
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_labels.cpp67
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_labels.h44
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mappings.cpp140
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mappings.h114
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.cpp117
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.h62
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.cpp245
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.h62
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.cpp51
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.h40
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.cpp36
-rw-r--r--eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h25
-rw-r--r--eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp2
-rw-r--r--eval/src/vespa/eval/tensor/serialization/dense_binary_format.h2
-rw-r--r--eval/src/vespa/eval/tensor/serialization/sparse_binary_format.cpp2
-rw-r--r--eval/src/vespa/eval/tensor/serialization/sparse_binary_format.h2
-rw-r--r--eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp2
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java20
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java4
-rw-r--r--http-utils/pom.xml16
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java96
-rw-r--r--jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java2
-rw-r--r--jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java3
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilter.java21
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java66
-rw-r--r--jdisc_http_service/pom.xml1
-rw-r--r--messagebus/src/tests/simpleprotocol/simpleprotocol.cpp20
-rw-r--r--messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp2
-rw-r--r--messagebus/src/vespa/messagebus/error.cpp10
-rw-r--r--messagebus/src/vespa/messagebus/error.h2
-rw-r--r--messagebus/src/vespa/messagebus/message.cpp1
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsend.cpp2
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsend.h4
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsendv1.cpp9
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsendv1.h2
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsendv2.cpp8
-rw-r--r--messagebus/src/vespa/messagebus/network/rpcsendv2.h2
-rw-r--r--messagebus/src/vespa/messagebus/reply.cpp22
-rw-r--r--messagebus/src/vespa/messagebus/reply.h8
-rw-r--r--messagebus/src/vespa/messagebus/result.cpp64
-rw-r--r--messagebus/src/vespa/messagebus/result.h64
-rw-r--r--messagebus/src/vespa/messagebus/routable.h9
-rw-r--r--messagebus/src/vespa/messagebus/routing/routingnode.cpp13
-rw-r--r--messagebus/src/vespa/messagebus/sendproxy.cpp7
-rw-r--r--messagebus_test/src/tests/trace/trace.cpp8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java25
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java95
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java50
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java17
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java138
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java106
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java5
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java9
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java80
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java32
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java7
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java35
-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/AutoscalingMaintainerTest.java7
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java7
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java57
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/TestMetric.java9
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java12
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java7
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java15
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java102
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java27
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json32
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json32
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node9.json3
-rw-r--r--orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java1
-rw-r--r--orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java1
-rw-r--r--parent/pom.xml6
-rw-r--r--persistence/src/vespa/persistence/conformancetest/conformancetest.cpp108
-rw-r--r--persistence/src/vespa/persistence/spi/context.cpp5
-rw-r--r--persistence/src/vespa/persistence/spi/context.h15
-rw-r--r--searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.cpp5
-rw-r--r--searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.h2
-rw-r--r--searchcore/src/apps/vespa-feed-bm/spi_bm_feed_handler.cpp6
-rw-r--r--searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.cpp5
-rw-r--r--searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.h2
-rw-r--r--searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp7
-rw-r--r--searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp54
-rw-r--r--searchcore/src/tests/proton/reference/gid_to_lid_change_handler/gid_to_lid_change_handler_test.cpp91
-rw-r--r--searchcore/src/vespa/searchcore/config/proton.def2
-rw-r--r--searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/CMakeLists.txt2
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.cpp23
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.h9
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h12
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h18
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_change.h44
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.cpp29
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.h26
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.cpp50
-rw-r--r--searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.h35
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt1
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.cpp9
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.h6
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/putdonecontext.cpp11
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/putdonecontext.h9
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.cpp27
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.h38
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/removedonecontext.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/removedonecontext.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp83
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/mock_gid_to_lid_change_handler.h5
-rw-r--r--searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp5
-rw-r--r--searchlib/src/tests/queryeval/nearest_neighbor/nearest_neighbor_test.cpp2
-rw-r--r--searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp10
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_iterator.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.h2
-rw-r--r--searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/tensor/distance_function_factory.cpp13
-rw-r--r--searchlib/src/vespa/searchlib/tensor/distance_function_factory.h2
-rw-r--r--searchlib/src/vespa/searchlib/tensor/nearest_neighbor_index_factory.h2
-rw-r--r--simplemetrics/src/main/java/com/yahoo/metrics/simple/MetricReceiver.java81
-rw-r--r--storage/src/tests/persistence/common/filestortestfixture.cpp8
-rw-r--r--storage/src/tests/persistence/common/filestortestfixture.h2
-rw-r--r--storage/src/tests/persistence/filestorage/filestormanagertest.cpp14
-rw-r--r--storage/src/tests/persistence/filestorage/mergeblockingtest.cpp2
-rw-r--r--storage/src/tests/persistence/filestorage/operationabortingtest.cpp4
-rw-r--r--storage/src/tests/persistence/mergehandlertest.cpp5
-rw-r--r--storage/src/tests/persistence/persistencetestutils.cpp49
-rw-r--r--storage/src/tests/persistence/persistencethread_splittest.cpp6
-rw-r--r--storage/src/tests/persistence/splitbitdetectortest.cpp7
-rw-r--r--storage/src/tests/persistence/testandsettest.cpp2
-rw-r--r--storage/src/tests/storageserver/documentapiconvertertest.cpp1
-rw-r--r--storage/src/tests/visiting/memory_bounded_trace_test.cpp10
-rw-r--r--storage/src/tests/visiting/visitormanagertest.cpp2
-rw-r--r--storage/src/tests/visiting/visitortest.cpp5
-rw-r--r--storage/src/vespa/storage/config/stor-communicationmanager.def2
-rw-r--r--storage/src/vespa/storage/distributor/bucketdbupdater.h1
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/getoperation.cpp4
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/getoperation.h1
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/putoperation.h1
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.cpp39
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.h6
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/updateoperation.h1
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp8
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/visitoroperation.h2
-rw-r--r--storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp1
-rw-r--r--storage/src/vespa/storage/distributor/operations/idealstate/removebucketoperation.cpp5
-rw-r--r--storage/src/vespa/storage/distributor/operations/idealstate/splitoperation.cpp2
-rw-r--r--storage/src/vespa/storage/distributor/operations/operation.cpp6
-rw-r--r--storage/src/vespa/storage/distributor/operations/operation.h1
-rw-r--r--storage/src/vespa/storage/distributor/pendingmessagetracker.h1
-rw-r--r--storage/src/vespa/storage/distributor/persistencemessagetracker.cpp20
-rw-r--r--storage/src/vespa/storage/distributor/persistencemessagetracker.h2
-rw-r--r--storage/src/vespa/storage/distributor/sentmessagemap.cpp29
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestorhandler.h5
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp19
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h6
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp4
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestormanager.h1
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestormetrics.cpp20
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestormetrics.h10
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/mergestatus.cpp7
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/mergestatus.h8
-rw-r--r--storage/src/vespa/storage/persistence/mergehandler.cpp54
-rw-r--r--storage/src/vespa/storage/persistence/mergehandler.h10
-rw-r--r--storage/src/vespa/storage/persistence/persistenceutil.cpp16
-rw-r--r--storage/src/vespa/storage/persistence/persistenceutil.h25
-rw-r--r--storage/src/vespa/storage/persistence/simplemessagehandler.cpp1
-rw-r--r--storage/src/vespa/storage/persistence/splitjoinhandler.cpp7
-rw-r--r--storage/src/vespa/storage/persistence/types.h3
-rw-r--r--storage/src/vespa/storage/storageserver/communicationmanager.cpp31
-rw-r--r--storage/src/vespa/storage/storageserver/communicationmanagermetrics.cpp16
-rw-r--r--storage/src/vespa/storage/storageserver/communicationmanagermetrics.h6
-rw-r--r--storage/src/vespa/storage/storageserver/documentapiconverter.cpp12
-rw-r--r--storage/src/vespa/storage/storageserver/mergethrottler.cpp3
-rw-r--r--storage/src/vespa/storage/storageserver/rpc/storage_api_rpc_service.cpp4
-rw-r--r--storage/src/vespa/storage/visiting/memory_bounded_trace.cpp44
-rw-r--r--storage/src/vespa/storage/visiting/memory_bounded_trace.h5
-rw-r--r--storage/src/vespa/storage/visiting/visitor.cpp38
-rw-r--r--storage/src/vespa/storage/visiting/visitor.h18
-rw-r--r--storage/src/vespa/storage/visiting/visitorthread.cpp28
-rw-r--r--storageapi/src/tests/mbusprot/storageprotocoltest.cpp30
-rw-r--r--storageapi/src/tests/messageapi/storage_message_address_test.cpp8
-rw-r--r--storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp2
-rw-r--r--storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.cpp6
-rw-r--r--storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.h6
-rw-r--r--storageapi/src/vespa/storageapi/mbusprot/protocolserialization7.cpp1
-rw-r--r--storageapi/src/vespa/storageapi/message/bucket.h2
-rw-r--r--storageapi/src/vespa/storageapi/message/visitor.h15
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/bucketreply.cpp6
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/returncode.cpp52
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/returncode.h14
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagecommand.cpp1
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagecommand.h2
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp85
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagemessage.h35
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagereply.cpp7
-rw-r--r--storageapi/src/vespa/storageapi/messageapi/storagereply.h2
-rw-r--r--storageserver/src/tests/storageservertest.cpp27
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java11
-rw-r--r--vespajlib/src/main/java/com/yahoo/concurrent/StripedExecutor.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/concurrent/ThreadLocalDirectory.java46
-rw-r--r--vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java2
-rw-r--r--vespalib/src/tests/spin_lock/spin_lock_test.cpp1
-rw-r--r--vespalib/src/tests/trace/trace.cpp24
-rw-r--r--vespalib/src/vespa/vespalib/data/slime/cursor.h7
-rw-r--r--vespalib/src/vespa/vespalib/trace/trace.cpp72
-rw-r--r--vespalib/src/vespa/vespalib/trace/trace.h90
-rw-r--r--vespalib/src/vespa/vespalib/trace/tracenode.cpp69
-rw-r--r--vespalib/src/vespa/vespalib/trace/tracenode.h8
-rw-r--r--zkfacade/abi-spec.json18
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/ConnectionSpec.java102
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java142
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/CuratorCompletionWaiter.java2
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java19
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/api/package-info.java10
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java1561
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCuratorFramework.java1356
-rw-r--r--zkfacade/src/main/java/com/yahoo/vespa/curator/package-info.java2
-rw-r--r--zkfacade/src/test/java/com/yahoo/vespa/curator/ConnectionSpecTest.java74
-rw-r--r--zkfacade/src/test/java/com/yahoo/vespa/curator/CuratorTest.java46
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java41
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java49
438 files changed, 6567 insertions, 6092 deletions
diff --git a/cloud-tenant-base-dependencies-enforcer/pom.xml b/cloud-tenant-base-dependencies-enforcer/pom.xml
index 5b8bd2032d6..16dbd6b4014 100644
--- a/cloud-tenant-base-dependencies-enforcer/pom.xml
+++ b/cloud-tenant-base-dependencies-enforcer/pom.xml
@@ -30,7 +30,7 @@
<javax.inject.version>1</javax.inject.version>
<javax.servlet-api.version>3.1.0</javax.servlet-api.version>
<jaxb.version>2.3.0</jaxb.version>
- <jetty.version>9.4.32.v20200930</jetty.version>
+ <jetty.version>9.4.35.v20201120</jetty.version>
<junit5.version>5.7.0</junit5.version>
<junit5.platform.version>1.7.0</junit5.platform.version>
<org.lz4.version>1.7.1</org.lz4.version>
@@ -259,6 +259,7 @@
<include>org.eclipse.jetty:jetty-server:[${jetty.version}]:jar:test</include>
<include>org.eclipse.jetty:jetty-servlet:[${jetty.version}]:jar:test</include>
<include>org.eclipse.jetty:jetty-servlets:[${jetty.version}]:jar:test</include>
+ <include>org.eclipse.jetty:jetty-util-ajax:[${jetty.version}]:jar:test</include>
<include>org.hamcrest:hamcrest-core:1.3:jar:test</include>
<include>org.hdrhistogram:HdrHistogram:2.1.8:jar:test</include>
<include>org.junit.jupiter:junit-jupiter-api:[${junit5.version}]:jar:test</include>
diff --git a/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingCurator.java b/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingCurator.java
index 2044e6869f6..202bd92d86e 100644
--- a/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingCurator.java
+++ b/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingCurator.java
@@ -28,14 +28,6 @@ import static java.util.stream.Collectors.toUnmodifiableMap;
*/
public class ReindexingCurator {
- private static final String STATUS = "status";
- private static final String TYPE = "type";
- private static final String STARTED_MILLIS = "startedMillis";
- private static final String ENDED_MILLIS = "endedMillis";
- private static final String PROGRESS = "progress";
- private static final String STATE = "state";
- private static final String MESSAGE = "message";
-
private final Curator curator;
private final String clusterName;
private final ReindexingSerializer serializer;
@@ -78,6 +70,14 @@ public class ReindexingCurator {
private static class ReindexingSerializer {
+ private static final String STATUS = "status";
+ private static final String TYPE = "type";
+ private static final String STARTED_MILLIS = "startedMillis";
+ private static final String ENDED_MILLIS = "endedMillis";
+ private static final String PROGRESS = "progress";
+ private static final String STATE = "state";
+ private static final String MESSAGE = "message";
+
private final DocumentTypeManager types;
public ReindexingSerializer(DocumentTypeManager types) {
diff --git a/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingMaintainer.java b/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingMaintainer.java
index a336ad02f20..7989338c406 100644
--- a/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingMaintainer.java
+++ b/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/ReindexingMaintainer.java
@@ -56,19 +56,18 @@ public class ReindexingMaintainer extends AbstractComponent {
Metric metric,
DocumentAccess access, ZookeepersConfig zookeepersConfig,
ClusterListConfig clusterListConfig, AllClustersBucketSpacesConfig allClustersBucketSpacesConfig,
- ReindexingConfig reindexingConfig, DocumentmanagerConfig documentmanagerConfig) {
- this(Clock.systemUTC(), metric, access, zookeepersConfig, clusterListConfig, allClustersBucketSpacesConfig, reindexingConfig, documentmanagerConfig);
+ ReindexingConfig reindexingConfig) {
+ this(Clock.systemUTC(), metric, access, zookeepersConfig, clusterListConfig, allClustersBucketSpacesConfig, reindexingConfig);
}
ReindexingMaintainer(Clock clock, Metric metric, DocumentAccess access, ZookeepersConfig zookeepersConfig,
ClusterListConfig clusterListConfig, AllClustersBucketSpacesConfig allClustersBucketSpacesConfig,
- ReindexingConfig reindexingConfig, DocumentmanagerConfig documentmanagerConfig) {
- DocumentTypeManager manager = new DocumentTypeManager(documentmanagerConfig);
- this.reindexer = new Reindexer(parseCluster(reindexingConfig.clusterName(), clusterListConfig, allClustersBucketSpacesConfig, manager),
- parseReady(reindexingConfig, manager),
+ ReindexingConfig reindexingConfig) {
+ this.reindexer = new Reindexer(parseCluster(reindexingConfig.clusterName(), clusterListConfig, allClustersBucketSpacesConfig, access.getDocumentTypeManager()),
+ parseReady(reindexingConfig, access.getDocumentTypeManager()),
new ReindexingCurator(Curator.create(zookeepersConfig.zookeeperserverlist()),
reindexingConfig.clusterName(),
- manager),
+ access.getDocumentTypeManager()),
access,
metric,
clock);
diff --git a/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/http/ReindexingV1ApiHandler.java b/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/http/ReindexingV1ApiHandler.java
new file mode 100644
index 00000000000..fca08f7743c
--- /dev/null
+++ b/clustercontroller-reindexer/src/main/java/ai/vespa/reindexing/http/ReindexingV1ApiHandler.java
@@ -0,0 +1,98 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.reindexing.http;
+
+import ai.vespa.reindexing.Reindexing;
+import ai.vespa.reindexing.ReindexingCurator;
+import com.google.inject.Inject;
+import com.yahoo.cloud.config.ClusterListConfig;
+import com.yahoo.cloud.config.ZookeepersConfig;
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
+import com.yahoo.document.DocumentTypeManager;
+import com.yahoo.document.config.DocumentmanagerConfig;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.Path;
+import com.yahoo.restapi.SlimeJsonResponse;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
+import com.yahoo.vespa.config.content.reindexing.ReindexingConfig;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.zookeeper.VespaZooKeeperServer;
+
+import java.util.concurrent.Executor;
+
+import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
+
+/**
+ * Allows inspecting reindexing status over HTTP.
+ *
+ * @author jonmv
+ */
+public class ReindexingV1ApiHandler extends ThreadedHttpRequestHandler {
+
+ private final ReindexingCurator database;
+
+ @Inject
+ public ReindexingV1ApiHandler(Executor executor, Metric metric,
+ @SuppressWarnings("unused") VespaZooKeeperServer ensureZkHasStarted, ZookeepersConfig zookeepersConfig,
+ ReindexingConfig reindexingConfig, DocumentmanagerConfig documentmanagerConfig) {
+ this(executor,
+ metric,
+ new ReindexingCurator(Curator.create(zookeepersConfig.zookeeperserverlist()),
+ reindexingConfig.clusterName(),
+ new DocumentTypeManager(documentmanagerConfig)));
+ }
+
+ ReindexingV1ApiHandler(Executor executor, Metric metric, ReindexingCurator database) {
+ super(executor, metric);
+ this.database = database;
+ }
+
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ Path path = new Path(request.getUri());
+ if (request.getMethod() != GET)
+ return ErrorResponse.methodNotAllowed("Only GET is supported under /reindexing/v1/");
+
+ if (path.matches("/reindexing/v1")) return getRoot();
+ if (path.matches("/reindexing/v1/status")) return getStatus();
+
+ return ErrorResponse.notFoundError("Nothing at " + request.getUri().getRawPath());
+ }
+
+ HttpResponse getRoot() {
+ Slime slime = new Slime();
+ slime.setObject().setArray("resources").addObject().setString("url", "/reindexing/v1/status");
+ return new SlimeJsonResponse(slime);
+ }
+
+ HttpResponse getStatus() {
+ Slime slime = new Slime();
+ Cursor statusArray = slime.setObject().setArray("status");
+ database.readReindexing().status().forEach((type, status) -> {
+ Cursor statusObject = statusArray.addObject();
+ statusObject.setString("type", type.getName());
+ statusObject.setLong("startedMillis", status.startedAt().toEpochMilli());
+ status.endedAt().ifPresent(endedAt -> statusObject.setLong("endedMillis", endedAt.toEpochMilli()));
+ status.progress().ifPresent(progress -> statusObject.setString("progress", progress.serializeToString()));
+ statusObject.setString("state", toString(status.state()));
+ status.message().ifPresent(message -> statusObject.setString("message", message));
+ });
+ return new SlimeJsonResponse(slime);
+ }
+
+
+ private static String toString(Reindexing.State state) {
+ switch (state) {
+ case READY: return "pending";
+ case RUNNING: return "running";
+ case SUCCESSFUL: return "successful";
+ case FAILED: return "failed";
+ default: throw new IllegalArgumentException("Unexpected state '" + state + "'");
+ }
+ }
+
+}
diff --git a/clustercontroller-reindexer/src/test/java/ai/vespa/reindexing/http/ReindexingV1ApiTest.java b/clustercontroller-reindexer/src/test/java/ai/vespa/reindexing/http/ReindexingV1ApiTest.java
new file mode 100644
index 00000000000..1b6379d21e5
--- /dev/null
+++ b/clustercontroller-reindexer/src/test/java/ai/vespa/reindexing/http/ReindexingV1ApiTest.java
@@ -0,0 +1,80 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.reindexing.http;
+
+import ai.vespa.reindexing.Reindexing;
+import ai.vespa.reindexing.Reindexing.Status;
+import ai.vespa.reindexing.ReindexingCurator;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import com.yahoo.document.DocumentType;
+import com.yahoo.document.DocumentTypeManager;
+import com.yahoo.document.config.DocumentmanagerConfig;
+import com.yahoo.documentapi.ProgressToken;
+import com.yahoo.jdisc.test.MockMetric;
+import com.yahoo.searchdefinition.derived.Deriver;
+import com.yahoo.vespa.curator.mock.MockCurator;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.concurrent.Executors;
+
+import static com.yahoo.jdisc.http.HttpRequest.Method.POST;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author jonmv
+ */
+class ReindexingV1ApiTest {
+
+ DocumentmanagerConfig musicConfig = Deriver.getDocumentManagerConfig("src/test/resources/schemas/music.sd").build();
+ DocumentTypeManager manager = new DocumentTypeManager(musicConfig);
+ DocumentType musicType = manager.getDocumentType("music");
+ ReindexingCurator database = new ReindexingCurator(new MockCurator(), "cluster", manager);
+ ReindexingV1ApiHandler handler = new ReindexingV1ApiHandler(Executors.newSingleThreadExecutor(), new MockMetric(), database);
+
+ @Test
+ void testResponses() {
+ RequestHandlerTestDriver driver = new RequestHandlerTestDriver(handler);
+
+ // GET at root
+ var response = driver.sendRequest("http://localhost/reindexing/v1/");
+ assertEquals("{\"resources\":[{\"url\":\"/reindexing/v1/status\"}]}", response.readAll());
+ assertEquals("application/json; charset=UTF-8", response.getResponse().headers().getFirst("Content-Type"));
+ assertEquals(200, response.getStatus());
+
+ // GET at status with empty database
+ response = driver.sendRequest("http://localhost/reindexing/v1/status");
+ assertEquals("{\"status\":[]}", response.readAll());
+ assertEquals(200, response.getStatus());
+
+ // GET at status with a failed status
+ database.writeReindexing(Reindexing.empty().with(musicType, Status.ready(Instant.EPOCH)
+ .running()
+ .progressed(new ProgressToken())
+ .failed(Instant.ofEpochMilli(123), "ヽ(。_°)ノ")));
+ response = driver.sendRequest("http://localhost/reindexing/v1/status");
+ assertEquals("{\"status\":[{" +
+ "\"type\":\"music\"," +
+ "\"startedMillis\":0," +
+ "\"endedMillis\":123," +
+ "\"progress\":\"" + new ProgressToken().serializeToString() + "\"," +
+ "\"state\":\"failed\"," +
+ "\"message\":\"ヽ(。_°)ノ\"}" +
+ "]}",
+ response.readAll());
+ assertEquals(200, response.getStatus());
+
+ // POST at root
+ response = driver.sendRequest("http://localhost/reindexing/v1/status", POST);
+ assertEquals("{\"error-code\":\"METHOD_NOT_ALLOWED\",\"message\":\"Only GET is supported under /reindexing/v1/\"}",
+ response.readAll());
+ assertEquals(405, response.getStatus());
+
+ // GET at non-existent path
+ response = driver.sendRequest("http://localhost/reindexing/v1/moo");
+ assertEquals("{\"error-code\":\"NOT_FOUND\",\"message\":\"Nothing at /reindexing/v1/moo\"}",
+ response.readAll());
+ assertEquals(404, response.getStatus());
+
+ }
+
+}
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json
index 9df68821454..fd229b35778 100644
--- a/config-model-api/abi-spec.json
+++ b/config-model-api/abi-spec.json
@@ -529,15 +529,14 @@
"public static final enum com.yahoo.config.application.api.ValidationId indexModeChange",
"public static final enum com.yahoo.config.application.api.ValidationId fieldTypeChange",
"public static final enum com.yahoo.config.application.api.ValidationId clusterSizeReduction",
+ "public static final enum com.yahoo.config.application.api.ValidationId tensorTypeChange",
"public static final enum com.yahoo.config.application.api.ValidationId resourcesReduction",
"public static final enum com.yahoo.config.application.api.ValidationId contentTypeRemoval",
"public static final enum com.yahoo.config.application.api.ValidationId contentClusterRemoval",
"public static final enum com.yahoo.config.application.api.ValidationId deploymentRemoval",
- "public static final enum com.yahoo.config.application.api.ValidationId skipAutomaticTenantUpgradeTests",
"public static final enum com.yahoo.config.application.api.ValidationId globalDocumentChange",
"public static final enum com.yahoo.config.application.api.ValidationId configModelVersionMismatch",
"public static final enum com.yahoo.config.application.api.ValidationId skipOldConfigModels",
- "public static final enum com.yahoo.config.application.api.ValidationId forceAutomaticTenantUpgradeTests",
"public static final enum com.yahoo.config.application.api.ValidationId accessControl",
"public static final enum com.yahoo.config.application.api.ValidationId globalEndpointChange"
]
@@ -577,10 +576,7 @@
"attributes": [
"public"
],
- "methods": [
- "public com.yahoo.config.application.api.ValidationId validationId()",
- "public java.lang.String getMessage()"
- ],
+ "methods": [],
"fields": []
},
"com.yahoo.config.application.api.ValidationOverrides": {
@@ -591,6 +587,7 @@
],
"methods": [
"public void <init>(java.util.List)",
+ "public void invalid(java.util.Map, java.time.Instant)",
"public void invalid(com.yahoo.config.application.api.ValidationId, java.lang.String, java.time.Instant)",
"public boolean allows(java.lang.String, java.time.Instant)",
"public boolean allows(com.yahoo.config.application.api.ValidationId, java.time.Instant)",
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationId.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationId.java
index c0bae137b0d..4c76d42a17e 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationId.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationId.java
@@ -14,15 +14,14 @@ public enum ValidationId {
indexModeChange("indexing-mode-change"), // Changing the index mode (streaming, indexed, store-only) of documents
fieldTypeChange("field-type-change"), // Field type changes
clusterSizeReduction("cluster-size-reduction"), // Large reductions in cluster size
+ tensorTypeChange("tensor-type-change"), // Tensor type change
resourcesReduction("resources-reduction"), // Large reductions in node resources
contentTypeRemoval("content-type-removal"), // Removal of a data type (causes deletion of all data)
contentClusterRemoval("content-cluster-removal"), // Removal (or id change) of content clusters
deploymentRemoval("deployment-removal"), // Removal of production zones from deployment.xml
- skipAutomaticTenantUpgradeTests("skip-automatic-tenant-upgrade-test"), // Skip platform supplied staging tests
globalDocumentChange("global-document-change"), // Changing global attribute for document types in content clusters
configModelVersionMismatch("config-model-version-mismatch"), // Internal use
skipOldConfigModels("skip-old-config-models"), // Internal use
- forceAutomaticTenantUpgradeTests("force-automatic-tenant-upgrade-test"), // Internal use
accessControl("access-control"), // Internal use, used in zones where there should be no access-control
globalEndpointChange("global-endpoint-change"); // Changing global endpoints
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java
index a85e109f731..f22cf0d8c47 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java
@@ -14,9 +14,13 @@ import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
+import java.util.stream.Collectors;
/**
* A set of allows which suppresses specific validations in limited time periods.
@@ -47,6 +51,14 @@ public class ValidationOverrides {
this.xmlForm = xmlForm;
}
+ /** Throws a ValidationException unless all given validation is overridden at this time */
+ public void invalid(Map<ValidationId, ? extends Collection<String>> messagesByValidationId, Instant now) {
+ Map<ValidationId, Collection<String>> disallowed = new HashMap<>(messagesByValidationId);
+ disallowed.keySet().removeIf(id -> allows(id, now));
+ if ( ! disallowed.isEmpty())
+ throw new ValidationException(disallowed);
+ }
+
/** Throws a ValidationException unless this validation is overridden at this time */
public void invalid(ValidationId validationId, String message, Instant now) {
if ( ! allows(validationId, now))
@@ -146,26 +158,21 @@ public class ValidationOverrides {
/**
* A deployment validation exception.
* Deployment validations can be {@link ValidationOverrides overridden} based on their id.
- * The purpose of this exception is to model that id as a separate field.
*/
public static class ValidationException extends IllegalArgumentException {
static final long serialVersionUID = 789984668;
- private final ValidationId validationId;
-
private ValidationException(ValidationId validationId, String message) {
- super(message);
- this.validationId = validationId;
+ super(validationId + ": " + message + ". " + toAllowMessage(validationId));
}
- /** Returns the unique id of this validation, which can be used to {@link ValidationOverrides override} it */
- public ValidationId validationId() { return validationId; }
-
- /** Returns "validationId: message" */
- @Override
- public String getMessage() {
- return validationId + ": " + super.getMessage() + ". " + toAllowMessage(validationId);
+ private ValidationException(Map<ValidationId, Collection<String>> messagesById) {
+ super(messagesById.entrySet().stream()
+ .map(messages -> messages.getKey() + ":\n\t" +
+ String.join("\n\t", messages.getValue()) + "\n" +
+ toAllowMessage(messages.getKey()))
+ .collect(Collectors.joining("\n")));
}
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java
index 1248560c931..87a150a6c3c 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java
@@ -1,9 +1,11 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.api;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.provision.ClusterSpec;
import java.util.List;
+import java.util.Optional;
/**
* Contains the action to be performed on the given services to handle a config change
@@ -37,12 +39,11 @@ public interface ConfigChangeAction {
/** Returns the list of services where the action must be performed */
List<ServiceInfo> getServices();
- /** Returns whether this change should be allowed */
- boolean allowed();
+ /** When this is non-empty, validation may fail unless this validation id is allowed by validation overrides. */
+ default Optional<ValidationId> validationId() { return Optional.empty(); }
/** The id of the cluster that needs this action applied */
- // TODO: Remove this default implementation after October 2020
- default ClusterSpec.Id clusterId() { return null; }
+ ClusterSpec.Id clusterId();
/** Returns whether this change should be ignored for internal redeploy */
default boolean ignoreForInternalRedeploy() {
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java
index c5a7bd030e2..13109088dcd 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java
@@ -1,6 +1,8 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.api;
+import com.yahoo.config.application.api.ValidationId;
+
/**
* Represents an action to re-feed a document type in order to handle a config change.
*
@@ -12,7 +14,7 @@ public interface ConfigChangeRefeedAction extends ConfigChangeAction {
default Type getType() { return Type.REFEED; }
/** Returns the name identifying this kind of change, used to identify names which should be allowed */
- String name();
+ default String name() { return validationId().orElseThrow().value(); }
/** Returns the name of the document type that one must re-feed to handle this config change */
String getDocumentType();
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeReindexAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeReindexAction.java
index 085638e31ff..bb714a55f94 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeReindexAction.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeReindexAction.java
@@ -1,6 +1,8 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.api;
+import com.yahoo.config.application.api.ValidationId;
+
/**
* Represents an action to re-index a document type in order to handle a config change.
*
@@ -11,7 +13,7 @@ public interface ConfigChangeReindexAction extends ConfigChangeAction {
@Override default Type getType() { return Type.REINDEX; }
/** @return name identifying this kind of change, used to identify names which should be allowed */
- String name();
+ default String name() { return validationId().orElseThrow().value(); }
/** @return name of the document type that must be re-indexed */
String getDocumentType();
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java
index f178180b6e0..c13399a42f5 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java
@@ -11,8 +11,4 @@ public interface ConfigChangeRestartAction extends ConfigChangeAction {
@Override
default Type getType() { return Type.RESTART; }
- /** Restarts are handled automatically so they are allowed */
- @Override
- default boolean allowed() { return true; }
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
index acd4d705889..2ffc24239f9 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
@@ -97,7 +97,7 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
private static final long serialVersionUID = 1L;
- public static final Logger log = Logger.getLogger(VespaModel.class.getPackage().toString());
+ public static final Logger log = Logger.getLogger(VespaModel.class.getName());
private final Version version;
private final ConfigModelRepo configModelRepo = new ConfigModelRepo();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
index ec8607daaca..9e15db348a2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
@@ -3,13 +3,16 @@ package com.yahoo.vespa.model.admin.clustercontroller;
import com.yahoo.cloud.config.ZookeeperServerConfig;
import com.yahoo.component.ComponentSpecification;
+import com.yahoo.config.model.api.Reindexing;
import com.yahoo.config.model.api.container.ContainerServiceType;
import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.core.documentapi.DocumentAccessProvider;
import com.yahoo.container.di.config.PlatformBundlesConfig;
+import com.yahoo.documentmodel.NewDocumentType;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.config.content.FleetcontrollerConfig;
+import com.yahoo.vespa.config.content.reindexing.ReindexingConfig;
import com.yahoo.vespa.model.application.validation.RestartConfigs;
import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.component.AccessLogComponent;
@@ -28,12 +31,15 @@ import java.util.TreeSet;
@RestartConfigs({FleetcontrollerConfig.class, ZookeeperServerConfig.class})
public class ClusterControllerContainer extends Container implements
PlatformBundlesConfig.Producer,
- ZookeeperServerConfig.Producer
+ ZookeeperServerConfig.Producer,
+ ReindexingConfig.Producer
{
private static final ComponentSpecification CLUSTERCONTROLLER_BUNDLE = new ComponentSpecification("clustercontroller-apps");
private static final ComponentSpecification ZOOKEEPER_SERVER_BUNDLE = new ComponentSpecification("zookeeper-server");
+ private static final ComponentSpecification REINDEXING_CONTROLLER_BUNDLE = new ComponentSpecification("clustercontroller-reindexer");
private final Set<String> bundles = new TreeSet<>();
+ private final ReindexingContext reindexingContext;
public ClusterControllerContainer(
AbstractConfigProducer<?> parent,
@@ -42,12 +48,16 @@ public class ClusterControllerContainer extends Container implements
boolean isHosted,
ReindexingContext reindexingContext) {
super(parent, "" + index, index, isHosted);
+ this.reindexingContext = reindexingContext;
+
addHandler("clustercontroller-status",
"com.yahoo.vespa.clustercontroller.apps.clustercontroller.StatusHandler",
- "/clustercontroller-status/*");
+ "/clustercontroller-status/*",
+ CLUSTERCONTROLLER_BUNDLE);
addHandler("clustercontroller-state-restapi-v2",
"com.yahoo.vespa.clustercontroller.apps.clustercontroller.StateRestApiV2Handler",
- "/cluster/v2/*");
+ "/cluster/v2/*",
+ CLUSTERCONTROLLER_BUNDLE);
if (runStandaloneZooKeeper) {
addComponent("clustercontroller-zkrunner",
"com.yahoo.vespa.zookeeper.VespaZooKeeperServerImpl",
@@ -73,7 +83,7 @@ public class ClusterControllerContainer extends Container implements
addFileBundle("clustercontroller-core");
addFileBundle("clustercontroller-utils");
addFileBundle("zookeeper-server");
- configureReindexing(reindexingContext);
+ configureReindexing();
}
@Override
@@ -110,15 +120,21 @@ public class ClusterControllerContainer extends Container implements
addComponent(new Component<>(createComponentModel(id, className, bundle)));
}
- private void addHandler(String id, String className, String path) {
- addHandler(new Handler(createComponentModel(id, className, CLUSTERCONTROLLER_BUNDLE)), path);
+ private void addHandler(String id, String className, String path, ComponentSpecification bundle) {
+ addHandler(new Handler(createComponentModel(id, className, bundle)), path);
}
- private void configureReindexing(ReindexingContext context) {
- if (context != null) {
- addFileBundle(ReindexingController.REINDEXING_CONTROLLER_BUNDLE);
- addComponent(new ReindexingController(context));
+ private void configureReindexing() {
+ if (reindexingContext != null) {
+ addFileBundle(REINDEXING_CONTROLLER_BUNDLE.getName());
addComponent(new SimpleComponent(DocumentAccessProvider.class.getName()));
+ addComponent("reindexing-maintainer",
+ "ai.vespa.reindexing.ReindexingMaintainer",
+ REINDEXING_CONTROLLER_BUNDLE);
+ addHandler("reindexing-status",
+ "ai.vespa.reindexing.http.ReindexingV1ApiHandler",
+ "/reindexing/v1/*",
+ REINDEXING_CONTROLLER_BUNDLE);
}
}
@@ -133,4 +149,20 @@ public class ClusterControllerContainer extends Container implements
builder.myid(index());
}
+ @Override
+ public void getConfig(ReindexingConfig.Builder builder) {
+ if (reindexingContext == null)
+ return;
+
+ builder.clusterName(reindexingContext.contentClusterName());
+ builder.enabled(reindexingContext.reindexing().enabled());
+ for (NewDocumentType type : reindexingContext.documentTypes()) {
+ String typeName = type.getFullName().getName();
+ reindexingContext.reindexing().status(reindexingContext.contentClusterName(), typeName)
+ .ifPresent(status -> builder.status(typeName,
+ new ReindexingConfig.Status.Builder()
+ .readyAtMillis(status.ready().toEpochMilli())));
+ }
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ReindexingController.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ReindexingController.java
deleted file mode 100644
index 24909ddbc8d..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ReindexingController.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.admin.clustercontroller;
-
-import com.yahoo.config.model.api.Reindexing;
-import com.yahoo.container.bundle.BundleInstantiationSpecification;
-import com.yahoo.documentmodel.NewDocumentType;
-import com.yahoo.osgi.provider.model.ComponentModel;
-import com.yahoo.vespa.config.content.reindexing.ReindexingConfig;
-import com.yahoo.vespa.model.container.component.SimpleComponent;
-
-import java.util.Collection;
-
-/**
- * @author bjorncs
- */
-class ReindexingController extends SimpleComponent implements ReindexingConfig.Producer {
-
- static final String REINDEXING_CONTROLLER_BUNDLE = "clustercontroller-reindexer";
-
- private final Reindexing reindexing;
- private final String contentClusterName;
- private final Collection<NewDocumentType> documentTypes;
-
- ReindexingController(ReindexingContext context) {
- super(new ComponentModel(
- BundleInstantiationSpecification.getFromStrings(
- "reindexing-maintainer",
- "ai.vespa.reindexing.ReindexingMaintainer",
- REINDEXING_CONTROLLER_BUNDLE)));
- this.reindexing = context.reindexing();
- this.contentClusterName = context.contentClusterName();
- this.documentTypes = context.documentTypes();
- }
-
- @Override
- public void getConfig(ReindexingConfig.Builder builder) {
- builder.clusterName(contentClusterName);
- builder.enabled(reindexing.enabled());
- for (NewDocumentType type : documentTypes) {
- String typeName = type.getFullName().getName();
- reindexing.status(contentClusterName, typeName).ifPresent(status ->
- builder.status(
- typeName,
- new ReindexingConfig.Status.Builder()
- .readyAtMillis(status.ready().toEpochMilli())));
- }
- }
-}
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 21f323fc0f3..4317f947e12 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
@@ -239,6 +239,8 @@ public class VespaMetricSet {
// DO NOT RELY ON THIS METRIC YET.
metrics.add(new Metric("cluster-controller.node-event.count"));
+ metrics.add(new Metric("cluster-controller.reindexing.progress.last"));
+
return metrics;
}
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 d300e31c3dc..e028eaea3c1 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.model.application.validation;
import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.Model;
@@ -26,13 +27,20 @@ import com.yahoo.vespa.model.application.validation.first.AccessControlOnFirstDe
import java.time.Instant;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
/**
* Executor of validators. This defines the right order of validator execution.
@@ -99,9 +107,17 @@ public class Validation {
new ContainerRestartValidator(),
new NodeResourceChangeValidator()
};
- return Arrays.stream(validators)
- .flatMap(v -> v.validate(currentModel, nextModel, overrides, now).stream())
- .collect(toList());
+ List<ConfigChangeAction> actions = Arrays.stream(validators)
+ .flatMap(v -> v.validate(currentModel, nextModel, overrides, now).stream())
+ .collect(toList());
+
+ Map<ValidationId, Collection<String>> disallowableActions = actions.stream()
+ .filter(action -> action.validationId().isPresent())
+ .collect(groupingBy(action -> action.validationId().orElseThrow(),
+ mapping(ConfigChangeAction::getMessage,
+ toCollection(LinkedHashSet::new))));
+ overrides.invalid(disallowableActions, now);
+ return actions;
}
private static void validateFirstTimeDeployment(VespaModel model, DeployState deployState) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidator.java
index b321d5f3fd7..58cea8c23e5 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidator.java
@@ -13,7 +13,10 @@ import com.yahoo.vespa.model.search.DocumentDatabase;
import com.yahoo.vespa.model.search.IndexedSearchCluster;
import java.time.Instant;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidator.java
index e3f16baf95a..385a678d452 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidator.java
@@ -46,22 +46,22 @@ public class IndexingModeChangeValidator implements ChangeValidator {
ContentSearchCluster currentSearchCluster = currentCluster.getSearch();
ContentSearchCluster nextSearchCluster = nextCluster.getSearch();
findDocumentTypesWithActionableIndexingModeChange(
- actions, overrides, nextCluster, now,
+ actions, nextCluster,
toDocumentTypeNames(currentSearchCluster.getDocumentTypesWithStreamingCluster()),
toDocumentTypeNames(nextSearchCluster.getDocumentTypesWithIndexedCluster()),
"streaming", "indexed");
findDocumentTypesWithActionableIndexingModeChange(
- actions, overrides, nextCluster, now,
+ actions, nextCluster,
toDocumentTypeNames(currentSearchCluster.getDocumentTypesWithIndexedCluster()),
toDocumentTypeNames(nextSearchCluster.getDocumentTypesWithStreamingCluster()),
"indexed", "streaming");
findDocumentTypesWithActionableIndexingModeChange(
- actions, overrides, nextCluster, now,
+ actions, nextCluster,
toDocumentTypeNames(currentSearchCluster.getDocumentTypesWithStoreOnly()),
toDocumentTypeNames(nextSearchCluster.getDocumentTypesWithIndexedCluster()),
"store-only", "indexed");
findDocumentTypesWithActionableIndexingModeChange(
- actions, overrides, nextCluster, now,
+ actions, nextCluster,
toDocumentTypeNames(currentSearchCluster.getDocumentTypesWithIndexedCluster()),
toDocumentTypeNames(nextSearchCluster.getDocumentTypesWithStoreOnly()),
"indexed", "store-only");
@@ -69,7 +69,7 @@ public class IndexingModeChangeValidator implements ChangeValidator {
}
private static void findDocumentTypesWithActionableIndexingModeChange(
- List<ConfigChangeAction> actions, ValidationOverrides overrides, ContentCluster nextCluster, Instant now,
+ List<ConfigChangeAction> actions, ContentCluster nextCluster,
Set<String> currentTypes, Set<String> nextTypes, String currentIndexMode, String nextIndexingMode) {
for (String type : nextTypes) {
if (currentTypes.contains(type)) {
@@ -78,14 +78,13 @@ public class IndexingModeChangeValidator implements ChangeValidator {
.collect(Collectors.toList());
actions.add(VespaReindexAction.of(
nextCluster.id(),
- ValidationId.indexModeChange.value(),
- overrides,
+ ValidationId.indexModeChange,
String.format(
"Document type '%s' in cluster '%s' changed indexing mode from '%s' to '%s'",
type, nextCluster.getName(), currentIndexMode, nextIndexingMode),
services,
- type,
- now));
+ type
+ ));
}
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidator.java
index d85d9bd2db5..2f13caa4e09 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidator.java
@@ -78,7 +78,7 @@ public class StreamingSearchClusterChangeValidator implements ChangeValidator {
NewDocumentType nextDocType,
ValidationOverrides overrides,
Instant now) {
- return new DocumentTypeChangeValidator(id, currentDocType, nextDocType).validate(overrides, now);
+ return new DocumentTypeChangeValidator(id, currentDocType, nextDocType).validate();
}
private static NewDocumentType getDocumentType(ContentCluster cluster, StreamingSearchCluster streamingCluster) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaRefeedAction.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaRefeedAction.java
index 19c63431c03..6a335447a31 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaRefeedAction.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaRefeedAction.java
@@ -1,6 +1,7 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.application.validation.change;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ConfigChangeRefeedAction;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.application.api.ValidationOverrides;
@@ -8,6 +9,7 @@ import com.yahoo.config.provision.ClusterSpec;
import java.time.Instant;
import java.util.List;
+import java.util.Optional;
/**
* Represents an action to re-feed a document type in order to handle a config change.
@@ -22,45 +24,39 @@ public class VespaRefeedAction extends VespaConfigChangeAction implements Config
* the validation ids belong to the Vespa model while these names are exposed to the config server,
* which is model version independent.
*/
- private final String name;
-
+ private final ValidationId validationId;
private final String documentType;
- private final boolean allowed;
- private VespaRefeedAction(ClusterSpec.Id id, String name, String message, List<ServiceInfo> services, String documentType, boolean allowed) {
+ private VespaRefeedAction(ClusterSpec.Id id, ValidationId validationId, String message, List<ServiceInfo> services, String documentType) {
super(id, message, services);
- this.name = name;
+ this.validationId = validationId;
this.documentType = documentType;
- this.allowed = allowed;
}
/** Creates a refeed action with some missing information */
// TODO: We should require document type or model its absence properly
- public static VespaRefeedAction of(ClusterSpec.Id id, String name, ValidationOverrides overrides, String message, Instant now) {
- return new VespaRefeedAction(id, name, message, List.of(), "", overrides.allows(name, now));
+ public static VespaRefeedAction of(ClusterSpec.Id id, ValidationId validationId, String message) {
+ return new VespaRefeedAction(id, validationId, message, List.of(), "");
}
/** Creates a refeed action */
- public static VespaRefeedAction of(ClusterSpec.Id id, String name, ValidationOverrides overrides, String message,
- List<ServiceInfo> services, String documentType, Instant now) {
- return new VespaRefeedAction(id, name, message, services, documentType, overrides.allows(name, now));
+ public static VespaRefeedAction of(ClusterSpec.Id id, ValidationId validationId, String message,
+ List<ServiceInfo> services, String documentType) {
+ return new VespaRefeedAction(id, validationId, message, services, documentType);
}
@Override
public VespaConfigChangeAction modifyAction(String newMessage, List<ServiceInfo> newServices, String documentType) {
- return new VespaRefeedAction(clusterId(), name, newMessage, newServices, documentType, allowed);
+ return new VespaRefeedAction(clusterId(), validationId, newMessage, newServices, documentType);
}
@Override
- public String name() { return name; }
+ public Optional<ValidationId> validationId() { return Optional.of(validationId); }
@Override
public String getDocumentType() { return documentType; }
@Override
- public boolean allowed() { return allowed; }
-
- @Override
public boolean ignoreForInternalRedeploy() {
return false;
}
@@ -76,14 +72,13 @@ public class VespaRefeedAction extends VespaConfigChangeAction implements Config
if ( ! (o instanceof VespaRefeedAction)) return false;
VespaRefeedAction other = (VespaRefeedAction)o;
if ( ! this.documentType.equals(other.documentType)) return false;
- if ( ! this.name.equals(other.name)) return false;
- if ( ! this.allowed == other.allowed) return false;
+ if ( ! this.validationId.equals(other.validationId)) return false;
return true;
}
@Override
public int hashCode() {
- return 31 * super.hashCode() + 11 * name.hashCode() + documentType.hashCode();
+ return 31 * super.hashCode() + 11 * validationId.hashCode() + documentType.hashCode();
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaReindexAction.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaReindexAction.java
index f10802afc31..8b4060e7d19 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaReindexAction.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/VespaReindexAction.java
@@ -1,14 +1,14 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.application.validation.change;
-import com.yahoo.config.application.api.ValidationOverrides;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ConfigChangeReindexAction;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ClusterSpec;
-import java.time.Instant;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
/**
* Represents an action to re-index a document type in order to handle a config change.
@@ -22,36 +22,32 @@ public class VespaReindexAction extends VespaConfigChangeAction implements Confi
* the validation ids belong to the Vespa model while these names are exposed to the config server,
* which is model version independent.
*/
- private final String name;
+ private final ValidationId validationId;
private final String documentType;
- private final boolean allowed;
- private VespaReindexAction(ClusterSpec.Id id, String name, String message, List<ServiceInfo> services, String documentType, boolean allowed) {
+ private VespaReindexAction(ClusterSpec.Id id, ValidationId validationId, String message, List<ServiceInfo> services, String documentType) {
super(id, message, services);
- this.name = name;
+ this.validationId = validationId;
this.documentType = documentType;
- this.allowed = allowed;
}
- public static VespaReindexAction of(
- ClusterSpec.Id id, String name, ValidationOverrides overrides, String message, Instant now) {
- return new VespaReindexAction(id, name, message, List.of(), /*documentType*/null, overrides.allows(name, now));
+ public static VespaReindexAction of(ClusterSpec.Id id, ValidationId validationId, String message) {
+ return new VespaReindexAction(id, validationId, message, List.of(), /*documentType*/null);
}
public static VespaReindexAction of(
- ClusterSpec.Id id, String name, ValidationOverrides overrides, String message,
- List<ServiceInfo> services, String documentType, Instant now) {
- return new VespaReindexAction(id, name, message, services, documentType, overrides.allows(name, now));
+ ClusterSpec.Id id, ValidationId validationId, String message,
+ List<ServiceInfo> services, String documentType) {
+ return new VespaReindexAction(id, validationId, message, services, documentType);
}
@Override
public VespaConfigChangeAction modifyAction(String newMessage, List<ServiceInfo> newServices, String documentType) {
- return new VespaReindexAction(clusterId(), name, newMessage, newServices, documentType, allowed);
+ return new VespaReindexAction(clusterId(), validationId, newMessage, newServices, documentType);
}
- @Override public String name() { return name; }
+ @Override public Optional<ValidationId> validationId() { return Optional.of(validationId); }
@Override public String getDocumentType() { return documentType; }
- @Override public boolean allowed() { return allowed; }
@Override public boolean ignoreForInternalRedeploy() { return false; }
@Override public String toString() { return super.toString() + ", documentType='" + documentType + "'"; }
@@ -61,10 +57,10 @@ public class VespaReindexAction extends VespaConfigChangeAction implements Confi
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
VespaReindexAction that = (VespaReindexAction) o;
- return allowed == that.allowed &&
- Objects.equals(name, that.name) &&
- Objects.equals(documentType, that.documentType);
+ return Objects.equals(validationId, that.validationId) &&
+ Objects.equals(documentType, that.documentType);
}
- @Override public int hashCode() { return Objects.hash(super.hashCode(), name, documentType, allowed); }
+ @Override public int hashCode() { return Objects.hash(super.hashCode(), validationId, documentType); }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java
index 3dcfbe3629d..ce435a4c157 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidator.java
@@ -72,7 +72,7 @@ public class DocumentDatabaseChangeValidator {
private List<VespaConfigChangeAction> validateDocumentTypeChanges(ValidationOverrides overrides, Instant now) {
return new DocumentTypeChangeValidator(id, currentDocType, nextDocType)
- .validate(overrides, now);
+ .validate();
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidator.java
index b66145a10c5..5b7fdfad0f7 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidator.java
@@ -1,15 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.application.validation.change.search;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.document.StructDataType;
import com.yahoo.documentmodel.NewDocumentType;
import com.yahoo.document.Field;
-import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.vespa.model.application.validation.change.VespaConfigChangeAction;
import com.yahoo.vespa.model.application.validation.change.VespaRefeedAction;
-import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
@@ -136,17 +135,16 @@ public class DocumentTypeChangeValidator {
this.nextDocType = nextDocType;
}
- public List<VespaConfigChangeAction> validate(ValidationOverrides overrides, Instant now) {
+ public List<VespaConfigChangeAction> validate() {
return currentDocType.getAllFields().stream().
map(field -> createFieldChange(field, nextDocType)).
filter(fieldChange -> fieldChange.valid() && fieldChange.changedType()).
map(fieldChange -> VespaRefeedAction.of(id,
- "field-type-change",
- overrides,
+ ValidationId.fieldTypeChange,
new ChangeMessageBuilder(fieldChange.fieldName()).
addChange("data type", fieldChange.currentTypeName(),
- fieldChange.nextTypeName()).build(),
- now)).
+ fieldChange.nextTypeName()).build()
+ )).
collect(Collectors.toList());
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidator.java
index 8f9b1a3ed77..e3f3abf0747 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidator.java
@@ -41,21 +41,20 @@ public class IndexingScriptChangeValidator {
String fieldName = nextField.getName();
ImmutableSDField currentField = currentSearch.getConcreteField(fieldName);
if (currentField != null) {
- validateScripts(currentField, nextField, overrides, now).ifPresent(r -> result.add(r));
+ validateScripts(currentField, nextField).ifPresent(r -> result.add(r));
}
}
return result;
}
- private Optional<VespaConfigChangeAction> validateScripts(ImmutableSDField currentField, ImmutableSDField nextField,
- ValidationOverrides overrides, Instant now) {
+ private Optional<VespaConfigChangeAction> validateScripts(ImmutableSDField currentField, ImmutableSDField nextField) {
ScriptExpression currentScript = currentField.getIndexingScript();
ScriptExpression nextScript = nextField.getIndexingScript();
if ( ! equalScripts(currentScript, nextScript)) {
ChangeMessageBuilder messageBuilder = new ChangeMessageBuilder(nextField.getName());
new IndexingScriptChangeMessageBuilder(currentSearch, currentField, nextSearch, nextField).populate(messageBuilder);
messageBuilder.addChange("indexing script", currentScript.toString(), nextScript.toString());
- return Optional.of(VespaReindexAction.of(id, ValidationId.indexingChange.value(), overrides, messageBuilder.build(), now));
+ return Optional.of(VespaReindexAction.of(id, ValidationId.indexingChange, messageBuilder.build()));
}
return Optional.empty();
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
index 9bd12350f26..00ff19ae68a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
@@ -3,6 +3,10 @@ package com.yahoo.vespa.model.container.http;
import com.yahoo.component.ComponentId;
import com.yahoo.component.ComponentSpecification;
+import com.yahoo.component.chain.dependencies.Dependencies;
+import com.yahoo.component.chain.model.ChainedComponentModel;
+import com.yahoo.container.bundle.BundleInstantiationSpecification;
+import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.BindingPattern;
@@ -27,11 +31,13 @@ import java.util.Set;
*/
public class AccessControl {
- public enum ClientAuthentication { want, need }
+
+ public enum ClientAuthentication { want, need;}
public static final ComponentId ACCESS_CONTROL_CHAIN_ID = ComponentId.fromString("access-control-chain");
- public static final ComponentId ACCESS_CONTROL_EXCLUDED_CHAIN_ID = ComponentId.fromString("access-control-excluded-chain");
+ public static final ComponentId ACCESS_CONTROL_EXCLUDED_CHAIN_ID = ComponentId.fromString("access-control-excluded-chain");
+ public static final ComponentId DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID = ComponentId.fromString("default-connector-hosted-request-chain");
private static final int HOSTED_CONTAINER_PORT = 4443;
// Handlers that are excluded from access control
@@ -44,7 +50,6 @@ public class AccessControl {
ApplicationContainerCluster.METRICS_V2_HANDLER_CLASS,
ApplicationContainerCluster.PROMETHEUS_V1_HANDLER_CLASS
);
-
public static class Builder {
private final String domain;
private boolean readEnabled = false;
@@ -52,7 +57,6 @@ public class AccessControl {
private ClientAuthentication clientAuthentication = ClientAuthentication.need;
private final Set<BindingPattern> excludeBindings = new LinkedHashSet<>();
private Collection<Handler<?>> handlers = Collections.emptyList();
-
public Builder(String domain) {
this.domain = domain;
}
@@ -112,6 +116,7 @@ public class AccessControl {
http.setAccessControl(this);
addAccessControlFilterChain(http);
addAccessControlExcludedChain(http);
+ addDefaultHostedRequestChain(http);
removeDuplicateBindingsFromAccessControlChain(http);
}
@@ -119,6 +124,18 @@ public class AccessControl {
connectorFactory.setDefaultRequestFilterChain(ACCESS_CONTROL_CHAIN_ID);
}
+ public void configureDefaultHostedConnector(Http http) {
+ // Set default filter chain on local port
+ http.getHttpServer()
+ .get()
+ .getConnectorFactories()
+ .stream()
+ .filter(cf -> cf.getListenPort() == Defaults.getDefaults().vespaWebServicePort())
+ .findFirst()
+ .orElseThrow(() -> new RuntimeException("Could not find default connector"))
+ .setDefaultRequestFilterChain(DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID);
+ }
+
/** returns the excluded bindings as specified in 'access-control' in services.xml **/
public Set<BindingPattern> excludedBindings() { return excludedBindings; }
@@ -148,6 +165,12 @@ public class AccessControl {
}
}
+ // Add a filter chain used by default hosted connector
+ private void addDefaultHostedRequestChain(Http http) {
+ Chain<Filter> chain = createChain(DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID);
+ http.getFilterChains().add(chain);
+ }
+
// Remove bindings from access control chain that have binding pattern as a different filter chain
private void removeDuplicateBindingsFromAccessControlChain(Http http) {
removeDuplicateBindingsFromChain(http, ACCESS_CONTROL_EXCLUDED_CHAIN_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 0e23527c97c..7eea5d8496f 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
@@ -85,6 +85,7 @@ import org.w3c.dom.Node;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -318,10 +319,16 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
if (isHostedTenantApplication(context)) {
addHostedImplicitHttpIfNotPresent(cluster);
addHostedImplicitAccessControlIfNotPresent(deployState, cluster);
+ addDefaultConnectorHostedFilterBinding(cluster);
addAdditionalHostedConnector(deployState, cluster, context);
}
}
+ private void addDefaultConnectorHostedFilterBinding(ApplicationContainerCluster cluster) {
+ cluster.getHttp().getAccessControl()
+ .ifPresent(accessControl -> accessControl.configureDefaultHostedConnector(cluster.getHttp())); ;
+ }
+
private void addAdditionalHostedConnector(DeployState deployState, ApplicationContainerCluster cluster, ConfigModelContext context) {
JettyHttpServer server = cluster.getHttp().getHttpServer().get();
String serverName = server.getComponentId().getName();
@@ -361,10 +368,15 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
if(cluster.getHttp() == null) {
cluster.setHttp(new Http(new FilterChains(cluster)));
}
- if(cluster.getHttp().getHttpServer().isEmpty()) {
- JettyHttpServer defaultHttpServer = new JettyHttpServer(new ComponentId("DefaultHttpServer"), cluster, cluster.isHostedVespa());
- cluster.getHttp().setHttpServer(defaultHttpServer);
- defaultHttpServer.addConnector(new ConnectorFactory.Builder("SearchServer", Defaults.getDefaults().vespaWebServicePort()).build());
+ JettyHttpServer httpServer = cluster.getHttp().getHttpServer().orElse(null);
+ if (httpServer == null) {
+ httpServer = new JettyHttpServer(new ComponentId("DefaultHttpServer"), cluster, cluster.isHostedVespa());
+ cluster.getHttp().setHttpServer(httpServer);
+ }
+ int defaultPort = Defaults.getDefaults().vespaWebServicePort();
+ boolean defaultConnectorPresent = httpServer.getConnectorFactories().stream().anyMatch(connector -> connector.getListenPort() == defaultPort);
+ if (!defaultConnectorPresent) {
+ httpServer.addConnector(new ConnectorFactory.Builder("SearchServer", defaultPort).build());
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java
index f3f9022f6f0..2157839ef5c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ConfigChangeTestUtils.java
@@ -1,9 +1,9 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.application.validation.change;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.ServiceInfo;
-import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ClusterSpec;
import java.time.Instant;
@@ -24,26 +24,22 @@ public class ConfigChangeTestUtils {
return new VespaRestartAction(id, message, services);
}
- public static VespaConfigChangeAction newRefeedAction(ClusterSpec.Id id, String name, String message) {
- return VespaRefeedAction.of(id, name, ValidationOverrides.empty, message, Instant.now());
+ public static VespaConfigChangeAction newRefeedAction(ClusterSpec.Id id, ValidationId validationId, String message) {
+ return VespaRefeedAction.of(id, validationId, message);
}
- public static VespaConfigChangeAction newRefeedAction(ClusterSpec.Id id, String name, ValidationOverrides overrides, String message, Instant now) {
- return VespaRefeedAction.of(id, name, overrides, message, now);
+ public static VespaConfigChangeAction newRefeedAction(ClusterSpec.Id id, ValidationId validationId, String message,
+ List<ServiceInfo> services, String documentType) {
+ return VespaRefeedAction.of(id, validationId, message, services, documentType);
}
- public static VespaConfigChangeAction newRefeedAction(ClusterSpec.Id id, String name, ValidationOverrides overrides, String message,
- List<ServiceInfo> services, String documentType, Instant now) {
- return VespaRefeedAction.of(id, name, overrides, message, services, documentType, now);
+ public static VespaConfigChangeAction newReindexAction(ClusterSpec.Id id, ValidationId validationId, String message) {
+ return VespaReindexAction.of(id, validationId, message);
}
- public static VespaConfigChangeAction newReindexAction(ClusterSpec.Id id, String name, ValidationOverrides overrides, String message, Instant now) {
- return VespaReindexAction.of(id, name, overrides, message, now);
- }
-
- public static VespaConfigChangeAction newReindexAction(ClusterSpec.Id id, String name, ValidationOverrides overrides, String message,
- List<ServiceInfo> services, String documentType, Instant now) {
- return VespaReindexAction.of(id, name, overrides, message, services, documentType, now);
+ public static VespaConfigChangeAction newReindexAction(ClusterSpec.Id id, ValidationId validationId, String message,
+ List<ServiceInfo> services, String documentType) {
+ return VespaReindexAction.of(id, validationId, message, services, documentType);
}
public static List<ConfigChangeAction> normalizeServicesInActions(List<ConfigChangeAction> result) {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidatorTest.java
index 8d365f24c7f..2b211c561d9 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexedSearchClusterChangeValidatorTest.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.vespa.model.application.validation.change;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ClusterSpec;
@@ -146,9 +147,8 @@ public class IndexedSearchClusterChangeValidatorTest {
public void requireThatChangingFieldTypeIsDiscovered() {
Fixture f = Fixture.newOneDocFixture(STRING_FIELD, INT_FIELD);
f.assertValidation(List.of(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Document type 'd1': " + FIELD_TYPE_CHANGE_MSG, FOO_SERVICE, "d1", Instant.now())));
+ ValidationId.fieldTypeChange,
+ "Document type 'd1': " + FIELD_TYPE_CHANGE_MSG, FOO_SERVICE, "d1")));
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java
index 8e2cecf89b4..ba9dfcdc388 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/IndexingModeChangeValidatorTest.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.vespa.model.application.validation.change;
+import com.yahoo.config.application.api.ValidationOverrides.ValidationException;
import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.ConfigChangeReindexAction;
import com.yahoo.config.provision.Environment;
@@ -11,8 +12,10 @@ import org.junit.Test;
import java.util.List;
import java.util.stream.Collectors;
+import static java.util.stream.Collectors.joining;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* @author bratseth
@@ -21,6 +24,25 @@ import static org.junit.Assert.assertTrue;
public class IndexingModeChangeValidatorTest {
@Test
+ public void testChangingIndexModeFromIndexedToStreamingWhenDisallowed() {
+ ValidationTester tester = new ValidationTester();
+
+ VespaModel oldModel =
+ tester.deploy(null, getServices("index"), Environment.prod, "<validation-overrides />").getFirst();
+ try {
+ List<ConfigChangeAction> changeActions =
+ tester.deploy(oldModel, getServices("streaming"), Environment.prod, "<calidation-overrides />").getSecond();
+ fail("Should throw on disallowed config change action");
+ }
+ catch (ValidationException e) {
+ assertEquals("indexing-mode-change:\n" +
+ "\tDocument type 'music' in cluster 'default' changed indexing mode from 'indexed' to 'streaming'\n" +
+ "To allow this add <allow until='yyyy-mm-dd'>indexing-mode-change</allow> to validation-overrides.xml, see https://docs.vespa.ai/documentation/reference/validation-overrides.html",
+ e.getMessage());
+ }
+ }
+
+ @Test
public void testChangingIndexModeFromIndexedToStreaming() {
ValidationTester tester = new ValidationTester();
@@ -29,9 +51,9 @@ public class IndexingModeChangeValidatorTest {
List<ConfigChangeAction> changeActions =
tester.deploy(oldModel, getServices("streaming"), Environment.prod, validationOverrides).getSecond();
- assertReindexingChange(true, // allowed=true due to validation override
- "Document type 'music' in cluster 'default' changed indexing mode from 'indexed' to 'streaming'",
- changeActions);
+ assertReindexingChange( // allowed=true due to validation override
+ "Document type 'music' in cluster 'default' changed indexing mode from 'indexed' to 'streaming'",
+ changeActions);
}
@Test
@@ -43,23 +65,22 @@ public class IndexingModeChangeValidatorTest {
List<ConfigChangeAction> changeActions =
tester.deploy(oldModel, getServices("store-only"), Environment.prod, validationOverrides).getSecond();
- assertReindexingChange(true, // allowed=true due to validation override
- "Document type 'music' in cluster 'default' changed indexing mode from 'indexed' to 'store-only'",
- changeActions);
+ assertReindexingChange( // allowed=true due to validation override
+ "Document type 'music' in cluster 'default' changed indexing mode from 'indexed' to 'store-only'",
+ changeActions);
}
- private void assertReindexingChange(boolean allowed, String message, List<ConfigChangeAction> changeActions) {
+ private void assertReindexingChange(String message, List<ConfigChangeAction> changeActions) {
List<ConfigChangeAction> reindexingActions = changeActions.stream()
.filter(a -> a instanceof ConfigChangeReindexAction)
.collect(Collectors.toList());
assertEquals(1, reindexingActions.size());
- assertEquals(allowed, reindexingActions.get(0).allowed());
assertTrue(reindexingActions.get(0) instanceof ConfigChangeReindexAction);
assertEquals("indexing-mode-change", ((ConfigChangeReindexAction)reindexingActions.get(0)).name());
assertEquals(message, reindexingActions.get(0).getMessage());
}
- private static final String getServices(String indexingMode) {
+ private static String getServices(String indexingMode) {
return "<services version='1.0'>" +
" <content id='default' version='1.0'>" +
" <redundancy>1</redundancy>" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidatorTest.java
index f5ef50ee3a4..18aac032fe7 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/StreamingSearchClusterChangeValidatorTest.java
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.application.validation.change;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ClusterSpec;
@@ -164,10 +165,9 @@ public class StreamingSearchClusterChangeValidatorTest {
private static VespaConfigChangeAction createFieldTypeChangeRefeedAction(String docType, List<ServiceInfo> service) {
return ConfigChangeTestUtils.newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Document type '" + docType + "': Field 'f1' changed: data type: 'string' -> 'int'",
- service, docType, Instant.now());
+ ValidationId.fieldTypeChange,
+ "Document type '" + docType + "': Field 'f1' changed: data type: 'string' -> 'int'",
+ service, docType);
}
private static VespaConfigChangeAction createAddFastAccessRestartAction() {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
index a4fbf474a7f..1f64d41e371 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentDatabaseChangeValidatorTest.java
@@ -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.vespa.model.application.validation.change.search;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.model.application.validation.change.VespaConfigChangeAction;
@@ -47,20 +48,17 @@ public class DocumentDatabaseChangeValidatorTest {
"field f2 type string { indexing: index | summary } " +
"field f3 type string { indexing: summary } " +
"field f4 type array<s> { struct-field s1 { indexing: attribute } }");
+ Instant.now();
f.assertValidation(Arrays.asList(
newRestartAction(ClusterSpec.Id.from("test"),
"Field 'f1' changed: add attribute aspect"),
newRestartAction(ClusterSpec.Id.from("test"),
"Field 'f4.s1' changed: add attribute aspect"),
newReindexAction(ClusterSpec.Id.from("test"),
- "indexing-change",
- ValidationOverrides.empty,
- "Field 'f2' changed: add index aspect, indexing script: '{ input f2 | summary f2; }' -> " +
- "'{ input f2 | tokenize normalize stem:\"BEST\" | index f2 | summary f2; }'", Instant.now()),
- newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f3' changed: data type: 'int' -> 'string'", Instant.now())));
+ ValidationId.indexingChange,
+ "Field 'f2' changed: add index aspect, indexing script: '{ input f2 | summary f2; }' -> " +
+ "'{ input f2 | tokenize normalize stem:\"BEST\" | index f2 | summary f2; }'"),
+ newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f3' changed: data type: 'int' -> 'string'")));
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java
index a074f961a53..8ee2a924503 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/DocumentTypeChangeValidatorTest.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.vespa.model.application.validation.change.search;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.document.DocumentType;
import com.yahoo.document.Field;
@@ -8,7 +9,6 @@ import com.yahoo.document.ReferenceDataType;
import com.yahoo.document.StructDataType;
import com.yahoo.documentmodel.NewDocumentType;
import com.yahoo.searchdefinition.FieldSets;
-import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.vespa.model.application.validation.change.VespaConfigChangeAction;
import com.yahoo.vespa.model.application.validation.change.VespaRefeedAction;
import org.junit.Test;
@@ -42,7 +42,7 @@ public class DocumentTypeChangeValidatorTest {
@Override
public List<VespaConfigChangeAction> validate() {
- return validator.validate(ValidationOverrides.empty, Instant.now());
+ return validator.validate();
}
}
@@ -65,21 +65,16 @@ public class DocumentTypeChangeValidatorTest {
public void requireThatDataTypeChangeIsNotOK() throws Exception {
Fixture f = new Fixture("field f1 type string { indexing: summary }",
"field f1 type int { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f1' changed: data type: 'string' -> 'int'",
- Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f1' changed: data type: 'string' -> 'int'"));
}
@Test
public void requireThatAddingCollectionTypeIsNotOK() throws Exception {
Fixture f = new Fixture("field f1 type string { indexing: summary }",
"field f1 type array<string> { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f1' changed: data type: 'string' -> 'Array<string>'", Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f1' changed: data type: 'string' -> 'Array<string>'"));
}
@@ -94,34 +89,26 @@ public class DocumentTypeChangeValidatorTest {
public void requireThatNestedDataTypeChangeIsNotOK() throws Exception {
Fixture f = new Fixture("field f1 type array<string> { indexing: summary }",
"field f1 type array<int> { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f1' changed: data type: 'Array<string>' -> 'Array<int>'", Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f1' changed: data type: 'Array<string>' -> 'Array<int>'"));
}
@Test
public void requireThatChangedCollectionTypeIsNotOK() throws Exception {
Fixture f = new Fixture("field f1 type array<string> { indexing: summary }",
"field f1 type weightedset<string> { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f1' changed: data type: 'Array<string>' -> 'WeightedSet<string>'", Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f1' changed: data type: 'Array<string>' -> 'WeightedSet<string>'"));
}
@Test
public void requireThatMultipleDataTypeChangesIsNotOK() throws Exception {
Fixture f = new Fixture("field f1 type string { indexing: summary } field f2 type int { indexing: summary }" ,
"field f2 type string { indexing: summary } field f1 type int { indexing: summary }");
- f.assertValidation(Arrays.asList(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f1' changed: data type: 'string' -> 'int'", Instant.now()),
- newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f2' changed: data type: 'int' -> 'string'", Instant.now())));
+ Instant.now();
+ Instant.now();
+ f.assertValidation(Arrays.asList(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f1' changed: data type: 'string' -> 'int'"),
+ newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f2' changed: data type: 'int' -> 'string'")));
}
@Test
@@ -156,40 +143,32 @@ public class DocumentTypeChangeValidatorTest {
public void requireThatDataTypeChangeInStructFieldIsNotOK() throws Exception {
Fixture f = new Fixture("struct s1 { field f1 type string {} } field f2 type s1 { indexing: summary }",
"struct s1 { field f1 type int {} } field f2 type s1 { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f2' changed: data type: 's1:{f1:string}' -> 's1:{f1:int}'", Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f2' changed: data type: 's1:{f1:string}' -> 's1:{f1:int}'"));
}
@Test
public void requireThatNestedDataTypeChangeInStructFieldIsNotOK() throws Exception {
Fixture f = new Fixture("struct s1 { field f1 type array<string> {} } field f2 type s1 { indexing: summary }",
"struct s1 { field f1 type array<int> {} } field f2 type s1 { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f2' changed: data type: 's1:{f1:Array<string>}' -> 's1:{f1:Array<int>}'", Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f2' changed: data type: 's1:{f1:Array<string>}' -> 's1:{f1:Array<int>}'"));
}
@Test
public void requireThatDataTypeChangeInNestedStructFieldIsNotOK() throws Exception {
Fixture f = new Fixture("struct s1 { field f1 type string {} } struct s2 { field f2 type s1 {} } field f3 type s2 { indexing: summary }",
"struct s1 { field f1 type int {} } struct s2 { field f2 type s1 {} } field f3 type s2 { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f3' changed: data type: 's2:{s1:{f1:string}}' -> 's2:{s1:{f1:int}}'", Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f3' changed: data type: 's2:{s1:{f1:string}}' -> 's2:{s1:{f1:int}}'"));
}
@Test
public void requireThatMultipleDataTypeChangesInStructFieldIsNotOK() throws Exception {
Fixture f = new Fixture("struct s1 { field f1 type string {} field f2 type int {} } field f3 type s1 { indexing: summary }",
"struct s1 { field f1 type int {} field f2 type string {} } field f3 type s1 { indexing: summary }");
- f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f3' changed: data type: 's1:{f1:string,f2:int}' -> 's1:{f1:int,f2:string}'", Instant.now()));
+ Instant.now();
+ f.assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f3' changed: data type: 's1:{f1:string,f2:int}' -> 's1:{f1:int,f2:string}'"));
}
@Test
@@ -197,7 +176,7 @@ public class DocumentTypeChangeValidatorTest {
var validator = new DocumentTypeChangeValidator(ClusterSpec.Id.from("test"),
createDocumentTypeWithReferenceField("oldDoc"),
createDocumentTypeWithReferenceField("newDoc"));
- List<VespaConfigChangeAction> result = validator.validate(ValidationOverrides.empty, Instant.now());
+ List<VespaConfigChangeAction> result = validator.validate();
assertEquals(1, result.size());
VespaConfigChangeAction action = result.get(0);
assertTrue(action instanceof VespaRefeedAction);
@@ -210,21 +189,17 @@ public class DocumentTypeChangeValidatorTest {
@Test
public void changing_tensor_type_of_tensor_field_requires_refeed() throws Exception {
+ Instant.now();
new Fixture(
"field f1 type tensor(x[2]) { indexing: attribute }",
"field f1 type tensor(x[3]) { indexing: attribute }")
- .assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f1' changed: data type: 'tensor(x[2])' -> 'tensor(x[3])'", Instant.now()));
+ .assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f1' changed: data type: 'tensor(x[2])' -> 'tensor(x[3])'"));
+ Instant.now();
new Fixture(
"field f1 type tensor(x[5]) { indexing: attribute }",
"field f1 type tensor(x[3]) { indexing: attribute }")
- .assertValidation(newRefeedAction(ClusterSpec.Id.from("test"),
- "field-type-change",
- ValidationOverrides.empty,
- "Field 'f1' changed: data type: 'tensor(x[5])' -> 'tensor(x[3])'", Instant.now()));
+ .assertValidation(newRefeedAction(ClusterSpec.Id.from("test"), ValidationId.fieldTypeChange, "Field 'f1' changed: data type: 'tensor(x[5])' -> 'tensor(x[3])'"));
}
private static NewDocumentType createDocumentTypeWithReferenceField(String nameReferencedDocumentType) {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidatorTest.java
index 9f418476a24..2e1ec53f886 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/IndexingScriptChangeValidatorTest.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.vespa.model.application.validation.change.search;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.indexinglanguage.expressions.ScriptExpression;
@@ -56,12 +57,10 @@ public class IndexingScriptChangeValidatorTest {
private static VespaConfigChangeAction expectedReindexingAction(String field, String changedMsg, String fromScript, String toScript) {
return VespaReindexAction.of(ClusterSpec.Id.from("test"),
- "indexing-change",
- ValidationOverrides.empty,
- "Field '" + field + "' changed: " +
+ ValidationId.indexingChange,
+ "Field '" + field + "' changed: " +
(changedMsg.isEmpty() ? "" : changedMsg + ", ") +
- "indexing script: '" + fromScript + "' -> '" + toScript + "'",
- Instant.now());
+ "indexing script: '" + fromScript + "' -> '" + toScript + "'");
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java
index 04efffab438..0bc4ecbfdfd 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/search/StructFieldAttributeChangeValidatorTestCase.java
@@ -36,7 +36,7 @@ public class StructFieldAttributeChangeValidatorTestCase {
public List<VespaConfigChangeAction> validate() {
List<VespaConfigChangeAction> result = new ArrayList<>();
result.addAll(structFieldAttributeValidator.validate(ValidationOverrides.empty, Instant.now()));
- result.addAll(docTypeValidator.validate(ValidationOverrides.empty, Instant.now()));
+ result.addAll(docTypeValidator.validate());
return result;
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java
index 62a36422dd8..7d4be4b5e33 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerIncludeTest.java
@@ -18,7 +18,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
- * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @author Einar M R Rosenvinge
* @since 5.1.13
*/
public class ContainerIncludeTest {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
index 1ac95ac9a99..4993a51ab74 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
@@ -6,8 +6,10 @@ import com.yahoo.config.model.builder.xml.test.DomBuilderTest;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.config.provision.AthenzDomain;
+import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.model.container.ApplicationContainer;
import com.yahoo.vespa.model.container.http.AccessControl;
+import com.yahoo.vespa.model.container.http.ConnectorFactory;
import com.yahoo.vespa.model.container.http.FilterChains;
import com.yahoo.vespa.model.container.http.Http;
import com.yahoo.vespa.model.container.http.ssl.HostedSslConnectorFactory;
@@ -50,6 +52,7 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
FilterChains filterChains = http.getFilterChains();
assertTrue(filterChains.hasChain(AccessControl.ACCESS_CONTROL_CHAIN_ID));
assertTrue(filterChains.hasChain(AccessControl.ACCESS_CONTROL_EXCLUDED_CHAIN_ID));
+ assertTrue(filterChains.hasChain(AccessControl.DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID));
}
@Test
@@ -297,6 +300,28 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
assertEquals(AccessControl.ClientAuthentication.want, http.getAccessControl().get().clientAuthentication);
}
+ @Test
+ public void local_connector_has_default_chain() {
+ Http http = createModelAndGetHttp(
+ " <http>",
+ " <filtering>",
+ " <access-control/>",
+ " </filtering>",
+ " </http>");
+
+ Set<String> actualBindings = getFilterBindings(http, AccessControl.DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID);
+ assertThat(actualBindings, empty());
+
+ ConnectorFactory connectorFactory = http.getHttpServer().get().getConnectorFactories().stream()
+ .filter(cf -> cf.getListenPort() == Defaults.getDefaults().vespaWebServicePort())
+ .findAny()
+ .get();
+
+ Optional<ComponentId> defaultChain = connectorFactory.getDefaultRequestFilterChain();
+ assertTrue(defaultChain.isPresent());
+ assertEquals(AccessControl.DEFAULT_CONNECTOR_HOSTED_REQUEST_CHAIN_ID, defaultChain.get());
+ }
+
private Http createModelAndGetHttp(String... httpElement) {
List<String> servicesXml = new ArrayList<>();
servicesXml.add("<container version='1.0'>");
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
index 92e9ca43193..fe0b9841d1c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
@@ -993,29 +993,4 @@ public class ContentClusterTest extends ContentBaseTest {
assertDirectStorageApiRpcFlagIsPropagatedToConfig(true);
}
-
- @Test
- public void use_fast_value_tensor_implementation_config_is_controlled_by_properties() {
- assertUseFastValueTensorImplementationFlagIsPropagatedToConfig(false);
- assertUseFastValueTensorImplementationFlagIsPropagatedToConfig(true);
- }
-
- void assertUseFastValueTensorImplementationFlagIsPropagatedToConfig(boolean useFastValueTensorImplementation) {
- VespaModel model = createEnd2EndOneNode(new TestProperties().setUseFastValueTensorImplementation(useFastValueTensorImplementation));
- ContentCluster cc = model.getContentClusters().get("storage");
- var node = cc.getSearch().getSearchNodes().get(0);
- if (useFastValueTensorImplementation) {
- assertTensorImplementationConfig(ProtonConfig.Tensor_implementation.FAST_VALUE, node);
- } else {
- assertTensorImplementationConfig(ProtonConfig.Tensor_implementation.TENSOR_ENGINE, node);
- }
- }
-
- void assertTensorImplementationConfig(ProtonConfig.Tensor_implementation.Enum exp, SearchNode node) {
- var builder = new ProtonConfig.Builder();
- node.getConfig(builder);
- var config = new ProtonConfig(builder);
- assertEquals(exp, config.tensor_implementation());
- }
-
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterResources.java
index f1c86485a64..8f4c9f81d7f 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterResources.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterResources.java
@@ -1,6 +1,8 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.provision;
+import com.yahoo.config.Node;
+
import java.util.List;
import java.util.Objects;
@@ -53,6 +55,14 @@ public class ClusterResources {
return true;
}
+ /** Returns the total resources of this, that is the number of nodes times the node resources */
+ public NodeResources totalResources() {
+ return nodeResources.withVcpu(nodeResources.vcpu() * nodes)
+ .withMemoryGb(nodeResources.memoryGb() * nodes)
+ .withDiskGb(nodeResources.diskGb() * nodes)
+ .withBandwidthGbps(nodeResources.bandwidthGbps() * nodes);
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) return true;
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java b/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java
index cea40da52b9..f79abbddc56 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config;
import ai.vespa.util.http.VespaHttpClientBuilder;
@@ -72,23 +72,22 @@ public class ConfigVerification {
for (Map.Entry<String, Stack<String>> entry : mappings.entrySet()) {
recurseUrls.add(entry.getValue().pop());
}
- int ret = compareOutputs(performRequests(recurseUrls, httpClient));
- if (ret != 0) {
- return ret;
- }
+ if ( ! equalOutputs(performRequests(recurseUrls, httpClient)))
+ return -1;
}
return 0;
}
- private static int compareOutputs(Map<String, String> outputs) {
+ private static boolean equalOutputs(Map<String, String> outputs) {
Map.Entry<String, String> firstEntry = outputs.entrySet().iterator().next();
for (Map.Entry<String, String> entry : outputs.entrySet()) {
if (!entry.getValue().equals(firstEntry.getValue())) {
- System.out.println("output from '" + entry.getKey() + "' did not equal output from '" + firstEntry.getKey() + "'");
- return -1;
+ System.out.println("output from '" + entry.getKey() + "': '" + entry.getValue() +
+ "' did not equal output from '" + firstEntry.getKey() + "': '" + firstEntry.getValue() + "'");
+ return false;
}
}
- return 0;
+ return true;
}
private static String performRequest(String url, CloseableHttpClient httpClient) throws IOException {
diff --git a/configdefinitions/src/vespa/lb-services.def b/configdefinitions/src/vespa/lb-services.def
index 33c568061fe..f22f5e5cb1c 100644
--- a/configdefinitions/src/vespa/lb-services.def
+++ b/configdefinitions/src/vespa/lb-services.def
@@ -7,6 +7,7 @@ namespace=cloud.config
# Active rotation given as flag 'active' for a prod region in deployment.xml
# Default true for now (since code in config-model to set it is not ready yet), should have no default value
tenants{}.applications{}.activeRotation bool default=true
+tenants{}.applications{}.usePowerOfTwoChoicesLb bool default=false
tenants{}.applications{}.hosts{}.hostname string default="(unknownhostname)"
tenants{}.applications{}.hosts{}.services{}.type string default="(noservicetype)"
diff --git a/configdefinitions/src/vespa/zookeeper-server.def b/configdefinitions/src/vespa/zookeeper-server.def
index 483e772b818..5aa0bb2ae9b 100644
--- a/configdefinitions/src/vespa/zookeeper-server.def
+++ b/configdefinitions/src/vespa/zookeeper-server.def
@@ -1,4 +1,4 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
namespace=cloud.config
# Vespa home is prepended if the file is relative
@@ -43,3 +43,5 @@ trustEmptySnapshot bool default=true
tlsForQuorumCommunication enum { OFF, PORT_UNIFICATION, TLS_WITH_PORT_UNIFICATION, TLS_ONLY } default=OFF
tlsForClientServerCommunication enum { OFF, PORT_UNIFICATION, TLS_WITH_PORT_UNIFICATION, TLS_ONLY } default=OFF
jksKeyStoreFile string default="conf/zookeeper/zookeeper.jks"
+
+dynamicReconfiguration bool default=false
diff --git a/configserver/pom.xml b/configserver/pom.xml
index 8cd1b4b4254..35c38eb7d7d 100644
--- a/configserver/pom.xml
+++ b/configserver/pom.xml
@@ -34,6 +34,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>defaults</artifactId>
<version>${project.version}</version>
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index b356b99c50a..6cc05a0f69e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -326,12 +326,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
long sessionId = createSession(applicationId, prepareParams.getTimeoutBudget(), applicationPackage);
Deployment deployment = prepare(sessionId, prepareParams, logger);
- if (deployment.configChangeActions().getRefeedActions().getEntries().stream().anyMatch(entry -> ! entry.allowed()))
- logger.log(Level.WARNING, "Activation rejected because of disallowed re-feed actions");
- else if (deployment.configChangeActions().getReindexActions().getEntries().stream().anyMatch(entry -> ! entry.allowed()))
- logger.log(Level.WARNING, "Activation rejected because of disallowed re-index actions");
- else
- deployment.activate();
+ deployment.activate();
return new PrepareResult(sessionId, deployment.configChangeActions(), logger);
}
@@ -1014,21 +1009,19 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
RestartActions restartActions = actions.getRestartActions();
if ( ! restartActions.isEmpty()) {
logger.log(Level.WARNING, "Change(s) between active and new application that require restart:\n" +
- restartActions.format());
+ restartActions.format());
}
RefeedActions refeedActions = actions.getRefeedActions();
if ( ! refeedActions.isEmpty()) {
- boolean allAllowed = refeedActions.getEntries().stream().allMatch(RefeedActions.Entry::allowed);
- logger.log(allAllowed ? Level.INFO : Level.WARNING,
+ logger.log(Level.WARNING,
"Change(s) between active and new application that may require re-feed:\n" +
- refeedActions.format());
+ refeedActions.format());
}
ReindexActions reindexActions = actions.getReindexActions();
if ( ! reindexActions.isEmpty()) {
- boolean allAllowed = reindexActions.getEntries().stream().allMatch(ReindexActions.Entry::allowed);
- logger.log(allAllowed ? Level.INFO : Level.WARNING,
- "Change(s) between active and new application that may require re-index:\n" +
- reindexActions.format());
+ logger.log(Level.WARNING,
+ "Change(s) between active and new application that may require re-index:\n" +
+ reindexActions.format());
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java
index 2e73a02c75b..8d001d5d5df 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java
@@ -130,7 +130,13 @@ public class Application implements ModelResult {
if (logDebug()) {
debug("Resolving " + configKey + " with config definition " + def);
}
- ConfigPayload payload = model.getConfig(configKey, def);
+
+ ConfigPayload payload = null;
+ try {
+ payload = model.getConfig(configKey, def);
+ } catch (Exception e) {
+ throw new ConfigurationRuntimeException("Unable to get config for " + app, e);
+ }
if (payload == null) {
metricUpdater.incrementFailedRequests();
throw new ConfigurationRuntimeException("Unable to resolve config " + configKey);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java
index ed9f12484b8..f98d58d9fb4 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ApplicationCuratorDatabase.java
@@ -73,11 +73,12 @@ public class ApplicationCuratorDatabase {
/**
* Creates a node for the given application, marking its existence.
*/
- public void createApplication(ApplicationId id) {
+ public void createApplication(ApplicationId id, Instant now) {
if ( ! id.tenant().equals(tenant))
throw new IllegalArgumentException("Cannot write application id '" + id + "' for tenant '" + tenant + "'");
try (Lock lock = lock(id)) {
curator.create(applicationPath(id));
+ modifyReindexing(id, ApplicationReindexing.ready(now), UnaryOperator.identity());
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ClusterReindexing.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ClusterReindexing.java
new file mode 100644
index 00000000000..ca9aa01ea56
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ClusterReindexing.java
@@ -0,0 +1,58 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.application;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Reindexing status for each document type in a content cluster.
+ *
+ * @author jonmv
+ */
+public class ClusterReindexing {
+
+ private static final ClusterReindexing empty = new ClusterReindexing(Map.of());
+
+ private final Map<String, Status> documentTypeStatus;
+
+ public ClusterReindexing(Map<String, Status> documentTypeStatus) {
+ this.documentTypeStatus = Map.copyOf(documentTypeStatus);
+ }
+
+ public static ClusterReindexing empty() { return empty; }
+
+ public Map<String, Status> documentTypeStatus() { return documentTypeStatus; }
+
+
+ public static class Status {
+
+ private final Instant startedAt;
+ private final Instant endedAt;
+ private final State state;
+ private final String message;
+ private final String progress;
+
+ public Status(Instant startedAt, Instant endedAt, State state, String message, String progress) {
+ this.startedAt = Objects.requireNonNull(startedAt);
+ this.endedAt = endedAt;
+ this.state = state;
+ this.message = message;
+ this.progress = progress;
+ }
+
+ public Instant startedAt() { return startedAt; }
+ public Optional<Instant> endedAt() { return Optional.ofNullable(endedAt); }
+ public Optional<State> state() { return Optional.ofNullable(state); }
+ public Optional<String> message() { return Optional.ofNullable(message); }
+ public Optional<String> progress() { return Optional.ofNullable(progress); }
+ }
+
+
+ public enum State {
+
+ PENDING, RUNNING, FAILED, SUCCESSFUL;
+
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
index 6b316c06b54..434b254336c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/ConfigConvergenceChecker.java
@@ -1,27 +1,32 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.application;
-import ai.vespa.util.http.VespaClientBuilderFactory;
+import ai.vespa.util.http.VespaAsyncHttpClientBuilder;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
+import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.PortInfo;
import com.yahoo.config.model.api.ServiceInfo;
-import java.util.logging.Level;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.config.server.http.JSONResponse;
-import org.glassfish.jersey.client.ClientProperties;
-import org.glassfish.jersey.client.proxy.WebResourceFactory;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.ProcessingException;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientRequestFilter;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.HttpHeaders;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.net.URIBuilder;
+import org.apache.hc.core5.util.Timeout;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -29,8 +34,15 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
import static com.yahoo.config.model.api.container.ContainerServiceType.CLUSTERCONTROLLER_CONTAINER;
import static com.yahoo.config.model.api.container.ContainerServiceType.CONTAINER;
@@ -42,12 +54,12 @@ import static com.yahoo.config.model.api.container.ContainerServiceType.QRSERVER
*
* @author Ulf Lilleengen
* @author hmusum
+ * @author bjorncs
*/
public class ConfigConvergenceChecker extends AbstractComponent {
private static final Logger log = Logger.getLogger(ConfigConvergenceChecker.class.getName());
- private static final String statePath = "/state/v1/";
- private static final String configSubPath = "config";
+
private final static Set<String> serviceTypesToCheck = Set.of(
CONTAINER.serviceName,
QRSERVER.serviceName,
@@ -58,17 +70,13 @@ public class ConfigConvergenceChecker extends AbstractComponent {
"distributor"
);
- private final StateApiFactory stateApiFactory;
- private final VespaClientBuilderFactory clientBuilderFactory = new VespaClientBuilderFactory();
- @Inject
- public ConfigConvergenceChecker() {
- this(ConfigConvergenceChecker::createStateApi);
- }
+ private final Executor responseHandlerExecutor =
+ Executors.newSingleThreadExecutor(new DaemonThreadFactory("config-convergence-checker-response-handler-"));
+ private final ObjectMapper jsonMapper = new ObjectMapper();
- public ConfigConvergenceChecker(StateApiFactory stateApiFactory) {
- this.stateApiFactory = stateApiFactory;
- }
+ @Inject
+ public ConfigConvergenceChecker() {}
/** Fetches the active config generation for all services in the given application. */
public Map<ServiceInfo, Long> getServiceConfigGenerations(Application application, Duration timeoutPerService) {
@@ -82,7 +90,7 @@ public class ConfigConvergenceChecker extends AbstractComponent {
}
/** Check all services in given application. Returns the minimum current generation of all services */
- public ServiceListResponse getServiceConfigGenerationsResponse(Application application, URI requestUrl, Duration timeoutPerService) {
+ public JSONResponse getServiceConfigGenerationsResponse(Application application, URI requestUrl, Duration timeoutPerService) {
Map<ServiceInfo, Long> currentGenerations = getServiceConfigGenerations(application, timeoutPerService);
long currentGeneration = currentGenerations.values().stream().mapToLong(Long::longValue).min().orElse(-1);
return new ServiceListResponse(200, currentGenerations, requestUrl, application.getApplicationGeneration(),
@@ -90,64 +98,81 @@ public class ConfigConvergenceChecker extends AbstractComponent {
}
/** Check service identified by host and port in given application */
- public ServiceResponse getServiceConfigGenerationResponse(Application application, String hostAndPortToCheck, URI requestUrl, Duration timeout) {
+ public JSONResponse getServiceConfigGenerationResponse(Application application, String hostAndPortToCheck, URI requestUrl, Duration timeout) {
Long wantedGeneration = application.getApplicationGeneration();
- try {
+ try (CloseableHttpAsyncClient client = createHttpClient()) {
+ client.start();
if ( ! hostInApplication(application, hostAndPortToCheck))
return ServiceResponse.createHostNotFoundInAppResponse(requestUrl, hostAndPortToCheck, wantedGeneration);
-
- long currentGeneration = getServiceGeneration(URI.create("http://" + hostAndPortToCheck), timeout);
+ long currentGeneration = getServiceGeneration(client, URI.create("http://" + hostAndPortToCheck), timeout).get();
boolean converged = currentGeneration >= wantedGeneration;
return ServiceResponse.createOkResponse(requestUrl, hostAndPortToCheck, wantedGeneration, currentGeneration, converged);
- } catch (ProcessingException e) { // e.g. if we cannot connect to the service to find generation
+ } catch (InterruptedException | ExecutionException | CancellationException e) { // e.g. if we cannot connect to the service to find generation
return ServiceResponse.createNotFoundResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage());
} catch (Exception e) {
return ServiceResponse.createErrorResponse(requestUrl, hostAndPortToCheck, wantedGeneration, e.getMessage());
}
}
- @Override
- public void deconstruct() {
- clientBuilderFactory.close();
- }
-
- @Path(statePath)
- public interface StateApi {
- @Path(configSubPath)
- @GET
- JsonNode config();
- }
-
- public interface StateApiFactory {
- StateApi createStateApi(Client client, URI serviceUri);
- }
-
/** Gets service generation for a list of services (in parallel). */
private Map<ServiceInfo, Long> getServiceGenerations(List<ServiceInfo> services, Duration timeout) {
- return services.parallelStream()
- .collect(Collectors.toMap(service -> service,
- service -> {
- try {
- return getServiceGeneration(URI.create("http://" + service.getHostName()
- + ":" + getStatePort(service).get()), timeout);
- }
- catch (ProcessingException e) { // Cannot connect to service to determine service generation
- return -1L;
- }
- },
- (v1, v2) -> { throw new IllegalStateException("Duplicate keys for values '" + v1 + "' and '" + v2 + "'."); },
- LinkedHashMap::new
- ));
+ try (CloseableHttpAsyncClient client = createHttpClient()) {
+ client.start();
+ List<CompletableFuture<Void>> inprogressRequests = new ArrayList<>();
+ ConcurrentMap<ServiceInfo, Long> temporaryResult = new ConcurrentHashMap<>();
+ for (ServiceInfo service : services) {
+ int statePort = getStatePort(service).orElse(0);
+ if (statePort <= 0) continue;
+ URI uri = URI.create("http://" + service.getHostName() + ":" + statePort);
+ CompletableFuture<Void> inprogressRequest = getServiceGeneration(client, uri, timeout)
+ .handle((result, error) -> {
+ if (result != null) {
+ temporaryResult.put(service, result);
+ } else {
+ log.log(
+ Level.FINE,
+ error,
+ () -> String.format("Failed to retrieve service config generation for '%s': %s", service, error.getMessage()));
+ temporaryResult.put(service, -1L);
+ }
+ return null;
+ });
+ inprogressRequests.add(inprogressRequest);
+ }
+ CompletableFuture.allOf(inprogressRequests.toArray(CompletableFuture[]::new)).join();
+ return createMapOrderedByServiceList(services, temporaryResult);
+ } catch (IOException e) {
+ // Actual client implementation does not throw IOException on close()
+ throw new UncheckedIOException(e);
+ }
}
/** Get service generation of service at given URL */
- private long getServiceGeneration(URI serviceUrl, Duration timeout) {
- Client client = createClient(timeout);
+ private CompletableFuture<Long> getServiceGeneration(CloseableHttpAsyncClient client, URI serviceUrl, Duration timeout) {
+ SimpleHttpRequest request = SimpleHttpRequests.get(createApiUri(serviceUrl));
+ request.setHeader("Connection", "close");
+ request.setConfig(createRequestConfig(timeout));
+
+ // Ignoring returned Future object as we want to use the more flexible CompletableFuture instead
+ CompletableFuture<SimpleHttpResponse> responsePromise = new CompletableFuture<>();
+ client.execute(request, new FutureCallback<>() {
+ @Override public void completed(SimpleHttpResponse result) { responsePromise.complete(result); }
+ @Override public void failed(Exception ex) { responsePromise.completeExceptionally(ex); }
+ @Override public void cancelled() { responsePromise.cancel(false); }
+ });
+
+ // Don't do json parsing in http client's thread
+ return responsePromise.thenApplyAsync(this::handleResponse, responseHandlerExecutor);
+ }
+
+ private long handleResponse(SimpleHttpResponse response) throws UncheckedIOException {
try {
- StateApi state = stateApiFactory.createStateApi(client, serviceUrl);
- return generationFromContainerState(state.config());
- } finally {
- client.close();
+ int statusCode = response.getCode();
+ if (statusCode != HttpStatus.SC_OK) throw new IOException("Expected status code 200, got " + statusCode);
+ if (response.getBody() == null) throw new IOException("Response has no content");
+ return generationFromContainerState(jsonMapper.readTree(response.getBodyText()));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
}
@@ -166,16 +191,6 @@ public class ConfigConvergenceChecker extends AbstractComponent {
return false;
}
- private Client createClient(Duration timeout) {
- return clientBuilderFactory.newBuilder()
- .register(
- (ClientRequestFilter) ctx ->
- ctx.getHeaders().put(HttpHeaders.USER_AGENT, List.of("config-convergence-checker")))
- .property(ClientProperties.CONNECT_TIMEOUT, (int) timeout.toMillis())
- .property(ClientProperties.READ_TIMEOUT, (int) timeout.toMillis())
- .build();
- }
-
private static Optional<Integer> getStatePort(ServiceInfo service) {
return service.getPorts().stream()
.filter(port -> port.getTags().contains("state"))
@@ -187,9 +202,47 @@ public class ConfigConvergenceChecker extends AbstractComponent {
return state.get("config").get("generation").asLong(-1);
}
- private static StateApi createStateApi(Client client, URI uri) {
- WebTarget target = client.target(uri);
- return WebResourceFactory.newResource(StateApi.class, target);
+ private static Map<ServiceInfo, Long> createMapOrderedByServiceList(
+ List<ServiceInfo> services, ConcurrentMap<ServiceInfo, Long> result) {
+ Map<ServiceInfo, Long> orderedResult = new LinkedHashMap<>();
+ for (ServiceInfo service : services) {
+ Long generation = result.get(service);
+ if (generation != null) {
+ orderedResult.put(service, generation);
+ }
+ }
+ return orderedResult;
+ }
+
+ private static URI createApiUri(URI serviceUrl) {
+ try {
+ return new URIBuilder(serviceUrl)
+ .setPath("/state/v1/config")
+ .build();
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private static RequestConfig createRequestConfig(Duration timeout) {
+ return RequestConfig.custom()
+ .setConnectionRequestTimeout(Timeout.ofSeconds(1))
+ .setResponseTimeout(Timeout.ofMilliseconds(timeout.toMillis()))
+ .setConnectTimeout(Timeout.ofSeconds(1))
+ .build();
+ }
+
+ private static CloseableHttpAsyncClient createHttpClient() {
+ return VespaAsyncHttpClientBuilder
+ .create(tlsStrategy ->
+ PoolingAsyncClientConnectionManagerBuilder.create()
+ .setMaxConnTotal(100)
+ .setMaxConnPerRoute(10)
+ .setTlsStrategy(tlsStrategy)
+ .build())
+ .setUserAgent("config-convergence-checker")
+ .setConnectionReuseStrategy((request, response, context) -> false) // Disable connection reuse
+ .build();
}
private static class ServiceListResponse extends JSONResponse {
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
index f07a595a830..0eeb9759a39 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java
@@ -64,13 +64,13 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
private final HostRegistry<ApplicationId> hostRegistry;
private final ApplicationMapper applicationMapper = new ApplicationMapper();
private final MetricUpdater tenantMetricUpdater;
- private final Clock clock = Clock.systemUTC();
+ private final Clock clock;
private final TenantFileSystemDirs tenantFileSystemDirs;
public TenantApplications(TenantName tenant, Curator curator, StripedExecutor<TenantName> zkWatcherExecutor,
ExecutorService zkCacheExecutor, Metrics metrics, ReloadListener reloadListener,
ConfigserverConfig configserverConfig, HostRegistry<ApplicationId> hostRegistry,
- TenantFileSystemDirs tenantFileSystemDirs) {
+ TenantFileSystemDirs tenantFileSystemDirs, Clock clock) {
this.database = new ApplicationCuratorDatabase(tenant, curator);
this.tenant = tenant;
this.zkWatcherExecutor = command -> zkWatcherExecutor.execute(tenant, command);
@@ -83,6 +83,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
this.tenantMetricUpdater = metrics.getOrCreateMetricUpdater(Metrics.createDimensions(tenant));
this.hostRegistry = hostRegistry;
this.tenantFileSystemDirs = tenantFileSystemDirs;
+ this.clock = clock;
}
// For testing only
@@ -95,7 +96,8 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
componentRegistry.getReloadListener(),
componentRegistry.getConfigserverConfig(),
componentRegistry.getHostRegistries().createApplicationHostRegistry(tenantName),
- new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName));
+ new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName),
+ componentRegistry.getClock());
}
/** The curator backed ZK storage of this. */
@@ -140,7 +142,7 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
* Creates a node for the given application, marking its existence.
*/
public void createApplication(ApplicationId id) {
- database().createApplication(id);
+ database().createApplication(id, clock.instant());
}
/**
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverter.java b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverter.java
index ec48b671a5b..1a0d109b6c9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverter.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverter.java
@@ -42,7 +42,6 @@ public class ConfigChangeActionsSlimeConverter {
for (RefeedActions.Entry entry : actions.getRefeedActions().getEntries()) {
Cursor entryCursor = refeedCursor.addObject();
entryCursor.setString("name", entry.name());
- entryCursor.setBool("allowed", entry.allowed());
entryCursor.setString("documentType", entry.getDocumentType());
entryCursor.setString("clusterName", entry.getClusterName());
messagesToSlime(entryCursor, entry.getMessages());
@@ -55,7 +54,6 @@ public class ConfigChangeActionsSlimeConverter {
for (ReindexActions.Entry entry : actions.getReindexActions().getEntries()) {
Cursor entryCursor = refeedCursor.addObject();
entryCursor.setString("name", entry.name());
- entryCursor.setBool("allowed", entry.allowed());
entryCursor.setString("documentType", entry.getDocumentType());
entryCursor.setString("clusterName", entry.getClusterName());
messagesToSlime(entryCursor, entry.getMessages());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActions.java b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActions.java
index c20b8527f2e..b2221cbcf6c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActions.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActions.java
@@ -5,7 +5,13 @@ import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.ConfigChangeRefeedAction;
import com.yahoo.config.model.api.ServiceInfo;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
/**
* Represents all actions to re-feed document types in order to handle config changes.
@@ -17,15 +23,13 @@ public class RefeedActions {
public static class Entry {
private final String name;
- private final boolean allowed;
private final String documentType;
private final String clusterName;
private final Set<ServiceInfo> services = new LinkedHashSet<>();
private final Set<String> messages = new TreeSet<>();
- private Entry(String name, boolean allowed, String documentType, String clusterName) {
+ private Entry(String name, String documentType, String clusterName) {
this.name = name;
- this.allowed = allowed;
this.documentType = documentType;
this.clusterName = clusterName;
}
@@ -42,8 +46,6 @@ public class RefeedActions {
public String name() { return name; }
- public boolean allowed() { return allowed; }
-
public String getDocumentType() { return documentType; }
public String getClusterName() { return clusterName; }
@@ -54,12 +56,12 @@ public class RefeedActions {
}
- private Entry addEntry(String name, boolean allowed, String documentType, ServiceInfo service) {
+ private Entry addEntry(String name, String documentType, ServiceInfo service) {
String clusterName = service.getProperty("clustername").orElse("");
- String entryId = name + "." + allowed + "." + clusterName + "." + documentType;
+ String entryId = name + "." + "." + clusterName + "." + documentType;
Entry entry = actions.get(entryId);
if (entry == null) {
- entry = new Entry(name, allowed, documentType, clusterName);
+ entry = new Entry(name, documentType, clusterName);
actions.put(entryId, entry);
}
return entry;
@@ -75,7 +77,7 @@ public class RefeedActions {
if (action.getType().equals(ConfigChangeAction.Type.REFEED)) {
ConfigChangeRefeedAction refeedAction = (ConfigChangeRefeedAction) action;
for (ServiceInfo service : refeedAction.getServices()) {
- addEntry(refeedAction.name(), refeedAction.allowed(), refeedAction.getDocumentType(), service).
+ addEntry(refeedAction.name(), refeedAction.getDocumentType(), service).
addService(service).
addMessage(action.getMessage());
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatter.java b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatter.java
index 425276cebd6..6e2e23ab6be 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatter.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatter.java
@@ -18,8 +18,6 @@ public class RefeedActionsFormatter {
public String format() {
StringBuilder builder = new StringBuilder();
for (RefeedActions.Entry entry : actions.getEntries()) {
- if (entry.allowed())
- builder.append("(allowed) ");
builder.append(entry.name() + ": Consider removing data and re-feed document type '" + entry.getDocumentType() +
"' in cluster '" + entry.getClusterName() + "' because:\n");
int counter = 1;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActions.java b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActions.java
index e328f9595b7..6ed1c43623f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActions.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActions.java
@@ -27,7 +27,7 @@ public class ReindexActions {
if (action.getType().equals(ConfigChangeAction.Type.REINDEX)) {
ConfigChangeReindexAction reindexChange = (ConfigChangeReindexAction) action;
for (ServiceInfo service : reindexChange.getServices()) {
- addEntry(reindexChange.name(), reindexChange.allowed(), reindexChange.getDocumentType(), service).
+ addEntry(reindexChange.name(), reindexChange.getDocumentType(), service).
addService(service).
addMessage(action.getMessage());
}
@@ -35,12 +35,12 @@ public class ReindexActions {
}
}
- private Entry addEntry(String name, boolean allowed, String documentType, ServiceInfo service) {
+ private Entry addEntry(String name, String documentType, ServiceInfo service) {
String clusterName = service.getProperty("clustername").orElse("");
- String entryId = name + "." + allowed + "." + clusterName + "." + documentType;
+ String entryId = name + "." + "." + clusterName + "." + documentType;
Entry entry = actions.get(entryId);
if (entry == null) {
- entry = new Entry(name, allowed, documentType, clusterName);
+ entry = new Entry(name, documentType, clusterName);
actions.put(entryId, entry);
}
return entry;
@@ -53,15 +53,13 @@ public class ReindexActions {
public static class Entry {
private final String name;
- private final boolean allowed;
private final String documentType;
private final String clusterName;
private final Set<ServiceInfo> services = new LinkedHashSet<>();
private final Set<String> messages = new TreeSet<>();
- private Entry(String name, boolean allowed, String documentType, String clusterName) {
+ private Entry(String name, String documentType, String clusterName) {
this.name = name;
- this.allowed = allowed;
this.documentType = documentType;
this.clusterName = clusterName;
}
@@ -77,7 +75,6 @@ public class ReindexActions {
}
public String name() { return name; }
- public boolean allowed() { return allowed; }
public String getDocumentType() { return documentType; }
public String getClusterName() { return clusterName; }
public Set<ServiceInfo> getServices() { return services; }
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatter.java b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatter.java
index e89bfd522cd..bdd01404f64 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatter.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatter.java
@@ -17,8 +17,6 @@ class ReindexActionsFormatter {
String format() {
StringBuilder builder = new StringBuilder();
for (ReindexActions.Entry entry : actions.getEntries()) {
- if (entry.allowed())
- builder.append("(allowed) ");
builder.append(entry.name() + ": Consider re-indexing document type '" + entry.getDocumentType() +
"' in cluster '" + entry.getClusterName() + "' because:\n");
int counter = 1;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
index 6fb315dc3b3..b924e07ff47 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java
@@ -18,6 +18,7 @@ import com.yahoo.jdisc.application.UriPattern;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.application.ApplicationReindexing;
+import com.yahoo.vespa.config.server.application.ClusterReindexing;
import com.yahoo.vespa.config.server.http.ContentHandler;
import com.yahoo.vespa.config.server.http.ContentRequest;
import com.yahoo.vespa.config.server.http.HttpErrorResponse;
@@ -29,7 +30,9 @@ import com.yahoo.vespa.config.server.tenant.Tenant;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
@@ -249,9 +252,10 @@ public class ApplicationHandler extends HttpHandler {
if (tenant == null)
throw new NotFoundException("Tenant '" + applicationId.tenant().value() + "' not found");
- return new ReindexResponse(tenant.getApplicationRepo().database()
- .readReindexingStatus(applicationId)
- .orElseThrow(() -> new NotFoundException("Reindexing status not found for " + applicationId)));
+ return new ReindexingResponse(tenant.getApplicationRepo().database()
+ .readReindexingStatus(applicationId)
+ .orElseThrow(() -> new NotFoundException("Reindexing status not found for " + applicationId)),
+ Map.of()); // TODO jonmv/bjorncs: Get status of each cluster and fill in here.
}
private HttpResponse restart(HttpRequest request, ApplicationId applicationId) {
@@ -439,31 +443,59 @@ public class ApplicationHandler extends HttpHandler {
}
}
- private static class ReindexResponse extends JSONResponse {
- ReindexResponse(ApplicationReindexing reindexing) {
+ static class ReindexingResponse extends JSONResponse {
+ ReindexingResponse(ApplicationReindexing reindexing, Map<String, ClusterReindexing> clusters) {
super(Response.Status.OK);
- object.setBool("enabled", reindexing.enabled());
- setStatus(object.setObject("status"), reindexing.common());
-
- Cursor clustersObject = object.setObject("clusters");
- reindexing.clusters().entrySet().stream().sorted(comparingByKey())
- .forEach(cluster -> {
- Cursor clusterObject = clustersObject.setObject(cluster.getKey());
- setStatus(clusterObject.setObject("status"), cluster.getValue().common());
-
- Cursor pendingObject = clusterObject.setObject("pending");
- cluster.getValue().pending().entrySet().stream().sorted(comparingByKey())
- .forEach(pending -> pendingObject.setLong(pending.getKey(), pending.getValue()));
-
- Cursor readyObject = clusterObject.setObject("ready");
- cluster.getValue().ready().entrySet().stream().sorted(comparingByKey())
- .forEach(ready -> setStatus(readyObject.setObject(ready.getKey()), ready.getValue()));
- });
+ object.setBool("enabled", reindexing.enabled());
+ setStatus(object.setObject("status"), reindexing.common());
+
+ Cursor clustersObject = object.setObject("clusters");
+ Stream<String> clusterNames = Stream.concat(clusters.keySet().stream(), reindexing.clusters().keySet().stream());
+ clusterNames.sorted()
+ .forEach(clusterName -> {
+ Cursor clusterObject = clustersObject.setObject(clusterName);
+ Cursor pendingObject = clusterObject.setObject("pending");
+ Cursor readyObject = clusterObject.setObject("ready");
+
+ Map<String, Cursor> statuses = new HashMap<>();
+ if (reindexing.clusters().containsKey(clusterName)) {
+ setStatus(clusterObject.setObject("status"), reindexing.clusters().get(clusterName).common());
+
+ reindexing.clusters().get(clusterName).pending().entrySet().stream().sorted(comparingByKey())
+ .forEach(pending -> pendingObject.setLong(pending.getKey(), pending.getValue()));
+
+ reindexing.clusters().get(clusterName).ready().entrySet().stream().sorted(comparingByKey())
+ .forEach(ready -> setStatus(statuses.computeIfAbsent(ready.getKey(), readyObject::setObject), ready.getValue()));
+ }
+ if (clusters.containsKey(clusterName))
+ clusters.get(clusterName).documentTypeStatus().entrySet().stream().sorted(comparingByKey())
+ .forEach(status -> setStatus(statuses.computeIfAbsent(status.getKey(), readyObject::setObject), status.getValue()));
+
+ });
}
- private static void setStatus(Cursor object, ApplicationReindexing.Status status) {
- object.setLong("readyMillis", status.ready().toEpochMilli());
+ private static void setStatus(Cursor object, ApplicationReindexing.Status readyStatus) {
+ object.setLong("readyMillis", readyStatus.ready().toEpochMilli());
}
+
+ private static void setStatus(Cursor object, ClusterReindexing.Status status) {
+ object.setLong("startedMillis", status.startedAt().toEpochMilli());
+ status.endedAt().ifPresent(endedAt -> object.setLong("endedMillis", endedAt.toEpochMilli()));
+ status.state().map(ReindexingResponse::toString).ifPresent(state -> object.setString("state", state));
+ status.message().ifPresent(message -> object.setString("message", message));
+ status.progress().ifPresent(progress -> object.setString("progress", progress));
+ }
+
+ static String toString(ClusterReindexing.State state) {
+ switch (state) {
+ case PENDING: return "pending";
+ case RUNNING: return "running";
+ case FAILED: return "failed";
+ case SUCCESSFUL: return "successful";
+ default: throw new IllegalArgumentException("Unexpected state '" + state + "'");
+ }
+ }
+
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
index 19534bba810..e59c334b89f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/SessionsMaintainer.java
@@ -1,12 +1,12 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.maintenance;
-import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
import java.time.Duration;
+import java.util.logging.Level;
/**
* Removes expired sessions and locks
@@ -17,6 +17,7 @@ import java.time.Duration;
*/
public class SessionsMaintainer extends ConfigServerMaintainer {
private final boolean hostedVespa;
+ private int iteration = 0;
SessionsMaintainer(ApplicationRepository applicationRepository, Curator curator, Duration interval, FlagSource flagSource) {
super(applicationRepository, curator, flagSource, Duration.ofMinutes(1), interval);
@@ -25,14 +26,18 @@ public class SessionsMaintainer extends ConfigServerMaintainer {
@Override
protected boolean maintain() {
+ if (iteration % 10 == 0)
+ log.log(Level.INFO, () -> "Running " + SessionsMaintainer.class.getSimpleName() + ", iteration " + iteration);
+
applicationRepository.deleteExpiredLocalSessions();
if (hostedVespa) {
Duration expiryTime = Duration.ofMinutes(90);
int deleted = applicationRepository.deleteExpiredRemoteSessions(expiryTime);
- log.log(LogLevel.FINE, () -> "Deleted " + deleted + " expired remote sessions older than " + expiryTime);
+ log.log(Level.FINE, () -> "Deleted " + deleted + " expired remote sessions older than " + expiryTime);
}
+ iteration++;
return true;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/model/LbServicesProducer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/model/LbServicesProducer.java
index ae258445e88..d816c3215a7 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/model/LbServicesProducer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/model/LbServicesProducer.java
@@ -9,7 +9,10 @@ import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.flags.BooleanFlag;
+import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
+import com.yahoo.vespa.flags.Flags;
import java.util.Collections;
import java.util.Comparator;
@@ -32,10 +35,12 @@ public class LbServicesProducer implements LbServicesConfig.Producer {
private final Map<TenantName, Set<ApplicationInfo>> models;
private final Zone zone;
+ private final BooleanFlag usePowerOfTwoChoicesLb;
public LbServicesProducer(Map<TenantName, Set<ApplicationInfo>> models, Zone zone, FlagSource flagSource) {
this.models = models;
this.zone = zone;
+ usePowerOfTwoChoicesLb = Flags.USE_POWER_OF_TWO_CHOICES_LOAD_BALANCING.bindTo(flagSource);
}
@Override
@@ -67,6 +72,7 @@ public class LbServicesProducer implements LbServicesConfig.Producer {
private LbServicesConfig.Tenants.Applications.Builder getAppConfig(ApplicationInfo app) {
LbServicesConfig.Tenants.Applications.Builder ab = new LbServicesConfig.Tenants.Applications.Builder();
ab.activeRotation(getActiveRotation(app));
+ ab.usePowerOfTwoChoicesLb(usePowerOfTwoChoicesLb(app));
app.getModel().getHosts().stream()
.sorted((a, b) -> a.getHostname().compareTo(b.getHostname()))
.forEach(hostInfo -> ab.hosts(hostInfo.getHostname(), getHostsConfig(hostInfo)));
@@ -87,6 +93,10 @@ public class LbServicesProducer implements LbServicesConfig.Producer {
return activeRotation;
}
+ private boolean usePowerOfTwoChoicesLb(ApplicationInfo app) {
+ return usePowerOfTwoChoicesLb.with(FetchVector.Dimension.APPLICATION_ID, app.getApplicationId().serializedForm()).value();
+ }
+
private LbServicesConfig.Tenants.Applications.Hosts.Builder getHostsConfig(HostInfo hostInfo) {
LbServicesConfig.Tenants.Applications.Hosts.Builder hb = new LbServicesConfig.Tenants.Applications.Hosts.Builder();
hb.hostname(hostInfo.getHostname());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
index 5ddad540d8e..4e3fae37d63 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java
@@ -242,7 +242,8 @@ public class TenantRepository {
componentRegistry.getReloadListener(),
componentRegistry.getConfigserverConfig(),
componentRegistry.getHostRegistries().createApplicationHostRegistry(tenantName),
- new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName));
+ new TenantFileSystemDirs(componentRegistry.getConfigServerDB(), tenantName),
+ componentRegistry.getClock());
SessionRepository sessionRepository = new SessionRepository(tenantName,
componentRegistry,
applicationRepo,
diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java
index cc452421d2d..05d1119aa4f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java
+++ b/configserver/src/main/java/com/yahoo/vespa/serviceview/ConfigServerLocation.java
@@ -15,6 +15,7 @@ public class ConfigServerLocation extends AbstractComponent {
final int restApiPort;
// The client factory must be owned by a component as StateResource is instantiated per request
+ @SuppressWarnings("removal")
final VespaClientBuilderFactory clientBuilderFactory = new VespaClientBuilderFactory();
@Inject
diff --git a/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java b/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java
index 76e600d2ad8..138e6c8798c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java
+++ b/configserver/src/main/java/com/yahoo/vespa/serviceview/StateResource.java
@@ -40,6 +40,7 @@ public class StateResource implements StateClient {
private static final String USER_AGENT = "service-view-config-server-client";
private static final String SINGLE_API_LINK = "url";
+ @SuppressWarnings("removal")
private final VespaClientBuilderFactory clientBuilderFactory;
private final int restApiPort;
private final String host;
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java
index 6aeb774d2b0..8a3a3b6e0ca 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/ConfigConvergenceCheckerTest.java
@@ -2,15 +2,13 @@
package com.yahoo.vespa.config.server.application;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.yahoo.component.Version;
import com.yahoo.config.model.api.Model;
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.component.Version;
import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.slime.Slime;
-import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.config.server.ServerCache;
import com.yahoo.vespa.config.server.monitoring.MetricUpdater;
import org.junit.Before;
@@ -31,8 +29,9 @@ import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static com.yahoo.test.json.JsonTestHelper.assertJsonEquals;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
/**
* @author Ulf Lilleengen
@@ -184,10 +183,15 @@ public class ConfigConvergenceCheckerTest {
.withBody("response too slow")));
HttpResponse response = checker.getServiceConfigGenerationResponse(application, hostAndPort(service), requestUrl, Duration.ofMillis(1));
// Message contained in a SocketTimeoutException may differ across platforms, so we do a partial match of the response here
- assertResponse((responseBody) -> assertTrue("Response matches", responseBody.startsWith(
- "{\"url\":\"" + requestUrl.toString() + "\",\"host\":\"" + hostAndPort(requestUrl) +
- "\",\"wantedGeneration\":3,\"error\":\"java.net.SocketTimeoutException") &&
- responseBody.endsWith("\"}")), 404, response);
+ assertResponse(
+ responseBody ->
+ assertThat(responseBody)
+ .startsWith("{\"url\":\"" + requestUrl.toString() + "\",\"host\":\"" + hostAndPort(requestUrl) +
+ "\",\"wantedGeneration\":3,\"error\":\"")
+ .contains("java.net.SocketTimeoutException: 1 MILLISECONDS")
+ .endsWith("\"}"),
+ 404,
+ response);
}
private URI testServer() {
@@ -202,16 +206,8 @@ public class ConfigConvergenceCheckerTest {
return uri.getHost() + ":" + uri.getPort();
}
- private static void assertResponse(String json, int status, HttpResponse response) {
- assertResponse((responseBody) -> {
- Slime expected = SlimeUtils.jsonToSlime(json.getBytes());
- Slime actual = SlimeUtils.jsonToSlime(responseBody.getBytes());
- try {
- assertEquals(new String((SlimeUtils.toJsonBytes(expected))), new String(SlimeUtils.toJsonBytes(actual)));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }, status, response);
+ private static void assertResponse(String expectedJson, int status, HttpResponse response) {
+ assertResponse((responseBody) -> assertJsonEquals(new String(responseBody.getBytes()), expectedJson), status, response);
}
private static void assertResponse(Consumer<String> assertFunc, int status, HttpResponse response) {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java
index b5194432682..876b169742f 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsBuilder.java
@@ -16,7 +16,6 @@ import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
/**
* @author geirst
@@ -44,20 +43,16 @@ public class ConfigChangeActionsBuilder {
}
- ConfigChangeActionsBuilder refeed(String name, boolean allowed, String message, String documentType, String clusterName, String serviceName) {
- actions.add(new MockRefeedAction(name,
- allowed,
+ ConfigChangeActionsBuilder refeed(ValidationId validationId, String message, String documentType, String clusterName, String serviceName) {
+ actions.add(new MockRefeedAction(validationId,
message,
List.of(createService(clusterName, "myclustertype", "myservicetype", serviceName)), documentType));
return this;
}
- ConfigChangeActionsBuilder reindex(String name, boolean allowed, String message, String documentType, String clusterName, String serviceName) {
+ ConfigChangeActionsBuilder reindex(ValidationId validationId, String message, String documentType, String clusterName, String serviceName) {
List<ServiceInfo> services = List.of(createService(clusterName, "myclustertype", "myservicetype", serviceName));
- ValidationOverrides overrides = mock(ValidationOverrides.class);
- when(overrides.allows((String) any(), any())).thenReturn(allowed);
- when(overrides.allows((ValidationId) any(), any())).thenReturn(allowed);
- actions.add(VespaReindexAction.of(ClusterSpec.Id.from(clusterName), name, overrides, message, services, documentType, Instant.now()));
+ actions.add(VespaReindexAction.of(ClusterSpec.Id.from(clusterName), validationId, message, services, documentType));
return this;
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverterTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverterTest.java
index d145a796725..d75f95d4c48 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverterTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ConfigChangeActionsSlimeConverterTest.java
@@ -89,16 +89,15 @@ public class ConfigChangeActionsSlimeConverterTest {
@Test
public void json_representation_of_refeed_actions() throws IOException {
ConfigChangeActions actions = new ConfigChangeActionsBuilder().
- refeed(CHANGE_ID, true, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_TYPE).
- refeed(CHANGE_ID_2, false, CHANGE_MSG, DOC_TYPE_2, CLUSTER, SERVICE_TYPE).build();
+ refeed(CHANGE_ID, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_TYPE).
+ refeed(CHANGE_ID_2, CHANGE_MSG, DOC_TYPE_2, CLUSTER, SERVICE_TYPE).build();
assertEquals("{\n" +
" \"configChangeActions\": {\n" +
" \"restart\": [\n" +
" ],\n" +
" \"refeed\": [\n" +
" {\n" +
- " \"name\": \"change-id\",\n" +
- " \"allowed\": true,\n" +
+ " \"name\": \"field-type-change\",\n" +
" \"documentType\": \"music\",\n" +
" \"clusterName\": \"foo\",\n" +
" \"messages\": [\n" +
@@ -114,8 +113,7 @@ public class ConfigChangeActionsSlimeConverterTest {
" ]\n" +
" },\n" +
" {\n" +
- " \"name\": \"other-change-id\",\n" +
- " \"allowed\": false,\n" +
+ " \"name\": \"indexing-change\",\n" +
" \"documentType\": \"book\",\n" +
" \"clusterName\": \"foo\",\n" +
" \"messages\": [\n" +
@@ -141,7 +139,7 @@ public class ConfigChangeActionsSlimeConverterTest {
@Test
public void json_representation_of_reindex_actions() throws IOException {
ConfigChangeActions actions = new ConfigChangeActionsBuilder().
- reindex(CHANGE_ID, true, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_TYPE).build();
+ reindex(CHANGE_ID, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_TYPE).build();
assertEquals(
"{\n" +
" \"configChangeActions\": {\n" +
@@ -151,8 +149,7 @@ public class ConfigChangeActionsSlimeConverterTest {
" ],\n" +
" \"reindex\": [\n" +
" {\n" +
- " \"name\": \"change-id\",\n" +
- " \"allowed\": true,\n" +
+ " \"name\": \"field-type-change\",\n" +
" \"documentType\": \"music\",\n" +
" \"clusterName\": \"foo\",\n" +
" \"messages\": [\n" +
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockRefeedAction.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockRefeedAction.java
index 11f2a46994c..615d4c86c1d 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockRefeedAction.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/MockRefeedAction.java
@@ -1,32 +1,35 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.configchange;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ConfigChangeRefeedAction;
import com.yahoo.config.model.api.ServiceInfo;
+import com.yahoo.config.provision.ClusterSpec;
import java.util.List;
+import java.util.Optional;
/**
* @author geirst
*/
public class MockRefeedAction extends MockConfigChangeAction implements ConfigChangeRefeedAction {
- private final String name;
- private final boolean allowed;
+ private final ValidationId validationId;
private final String documentType;
- public MockRefeedAction(String name, boolean allowed, String message, List<ServiceInfo> services, String documentType) {
+ public MockRefeedAction(ValidationId validationId, String message, List<ServiceInfo> services, String documentType) {
super(message, services);
- this.name = name;
- this.allowed = allowed;
+ this.validationId = validationId;
this.documentType = documentType;
}
@Override
- public String name() { return name; }
+ public Optional<ValidationId> validationId() { return Optional.of(validationId); }
@Override
- public boolean allowed() { return allowed; }
+ public ClusterSpec.Id clusterId() {
+ return null;
+ }
@Override
public boolean ignoreForInternalRedeploy() {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatterTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatterTest.java
index 48d6833129e..4b898b501ec 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatterTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsFormatterTest.java
@@ -15,9 +15,9 @@ public class RefeedActionsFormatterTest {
@Test
public void formatting_of_single_action() {
RefeedActions actions = new ConfigChangeActionsBuilder().
- refeed(CHANGE_ID, false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(CHANGE_ID, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
build().getRefeedActions();
- assertEquals("change-id: Consider removing data and re-feed document type 'music' in cluster 'foo' because:\n" +
+ assertEquals("field-type-change: Consider removing data and re-feed document type 'music' in cluster 'foo' because:\n" +
" 1) change\n",
new RefeedActionsFormatter(actions).format());
}
@@ -25,20 +25,18 @@ public class RefeedActionsFormatterTest {
@Test
public void formatting_of_multiple_actions() {
RefeedActions actions = new ConfigChangeActionsBuilder().
- refeed(CHANGE_ID, false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed(CHANGE_ID, false, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed(CHANGE_ID_2, false, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed(CHANGE_ID_2, true, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed(CHANGE_ID, false, CHANGE_MSG_2, DOC_TYPE_2, CLUSTER, SERVICE_NAME).
+ refeed(CHANGE_ID, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(CHANGE_ID, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(CHANGE_ID_2, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(CHANGE_ID_2, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(CHANGE_ID, CHANGE_MSG_2, DOC_TYPE_2, CLUSTER, SERVICE_NAME).
build().getRefeedActions();
- assertEquals("change-id: Consider removing data and re-feed document type 'book' in cluster 'foo' because:\n" +
+ assertEquals("field-type-change: Consider removing data and re-feed document type 'book' in cluster 'foo' because:\n" +
" 1) other change\n" +
- "change-id: Consider removing data and re-feed document type 'music' in cluster 'foo' because:\n" +
+ "field-type-change: Consider removing data and re-feed document type 'music' in cluster 'foo' because:\n" +
" 1) change\n" +
" 2) other change\n" +
- "other-change-id: Consider removing data and re-feed document type 'music' in cluster 'foo' because:\n" +
- " 1) other change\n" +
- "(allowed) other-change-id: Consider removing data and re-feed document type 'music' in cluster 'foo' because:\n" +
+ "indexing-change: Consider removing data and re-feed document type 'music' in cluster 'foo' because:\n" +
" 1) other change\n",
new RefeedActionsFormatter(actions).format());
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java
index 7235b8905c5..24e81dc3f99 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/RefeedActionsTest.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.vespa.config.server.configchange;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ServiceInfo;
import org.junit.Test;
@@ -32,8 +33,8 @@ public class RefeedActionsTest {
@Test
public void action_with_multiple_reasons() {
List<RefeedActions.Entry> entries = new ConfigChangeActionsBuilder().
- refeed("change-id", false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed("change-id", false, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
build().getRefeedActions().getEntries();
assertThat(entries.size(), is(1));
assertThat(toString(entries.get(0)), equalTo("music.foo:[baz][change,other change]"));
@@ -42,8 +43,8 @@ public class RefeedActionsTest {
@Test
public void actions_with_multiple_services() {
List<RefeedActions.Entry> entries = new ConfigChangeActionsBuilder().
- refeed("change-id", false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed("change-id", false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME_2).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME_2).
build().getRefeedActions().getEntries();
assertThat(entries.size(), is(1));
assertThat(toString(entries.get(0)), equalTo("music.foo:[baz,qux][change]"));
@@ -52,8 +53,8 @@ public class RefeedActionsTest {
@Test
public void actions_with_multiple_document_types() {
List<RefeedActions.Entry> entries = new ConfigChangeActionsBuilder().
- refeed("change-id", false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed("change-id", false, CHANGE_MSG, DOC_TYPE_2, CLUSTER, SERVICE_NAME).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG, DOC_TYPE_2, CLUSTER, SERVICE_NAME).
build().getRefeedActions().getEntries();
assertThat(entries.size(), is(2));
assertThat(toString(entries.get(0)), equalTo("book.foo:[baz][change]"));
@@ -63,8 +64,8 @@ public class RefeedActionsTest {
@Test
public void actions_with_multiple_clusters() {
List<RefeedActions.Entry> entries = new ConfigChangeActionsBuilder().
- refeed("change-id", false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
- refeed("change-id", false, CHANGE_MSG, DOC_TYPE, CLUSTER_2, SERVICE_NAME).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ refeed(ValidationId.indexModeChange, CHANGE_MSG, DOC_TYPE, CLUSTER_2, SERVICE_NAME).
build().getRefeedActions().getEntries();
assertThat(entries.size(), is(2));
assertThat(toString(entries.get(0)), equalTo("music.bar:[baz][change]"));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatterTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatterTest.java
index e9dd3f3bbfc..b07d002a431 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatterTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/ReindexActionsFormatterTest.java
@@ -21,9 +21,9 @@ public class ReindexActionsFormatterTest {
@Test
public void formatting_of_single_action() {
ReindexActions actions = new ConfigChangeActionsBuilder().
- reindex(CHANGE_ID, false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ reindex(CHANGE_ID, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
build().getReindexActions();
- assertEquals("change-id: Consider re-indexing document type 'music' in cluster 'foo' because:\n" +
+ assertEquals("field-type-change: Consider re-indexing document type 'music' in cluster 'foo' because:\n" +
" 1) change\n",
new ReindexActionsFormatter(actions).format());
}
@@ -31,20 +31,18 @@ public class ReindexActionsFormatterTest {
@Test
public void formatting_of_multiple_actions() {
ReindexActions actions = new ConfigChangeActionsBuilder().
- reindex(CHANGE_ID, false, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
- reindex(CHANGE_ID, false, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
- reindex(CHANGE_ID_2, false, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
- reindex(CHANGE_ID_2, true, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
- reindex(CHANGE_ID, false, CHANGE_MSG_2, DOC_TYPE_2, CLUSTER, SERVICE_NAME).
+ reindex(CHANGE_ID, CHANGE_MSG, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ reindex(CHANGE_ID, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ reindex(CHANGE_ID_2, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ reindex(CHANGE_ID_2, CHANGE_MSG_2, DOC_TYPE, CLUSTER, SERVICE_NAME).
+ reindex(CHANGE_ID, CHANGE_MSG_2, DOC_TYPE_2, CLUSTER, SERVICE_NAME).
build().getReindexActions();
- assertEquals("change-id: Consider re-indexing document type 'book' in cluster 'foo' because:\n" +
+ assertEquals("field-type-change: Consider re-indexing document type 'book' in cluster 'foo' because:\n" +
" 1) other change\n" +
- "change-id: Consider re-indexing document type 'music' in cluster 'foo' because:\n" +
+ "field-type-change: Consider re-indexing document type 'music' in cluster 'foo' because:\n" +
" 1) change\n" +
" 2) other change\n" +
- "other-change-id: Consider re-indexing document type 'music' in cluster 'foo' because:\n" +
- " 1) other change\n" +
- "(allowed) other-change-id: Consider re-indexing document type 'music' in cluster 'foo' because:\n" +
+ "indexing-change: Consider re-indexing document type 'music' in cluster 'foo' because:\n" +
" 1) other change\n",
new ReindexActionsFormatter(actions).format());
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/Utils.java b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/Utils.java
index 8499c12f648..e02e1e2b143 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/Utils.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/configchange/Utils.java
@@ -1,14 +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.config.server.configchange;
+import com.yahoo.config.application.api.ValidationId;
+
/**
* @author geirst
* @since 5.44
*/
public class Utils {
- final static String CHANGE_ID = "change-id";
- final static String CHANGE_ID_2 = "other-change-id";
+ final static ValidationId CHANGE_ID = ValidationId.fieldTypeChange;
+ final static ValidationId CHANGE_ID_2 = ValidationId.indexingChange;
final static String CHANGE_MSG = "change";
final static String CHANGE_MSG_2 = "other change";
final static String DOC_TYPE = "music";
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
index 4d1b9341e7f..341fa7109da 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.config.server.deploy;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.Version;
-import com.yahoo.config.application.api.ValidationOverrides;
+import com.yahoo.config.application.api.ValidationId;
import com.yahoo.config.model.api.ConfigChangeAction;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
@@ -49,6 +49,7 @@ import static com.yahoo.vespa.config.server.deploy.DeployTester.createFailingMod
import static com.yahoo.vespa.config.server.deploy.DeployTester.createHostedModelFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -390,27 +391,6 @@ public class HostedDeployTest {
}
@Test
- public void testThatDisallowedConfigChangeActionsBlockDeployment() throws IOException {
- List<Host> hosts = List.of(createHost("host1", "6.1.0"),
- createHost("host2", "6.1.0"),
- createHost("host3", "6.1.0"),
- createHost("host4", "6.1.0"));
- List<ServiceInfo> services = List.of(
- new ServiceInfo("serviceName", "serviceType", null, Map.of("clustername", "cluster"), "configId", "hostName"));
-
- ManualClock clock = new ManualClock(Instant.EPOCH);
- List<ModelFactory> modelFactories = List.of(
- new ConfigChangeActionsModelFactory(Version.fromString("6.1.0"),
- VespaReindexAction.of(ClusterSpec.Id.from("test"), "indexing-mode-change", ValidationOverrides.empty,
- "reindex please", services, "music", clock.instant()),
- new VespaRestartAction(ClusterSpec.Id.from("test"), "change", services)));
-
- DeployTester tester = createTester(hosts, modelFactories, prodZone, clock);
- PrepareResult prepareResult = tester.deployApp("src/test/apps/hosted/", "6.1.0");
- assertNull("Deployment was not activated", tester.applicationRepository().getActiveSession(tester.applicationId()));
- }
-
- @Test
public void testThatAllowedConfigChangeActionsAreActedUpon() throws IOException {
List<Host> hosts = List.of(createHost("host1", "6.1.0"),
createHost("host2", "6.1.0"),
@@ -422,8 +402,8 @@ public class HostedDeployTest {
ManualClock clock = new ManualClock(Instant.EPOCH);
List<ModelFactory> modelFactories = List.of(
new ConfigChangeActionsModelFactory(Version.fromString("6.1.0"),
- VespaReindexAction.of(ClusterSpec.Id.from("test"), "indexing-mode-change", ValidationOverrides.all,
- "reindex please", services, "music", clock.instant()),
+ VespaReindexAction.of(ClusterSpec.Id.from("test"), ValidationId.indexModeChange,
+ "reindex please", services, "music"),
new VespaRestartAction(ClusterSpec.Id.from("test"), "change", services)));
DeployTester tester = createTester(hosts, modelFactories, prodZone, clock);
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
index a34de472d1e..910c4b069e3 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java
@@ -1,7 +1,6 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.http.v2;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.Version;
import com.yahoo.config.model.api.ModelFactory;
@@ -15,7 +14,6 @@ import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.test.ManualClock;
-import com.yahoo.test.json.JsonTestHelper;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.MockLogRetriever;
import com.yahoo.vespa.config.server.MockProvisioner;
@@ -23,7 +21,8 @@ import com.yahoo.vespa.config.server.MockTesterClient;
import com.yahoo.vespa.config.server.TestComponentRegistry;
import com.yahoo.vespa.config.server.application.ApplicationCuratorDatabase;
import com.yahoo.vespa.config.server.application.ApplicationReindexing;
-import com.yahoo.vespa.config.server.application.ConfigConvergenceChecker;
+import com.yahoo.vespa.config.server.application.ClusterReindexing;
+import com.yahoo.vespa.config.server.application.ClusterReindexing.Status;
import com.yahoo.vespa.config.server.application.HttpProxy;
import com.yahoo.vespa.config.server.application.OrchestratorMock;
import com.yahoo.vespa.config.server.deploy.DeployTester;
@@ -31,6 +30,7 @@ import com.yahoo.vespa.config.server.http.HandlerTest;
import com.yahoo.vespa.config.server.http.HttpErrorResponse;
import com.yahoo.vespa.config.server.http.SessionHandlerTest;
import com.yahoo.vespa.config.server.http.StaticResponse;
+import com.yahoo.vespa.config.server.http.v2.ApplicationHandler.ReindexingResponse;
import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
import com.yahoo.vespa.config.server.session.PrepareParams;
@@ -42,16 +42,17 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import javax.ws.rs.client.Client;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
+import java.time.Instant;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
import static com.yahoo.config.model.api.container.ContainerServiceType.CLUSTERCONTROLLER_CONTAINER;
import static com.yahoo.container.jdisc.HttpRequest.createTestRequest;
@@ -437,6 +438,80 @@ public class ApplicationHandlerTest {
assertEquals("report", getRenderedString(response));
}
+ @Test
+ public void testClusterReindexingStateSerialization() {
+ Stream.of(ClusterReindexing.State.values()).forEach(ReindexingResponse::toString);
+ }
+
+ @Test
+ public void testReindexingSerialization() throws IOException {
+ Instant now = Instant.ofEpochMilli(123456);
+ ApplicationReindexing applicationReindexing = ApplicationReindexing.ready(now.minusSeconds(10))
+ .withPending("foo", "bar", 123L)
+ .withReady("moo", now.minusSeconds(1))
+ .withReady("moo", "baz", now);
+ ClusterReindexing clusterReindexing = new ClusterReindexing(Map.of("bax", new Status(now, null, null, null, null),
+ "baz", new Status(now.plusSeconds(1),
+ now.plusSeconds(2),
+ ClusterReindexing.State.FAILED,
+ "message",
+ "some")));
+ assertJsonEquals(getRenderedString(new ReindexingResponse(applicationReindexing,
+ Map.of("boo", clusterReindexing,
+ "moo", clusterReindexing))),
+ "{\n" +
+ " \"enabled\": true,\n" +
+ " \"status\": {\n" +
+ " \"readyMillis\": 113456\n" +
+ " },\n" +
+ " \"clusters\": {\n" +
+ " \"boo\": {\n" +
+ " \"pending\": {},\n" +
+ " \"ready\": {\n" +
+ " \"bax\": {\n" +
+ " \"startedMillis\": 123456\n" +
+ " },\n" +
+ " \"baz\": {\n" +
+ " \"startedMillis\": 124456,\n" +
+ " \"endedMillis\": 125456,\n" +
+ " \"state\": \"failed\",\n" +
+ " \"message\": \"message\",\n" +
+ " \"progress\": \"some\"\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"foo\": {\n" +
+ " \"pending\": {\n" +
+ " \"bar\": 123\n" +
+ " },\n" +
+ " \"ready\": {},\n" +
+ " \"status\": {\n" +
+ " \"readyMillis\": 113456\n" +
+ " }\n" +
+ " },\n" +
+ " \"moo\": {\n" +
+ " \"pending\": {},\n" +
+ " \"ready\": {\n" +
+ " \"baz\": {\n" +
+ " \"readyMillis\": 123456,\n" +
+ " \"startedMillis\": 124456,\n" +
+ " \"endedMillis\": 125456,\n" +
+ " \"state\": \"failed\",\n" +
+ " \"message\": \"message\",\n" +
+ " \"progress\": \"some\"\n" +
+ " },\n" +
+ " \"bax\": {\n" +
+ " \"startedMillis\": 123456\n" +
+ " }\n" +
+ " },\n" +
+ " \"status\": {\n" +
+ " \"readyMillis\": 122456\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}\n");
+ }
+
private void assertNotAllowed(Method method) throws IOException {
String url = "http://myhost:14000/application/v2/tenant/" + mytenantName + "/application/default";
deleteAndAssertResponse(url, Response.Status.METHOD_NOT_ALLOWED, HttpErrorResponse.errorCodes.METHOD_NOT_ALLOWED, "{\"error-code\":\"METHOD_NOT_ALLOWED\",\"message\":\"Method '" + method + "' is not supported\"}",
@@ -558,21 +633,6 @@ public class ApplicationHandlerTest {
return createApplicationHandler().handle(createTestRequest(restartUrl, GET));
}
- private static class MockStateApiFactory implements ConfigConvergenceChecker.StateApiFactory {
- boolean createdApi = false;
- @Override
- public ConfigConvergenceChecker.StateApi createStateApi(Client client, URI serviceUri) {
- createdApi = true;
- return () -> {
- try {
- return new ObjectMapper().readTree("{\"config\":{\"generation\":1}}");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- };
- }
- }
-
private ApplicationHandler createApplicationHandler() {
return createApplicationHandler(applicationRepository);
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java
index 325db1feba6..6729be20305 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/model/LbServicesProducerTest.java
@@ -16,6 +16,7 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.config.ConfigPayload;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.model.VespaModel;
import org.junit.Test;
@@ -108,6 +109,17 @@ public class LbServicesProducerTest {
}
}
+ @Test
+ public void use_power_of_two_lb_is_configured_from_feature_flag() throws IOException, SAXException {
+ RegionName regionName = RegionName.from("us-east-1");
+
+ LbServicesConfig conf = createModelAndGetLbServicesConfig(regionName);
+ assertFalse(conf.tenants("foo").applications("foo:prod:" + regionName.value() + ":default").usePowerOfTwoChoicesLb());
+
+ flagSource.withBooleanFlag(Flags.USE_POWER_OF_TWO_CHOICES_LOAD_BALANCING.id(), true);
+ conf = createModelAndGetLbServicesConfig(regionName);
+ assertTrue(conf.tenants("foo").applications("foo:prod:" + regionName.value() + ":default").usePowerOfTwoChoicesLb());
+ }
private LbServicesConfig createModelAndGetLbServicesConfig(RegionName regionName) throws IOException, SAXException {
Zone zone = new Zone(Environment.prod, regionName);
Map<TenantName, Set<ApplicationInfo>> testModel = createTestModel(new DeployState.Builder()
diff --git a/container-core/src/main/java/com/yahoo/restapi/Path.java b/container-core/src/main/java/com/yahoo/restapi/Path.java
index fe65245fd15..23a791e7532 100644
--- a/container-core/src/main/java/com/yahoo/restapi/Path.java
+++ b/container-core/src/main/java/com/yahoo/restapi/Path.java
@@ -42,24 +42,6 @@ public class Path {
private final Map<String, String> values = new HashMap<>();
private String rest = "";
- /**
- * @deprecated use {@link #Path(URI)} for correct handling of URL encoded paths.
- */
- @Deprecated
- public Path(String path) {
- this(path, "");
- }
-
- /**
- * @deprecated use {@link #Path(URI, String)} for correct handling of URL encoded paths.
- */
- @Deprecated
- public Path(String path, String optionalPrefix) {
- this.optionalPrefix = optionalPrefix;
- this.pathString = path;
- this.elements = path.split("/");
- }
-
public Path(URI uri) {
this(uri, "");
}
diff --git a/container-dependency-versions/pom.xml b/container-dependency-versions/pom.xml
index b239518303d..8691d9a7ffb 100644
--- a/container-dependency-versions/pom.xml
+++ b/container-dependency-versions/pom.xml
@@ -446,7 +446,7 @@
<javax.inject.version>1</javax.inject.version>
<javax.servlet-api.version>3.1.0</javax.servlet-api.version>
<jaxb.version>2.3.0</jaxb.version>
- <jetty.version>9.4.32.v20200930</jetty.version>
+ <jetty.version>9.4.35.v20201120</jetty.version>
<org.lz4.version>1.7.1</org.lz4.version>
<org.json.version>20090211</org.json.version>
<slf4j.version>1.7.5</slf4j.version>
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
index 4614f8f9857..3158c06b0b1 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
@@ -261,12 +261,16 @@ public final class ConfiguredApplication implements Application {
private void startReconfigurerThread() {
reconfigurerThread = new Thread(() -> {
+ boolean restartOnDeploy = false;
while ( ! Thread.interrupted()) {
try {
ContainerBuilder builder = createBuilderWithGuiceBindings();
+ // Restart on deploy is sticky: Once it is set no future generation should be applied until restart
+ restartOnDeploy = restartOnDeploy || qrConfig.restartOnDeploy();
+
// Block until new config arrives, and it should be applied
- configurer.getNewComponentGraph(builder.guiceModules().activate(), qrConfig.restartOnDeploy());
+ configurer.getNewComponentGraph(builder.guiceModules().activate(), restartOnDeploy);
initializeAndActivateContainer(builder);
} catch (ConfigInterruptedException e) {
break;
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json
index 19fb1862262..cd3f011352d 100644
--- a/container-search/abi-spec.json
+++ b/container-search/abi-spec.json
@@ -6089,6 +6089,7 @@
"public final java.lang.Object get(java.lang.String, java.util.Map)",
"public final java.lang.Object get(java.lang.String, java.util.Map, com.yahoo.processing.request.Properties)",
"public final java.lang.Object get(com.yahoo.processing.request.CompoundName, java.util.Map, com.yahoo.processing.request.Properties)",
+ "public final com.yahoo.search.query.profile.compiled.DimensionalMap getEntries()",
"public com.yahoo.search.query.profile.compiled.CompiledQueryProfile clone()",
"public java.lang.String toString()",
"public bridge synthetic com.yahoo.component.AbstractComponent clone()",
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 4995927f7a2..ce31b9a3ba3 100644
--- a/container-search/src/main/java/com/yahoo/search/Query.java
+++ b/container-search/src/main/java/com/yahoo/search/Query.java
@@ -181,7 +181,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
//---------------- Tracing ----------------------------------------------------
- private static Logger log = Logger.getLogger(Query.class.getName());
+ private static final Logger log = Logger.getLogger(Query.class.getName());
/** The time this query was created */
private long startTime;
@@ -200,7 +200,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
public static final CompoundName TIMEOUT = new CompoundName("timeout");
- private static QueryProfileType argumentType;
+ private static final QueryProfileType argumentType;
static {
argumentType = new QueryProfileType("native");
argumentType.setBuiltin(true);
@@ -226,7 +226,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
public static QueryProfileType getArgumentType() { return argumentType; }
/** The aliases of query properties */
- private static Map<String, CompoundName> propertyAliases;
+ private static final Map<String, CompoundName> propertyAliases;
static {
Map<String,CompoundName> propertyAliasesBuilder = new HashMap<>();
addAliases(Query.getArgumentType(), propertyAliasesBuilder);
@@ -316,7 +316,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
* Creates a query from a request
*
* @param request the HTTP request from which this is created
- * @param queryProfile the query profile to use for this query, or null if none.
+ * @param queryProfile the query profile to use for this query, or null if none
*/
public Query(HttpRequest request, CompiledQueryProfile queryProfile) {
this(request, request.propertyMap(), queryProfile);
@@ -325,9 +325,9 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
/**
* Creates a query from a request
*
- * @param request the HTTP request from which this is created.
- * @param requestMap the property map of the query.
- * @param queryProfile the query profile to use for this query, or null if none.
+ * @param request the HTTP request from which this is created
+ * @param requestMap the property map of the query
+ * @param queryProfile the query profile to use for this query, or null if none
*/
public Query(HttpRequest request, Map<String, String> requestMap, CompiledQueryProfile queryProfile) {
super(new QueryPropertyAliases(propertyAliases));
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java
index c6b0f4a533b..2439908183c 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java
@@ -183,6 +183,11 @@ public class CompiledQueryProfile extends AbstractComponent implements Cloneable
return substitute(value.value(), context, substitution);
}
+ /** Returns all the entries from the profile **/
+ public final DimensionalMap<ValueWithSource> getEntries() {
+ return this.entries;
+ }
+
private Object substitute(Object value, Map<String, String> context, Properties substitution) {
if (value == null) return value;
if (substitution == null) return value;
diff --git a/container/pom.xml b/container/pom.xml
index cf3aa21513b..4f045aac90e 100644
--- a/container/pom.xml
+++ b/container/pom.xml
@@ -45,6 +45,10 @@
<groupId>io.airlift</groupId>
<artifactId>airline</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ </exclusion>
</exclusions>
</dependency>
</dependencies>
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/RefeedAction.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/RefeedAction.java
index faa2c39ee65..799bc814abe 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/RefeedAction.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/RefeedAction.java
@@ -14,7 +14,6 @@ import java.util.List;
public class RefeedAction {
public final String name;
- public final boolean allowed;
public final String documentType;
public final String clusterName;
public final List<ServiceInfo> services;
@@ -22,13 +21,11 @@ public class RefeedAction {
@JsonCreator
public RefeedAction(@JsonProperty("name") String name,
- @JsonProperty("allowed") boolean allowed,
@JsonProperty("documentType") String documentType,
@JsonProperty("clusterName") String clusterName,
@JsonProperty("services") List<ServiceInfo> services,
@JsonProperty("messages") List<String> messages) {
this.name = name;
- this.allowed = allowed;
this.documentType = documentType;
this.clusterName = clusterName;
this.services = services;
@@ -39,7 +36,6 @@ public class RefeedAction {
public String toString() {
return "RefeedAction{" +
"name='" + name + '\'' +
- ", allowed=" + allowed +
", documentType='" + documentType + '\'' +
", clusterName='" + clusterName + '\'' +
", services=" + services +
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/ReindexAction.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/ReindexAction.java
index c5735fbd4a6..c2b28a94c66 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/ReindexAction.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/configserverbindings/ReindexAction.java
@@ -14,7 +14,6 @@ import java.util.List;
public class ReindexAction {
public final String name;
- public final boolean allowed;
public final String documentType;
public final String clusterName;
public final List<ServiceInfo> services;
@@ -22,13 +21,11 @@ public class ReindexAction {
@JsonCreator
public ReindexAction(@JsonProperty("name") String name,
- @JsonProperty("allowed") boolean allowed,
@JsonProperty("documentType") String documentType,
@JsonProperty("clusterName") String clusterName,
@JsonProperty("services") List<ServiceInfo> services,
@JsonProperty("messages") List<String> messages) {
this.name = name;
- this.allowed = allowed;
this.documentType = documentType;
this.clusterName = clusterName;
this.services = services;
@@ -39,7 +36,6 @@ public class ReindexAction {
public String toString() {
return "ReindexAction{" +
"name='" + name + '\'' +
- ", allowed=" + allowed +
", documentType='" + documentType + '\'' +
", clusterName='" + clusterName + '\'' +
", services=" + services +
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ApplicationReindexing.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ApplicationReindexing.java
index 8d002640156..b6c7899aef1 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ApplicationReindexing.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ApplicationReindexing.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.api.integration.configserver;
import java.time.Instant;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import static java.util.Objects.requireNonNull;
@@ -115,31 +116,69 @@ public class ApplicationReindexing {
public static class Status {
private final Instant readyAt;
+ private final Instant startedAt;
+ private final Instant endedAt;
+ private final State state;
+ private final String message;
+ private final String progress;
+
+ public Status(Instant readyAt, Instant startedAt, Instant endedAt, State state, String message, String progress) {
+ this.readyAt = readyAt;
+ this.startedAt = startedAt;
+ this.endedAt = endedAt;
+ this.state = state;
+ this.message = message;
+ this.progress = progress;
+ }
public Status(Instant readyAt) {
- this.readyAt = requireNonNull(readyAt);
+ this(readyAt, null, null, null, null, null);
}
- public Instant readyAt() { return readyAt; }
+ public Optional<Instant> readyAt() { return Optional.ofNullable(readyAt); }
+ public Optional<Instant> startedAt() { return Optional.ofNullable(startedAt); }
+ public Optional<Instant> endedAt() { return Optional.ofNullable(endedAt); }
+ public Optional<State> state() { return Optional.ofNullable(state); }
+ public Optional<String> message() { return Optional.ofNullable(message); }
+ public Optional<String> progress() { return Optional.ofNullable(progress); }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Status status = (Status) o;
- return readyAt.equals(status.readyAt);
+ return Objects.equals(readyAt, status.readyAt) &&
+ Objects.equals(startedAt, status.startedAt) &&
+ Objects.equals(endedAt, status.endedAt) &&
+ state == status.state &&
+ Objects.equals(message, status.message) &&
+ Objects.equals(progress, status.progress);
}
@Override
public int hashCode() {
- return Objects.hash(readyAt);
+ return Objects.hash(readyAt, startedAt, endedAt, state, message, progress);
}
@Override
public String toString() {
- return "ready at " + readyAt;
+ return "Status{" +
+ "readyAt=" + readyAt +
+ ", startedAt=" + startedAt +
+ ", endedAt=" + endedAt +
+ ", state=" + state +
+ ", message='" + message + '\'' +
+ ", progress='" + progress + '\'' +
+ '}';
}
}
+
+ public enum State {
+
+ PENDING, RUNNING, FAILED, SUCCESSFUL;
+
+ }
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java
index fd339e3bb43..98b7ffd1d47 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java
@@ -4,6 +4,8 @@ package com.yahoo.vespa.hosted.controller.api.integration.configserver;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
+import java.time.Instant;
+import java.util.List;
import java.util.Optional;
/**
@@ -17,19 +19,25 @@ public class Cluster {
private final ClusterResources current;
private final Optional<ClusterResources> target;
private final Optional<ClusterResources> suggested;
+ private final List<ScalingEvent> scalingEvents;
+ private final String autoscalingStatus;
public Cluster(ClusterSpec.Id id,
ClusterResources min,
ClusterResources max,
ClusterResources current,
Optional<ClusterResources> target,
- Optional<ClusterResources> suggested) {
+ Optional<ClusterResources> suggested,
+ List<ScalingEvent> scalingEvents,
+ String autoscalingStatus) {
this.id = id;
this.min = min;
this.max = max;
this.current = current;
this.target = target;
this.suggested = suggested;
+ this.scalingEvents = scalingEvents;
+ this.autoscalingStatus = autoscalingStatus;
}
public ClusterSpec.Id id() { return id; }
@@ -38,10 +46,29 @@ public class Cluster {
public ClusterResources current() { return current; }
public Optional<ClusterResources> target() { return target; }
public Optional<ClusterResources> suggested() { return suggested; }
+ public List<ScalingEvent> scalingEvents() { return scalingEvents; }
+ public String autoscalingStatus() { return autoscalingStatus; }
@Override
public String toString() {
return "cluster '" + id + "'";
}
+ public static class ScalingEvent {
+
+ private final ClusterResources from, to;
+ private final Instant at;
+
+ public ScalingEvent(ClusterResources from, ClusterResources to, Instant at) {
+ this.from = from;
+ this.to = to;
+ this.at = at;
+ }
+
+ public ClusterResources from() { return from; }
+ public ClusterResources to() { return to; }
+ public Instant at() { return at; }
+
+ }
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
index 8fd294f64f8..7d85c11789b 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Node.java
@@ -14,10 +14,12 @@ import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeHist
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
/**
* A node in hosted Vespa.
@@ -57,6 +59,8 @@ public class Node {
private final Optional<ApplicationId> exclusiveTo;
private final Map<String, JsonNode> reports;
private final List<NodeHistory> history;
+ private final Set<String> additionalIpAddresses;
+ private final String openStackId;
public Node(HostName hostname, Optional<HostName> parentHostname, State state, NodeType type, NodeResources resources, Optional<ApplicationId> owner,
Version currentVersion, Version wantedVersion, Version currentOsVersion, Version wantedOsVersion,
@@ -64,7 +68,8 @@ public class Node {
Optional<Instant> suspendedSince, long restartGeneration, long wantedRestartGeneration, long rebootGeneration, long wantedRebootGeneration,
int cost, String flavor, String clusterId, ClusterType clusterType, boolean wantToRetire, boolean wantToDeprovision,
Optional<TenantName> reservedTo, Optional<ApplicationId> exclusiveTo,
- DockerImage wantedDockerImage, DockerImage currentDockerImage, Map<String, JsonNode> reports, List<NodeHistory> history) {
+ DockerImage wantedDockerImage, DockerImage currentDockerImage, Map<String, JsonNode> reports, List<NodeHistory> history,
+ Set<String> additionalIpAddresses, String openStackId) {
this.hostname = hostname;
this.parentHostname = parentHostname;
this.state = state;
@@ -95,6 +100,8 @@ public class Node {
this.currentDockerImage = currentDockerImage;
this.reports = reports;
this.history = history;
+ this.openStackId = openStackId;
+ this.additionalIpAddresses = additionalIpAddresses;
}
public HostName hostname() {
@@ -211,6 +218,14 @@ public class Node {
return history;
}
+ public Set<String> additionalIpAddresses() {
+ return additionalIpAddresses;
+ }
+
+ public String openStackId() {
+ return openStackId;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -285,6 +300,8 @@ public class Node {
private Optional<ApplicationId> exclusiveTo = Optional.empty();
private Map<String, JsonNode> reports = new HashMap<>();
private List<NodeHistory> history = new ArrayList<>();
+ private Set<String> additionalIpAddresses = new HashSet<>();
+ private String openStackId;
public Builder() { }
@@ -319,6 +336,8 @@ public class Node {
this.exclusiveTo = node.exclusiveTo;
this.reports = node.reports;
this.history = node.history;
+ this.additionalIpAddresses = node.additionalIpAddresses;
+ this.openStackId = node.openStackId;
}
public Builder hostname(HostName hostname) {
@@ -466,12 +485,22 @@ public class Node {
return this;
}
+ public Builder additionalIpAddresses(Set<String> additionalIpAddresses) {
+ this.additionalIpAddresses = additionalIpAddresses;
+ return this;
+ }
+
+ public Builder openStackId(String openStackId) {
+ this.openStackId = openStackId;
+ return this;
+ }
+
public Node build() {
return new Node(hostname, parentHostname, state, type, resources, owner, currentVersion, wantedVersion,
currentOsVersion, wantedOsVersion, currentFirmwareCheck, wantedFirmwareCheck, serviceState,
suspendedSince, restartGeneration, wantedRestartGeneration, rebootGeneration, wantedRebootGeneration,
cost, flavor, clusterId, clusterType, wantToRetire, wantToDeprovision, reservedTo, exclusiveTo,
- wantedDockerImage, currentDockerImage, reports, history);
+ wantedDockerImage, currentDockerImage, reports, history, additionalIpAddresses, openStackId);
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
index ca8af48e4fd..af1b3fa53fc 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
@@ -135,7 +135,9 @@ public interface NodeRepository {
dockerImageFrom(node.getWantedDockerImage()),
dockerImageFrom(node.getCurrentDockerImage()),
node.getReports(),
- node.getHistory());
+ node.getHistory(),
+ node.getAdditionalIpAddresses(),
+ node.getOpenStackId());
}
private static String clusterIdOf(NodeMembership nodeMembership) {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/PrepareResponse.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/PrepareResponse.java
index 6054e05149b..5c946538625 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/PrepareResponse.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/PrepareResponse.java
@@ -15,7 +15,6 @@ import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
public class PrepareResponse {
public TenantId tenant;
- @JsonProperty("activate") public URI activationUri;
public String message;
public List<Log> log;
public ConfigChangeActions configChangeActions;
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
index 700be6d263a..efdeff8fc16 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/JobType.java
@@ -144,6 +144,9 @@ public enum JobType {
Map.of(Public, ZoneId.from("dev", "aws-us-east-1c"),
PublicCd, ZoneId.from("dev", "aws-us-east-1c"))),
+ perfAwsUsEast1c ("perf-aws-us-east-1c",
+ Map.of(Public, ZoneId.from("perf", "aws-us-east-1c"))),
+
perfUsEast3 ("perf-us-east-3",
Map.of(main, ZoneId.from("perf" , "us-east-3")));
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java
index 298928a881d..99a72fbc827 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java
@@ -7,7 +7,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Cluster;
+import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
/**
* @author bratseth
@@ -26,6 +28,10 @@ public class ClusterData {
public ClusterResourcesData suggested;
@JsonProperty("target")
public ClusterResourcesData target;
+ @JsonProperty("scalingEvents")
+ public List<ScalingEventData> scalingEvents;
+ @JsonProperty("autoscalingStatus")
+ public String autoscalingStatus;
public Cluster toCluster(String id) {
return new Cluster(ClusterSpec.Id.from(id),
@@ -33,7 +39,10 @@ public class ClusterData {
max.toClusterResources(),
current.toClusterResources(),
target == null ? Optional.empty() : Optional.of(target.toClusterResources()),
- suggested == null ? Optional.empty() : Optional.of(suggested.toClusterResources()));
+ suggested == null ? Optional.empty() : Optional.of(suggested.toClusterResources()),
+ scalingEvents == null ? List.of()
+ : scalingEvents.stream().map(data -> data.toScalingEvent()).collect(Collectors.toList()),
+ autoscalingStatus);
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java
index 97e7f2e897a..393814478dd 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeHistory.java
@@ -13,6 +13,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class NodeHistory {
+
@JsonProperty("at")
public Long at;
@JsonProperty("agent")
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java
index 7bb47185751..65d6f2a5fa6 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryNode.java
@@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -29,6 +28,8 @@ public class NodeRepositoryNode {
private Set<String> ipAddresses;
@JsonProperty("additionalIpAddresses")
private Set<String> additionalIpAddresses;
+ @JsonProperty("additionalHostnames")
+ private List<String> additionalHostnames;
@JsonProperty("openStackId")
private String openStackId;
@JsonProperty("flavor")
@@ -142,6 +143,14 @@ public class NodeRepositoryNode {
this.additionalIpAddresses = additionalIpAddresses;
}
+ public List<String> getAdditionalHostnames() {
+ return additionalHostnames;
+ }
+
+ public void setAdditionalHostnames(List<String> additionalHostnames) {
+ this.additionalHostnames = additionalHostnames;
+ }
+
public String getOpenStackId() {
return openStackId;
}
@@ -397,6 +406,7 @@ public class NodeRepositoryNode {
", hostname='" + hostname + '\'' +
", ipAddresses=" + ipAddresses +
", additionalIpAddresses=" + additionalIpAddresses +
+ ", additionalHostnames=" + additionalHostnames +
", openStackId='" + openStackId + '\'' +
", flavor='" + flavor + '\'' +
", resources=" + resources +
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ScalingEventData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ScalingEventData.java
new file mode 100644
index 00000000000..b33a7436522
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ScalingEventData.java
@@ -0,0 +1,31 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.noderepository;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Cluster;
+
+import java.time.Instant;
+
+/**
+ * @author bratseth
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ScalingEventData {
+
+ @JsonProperty("from")
+ public ClusterResourcesData from;
+
+ @JsonProperty("to")
+ public ClusterResourcesData to;
+
+ @JsonProperty("at")
+ public Long at;
+
+ public Cluster.ScalingEvent toScalingEvent() {
+ return new Cluster.ScalingEvent(from.toClusterResources(), to.toClusterResources(), Instant.ofEpochMilli(at));
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java
index c3126cc8b7a..85db447dfbd 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java
@@ -92,7 +92,8 @@ public enum RoleDefinition {
paymentProcessor(Policy.paymentProcessor),
hostedAccountant(Policy.hostedAccountant,
- Policy.collectionMethodUpdate);
+ Policy.collectionMethodUpdate,
+ Policy.planUpdate);
private final Set<RoleDefinition> parents;
private final Set<Policy> policies;
diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java
index 10d4732984c..1a24b5361dd 100644
--- a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java
+++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java
@@ -172,7 +172,7 @@ public class RoleTest {
}
@Test
- public void billing() {
+ public void billing_tenant() {
URI billing = URI.create("/billing/v1/tenant/t1/billing");
Role user = Role.reader(TenantName.from("t1"));
@@ -188,4 +188,129 @@ public class RoleTest {
}
+ @Test
+ public void billing_test() {
+ var tester = new EnforcerTester(publicCdEnforcer);
+
+ var accountant = Role.hostedAccountant();
+ var operator = Role.hostedOperator();
+ var reader = Role.reader(TenantName.from("t1"));
+ var developer = Role.developer(TenantName.from("t1"));
+ var admin = Role.administrator(TenantName.from("t1"));
+ var otherAdmin = Role.administrator(TenantName.from("t2"));
+
+ tester.on("/billing/v1/tenant/t1/token")
+ .assertAction(accountant)
+ .assertAction(operator, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(reader)
+ .assertAction(developer)
+ .assertAction(admin, Action.read)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/tenant/t1/instrument")
+ .assertAction(accountant)
+ .assertAction(operator, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(reader, Action.read, Action.delete)
+ .assertAction(developer, Action.read, Action.delete)
+ .assertAction(admin, Action.read, Action.update, Action.delete)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/tenant/t1/instrument/i1")
+ .assertAction(accountant)
+ .assertAction(operator, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(reader, Action.read, Action.delete)
+ .assertAction(developer, Action.read, Action.delete)
+ .assertAction(admin, Action.read, Action.update, Action.delete)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/tenant/t1/billing")
+ .assertAction(accountant)
+ .assertAction(operator, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(reader, Action.read)
+ .assertAction(developer, Action.read)
+ .assertAction(admin, Action.read)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/tenant/t1/plan")
+ .assertAction(accountant, Action.update)
+ .assertAction(operator, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(reader)
+ .assertAction(developer)
+ .assertAction(admin, Action.update)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/tenant/t1/collection")
+ .assertAction(accountant, Action.update)
+ .assertAction(operator, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(reader)
+ .assertAction(developer)
+ .assertAction(admin)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/billing")
+ .assertAction(accountant, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(operator, Action.read)
+ .assertAction(reader)
+ .assertAction(developer)
+ .assertAction(admin)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/invoice/tenant/t1/line-item")
+ .assertAction(accountant, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(operator, Action.read)
+ .assertAction(reader)
+ .assertAction(developer)
+ .assertAction(admin)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/invoice")
+ .assertAction(accountant, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(operator, Action.read)
+ .assertAction(reader)
+ .assertAction(developer)
+ .assertAction(admin)
+ .assertAction(otherAdmin);
+
+ tester.on("/billing/v1/invoice/i1/status")
+ .assertAction(accountant, Action.create, Action.read, Action.update, Action.delete)
+ .assertAction(operator, Action.read)
+ .assertAction(reader)
+ .assertAction(developer)
+ .assertAction(admin)
+ .assertAction(otherAdmin);
+ }
+
+ private static class EnforcerTester {
+ private final Enforcer enforcer;
+ private final URI resource;
+
+ EnforcerTester(Enforcer enforcer) {
+ this(enforcer, null);
+ }
+
+ EnforcerTester(Enforcer enforcer, URI uri) {
+ this.enforcer = enforcer;
+ this.resource = uri;
+ }
+
+ public EnforcerTester on(String uri) {
+ return new EnforcerTester(enforcer, URI.create(uri));
+ }
+
+ public EnforcerTester assertAction(Role role, Action ...actions) {
+ var allowed = List.of(actions);
+
+ allowed.forEach(action -> {
+ var msg = String.format("%s should be allowed to %s on %s", role, action, resource);
+ assertTrue(msg, enforcer.allows(role, action, resource));
+ });
+
+ Action.all().stream().filter(a -> ! allowed.contains(a)).forEach(action -> {
+ var msg = String.format("%s should not be allowed to %s on %s", role, action, resource);
+ assertFalse(msg, enforcer.allows(role, action, resource));
+ });
+
+ return this;
+ }
+ }
}
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 1e28ed466e8..ac146858145 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
@@ -12,7 +12,6 @@ import com.yahoo.config.provision.zone.ZoneApi;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.container.jdisc.secretstore.SecretNotFoundException;
import com.yahoo.container.jdisc.secretstore.SecretStore;
-import com.yahoo.log.LogLevel;
import com.yahoo.security.SubjectAlternativeName;
import com.yahoo.security.X509CertificateUtils;
import com.yahoo.vespa.flags.BooleanFlag;
@@ -156,7 +155,7 @@ public class EndpointCertificateManager {
curator.readAllEndpointCertificateMetadata().forEach((applicationId, storedMetaData) -> {
var lastRequested = Instant.ofEpochSecond(storedMetaData.lastRequested());
if (lastRequested.isBefore(oneMonthAgo) && hasNoDeployments(applicationId)) {
- log.log(LogLevel.INFO, "Cert for app " + applicationId.serializedForm()
+ log.log(Level.INFO, "Cert for app " + applicationId.serializedForm()
+ " has not been requested in a month and app has no deployments"
+ (mode == CleanupMode.ENABLE ? ", deleting from provider and ZK" : ""));
if (mode == CleanupMode.ENABLE) {
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 9b959bf1765..ed1e442f266 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
@@ -219,43 +219,11 @@ public class InternalStepRunner implements StepRunner {
LogEntry.typeOf(LogLevel.parse(entry.level)),
entry.message))
.collect(toList()));
- if ( ! prepareResponse.configChangeActions.refeedActions.stream().allMatch(action -> action.allowed)) {
- List<String> messages = new ArrayList<>();
- messages.add("Deploy failed due to non-compatible changes that require re-feed.");
- messages.add("Your options are:");
- messages.add("1. Revert the incompatible changes.");
- messages.add("2. If you think it is safe in your case, you can override this validation, see");
- messages.add(" http://docs.vespa.ai/documentation/reference/validation-overrides.html");
- messages.add("3. Deploy as a new application under a different name.");
- messages.add("Illegal actions:");
- prepareResponse.configChangeActions.refeedActions.stream()
- .filter(action -> ! action.allowed)
- .flatMap(action -> action.messages.stream())
- .forEach(messages::add);
- logger.log(messages);
- return Optional.of(deploymentFailed);
- }
-
- if ( ! prepareResponse.configChangeActions.reindexActions.stream().allMatch(action -> action.allowed)) {
- List<String> messages = new ArrayList<>();
- messages.add("Deploy failed due to non-compatible changes that require re-index.");
- messages.add("Your options are:");
- messages.add("1. Revert the incompatible changes.");
- messages.add("2. If you think it is safe in your case, you can override this validation, see");
- messages.add(" http://docs.vespa.ai/documentation/reference/validation-overrides.html");
- messages.add("3. Deploy as a new application under a different name.");
- messages.add("Illegal actions:");
- prepareResponse.configChangeActions.reindexActions.stream()
- .filter(action -> ! action.allowed)
- .flatMap(action -> action.messages.stream())
- .forEach(messages::add);
- logger.log(messages);
- return Optional.of(deploymentFailed);
- }
logger.log("Deployment successful.");
if (prepareResponse.message != null)
logger.log(prepareResponse.message);
+
return Optional.of(running);
}
catch (ConfigServerException e) {
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 4d928a6b8a7..29e9ec53577 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
@@ -701,6 +701,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
toSlime(cluster.current(), clusterObject.setObject("current"));
cluster.target().ifPresent(target -> toSlime(target, clusterObject.setObject("target")));
cluster.suggested().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject("suggested")));
+ scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray("scalingEvents"));
+ clusterObject.setString("autoscalingStatus", cluster.autoscalingStatus());
}
return new SlimeJsonResponse(slime);
}
@@ -1593,7 +1595,22 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
void setStatus(Cursor statusObject, ApplicationReindexing.Status status) {
- statusObject.setLong("readyAtMillis", status.readyAt().toEpochMilli());
+ status.readyAt().ifPresent(readyAt -> statusObject.setLong("readyAtMillis", readyAt.toEpochMilli()));
+ status.startedAt().ifPresent(startedAt -> statusObject.setLong("startedAtMillis", startedAt.toEpochMilli()));
+ status.endedAt().ifPresent(endedAt -> statusObject.setLong("endedAtMillis", endedAt.toEpochMilli()));
+ status.state().map(ApplicationApiHandler::toString).ifPresent(state -> statusObject.setString("state", state));
+ status.message().ifPresent(message -> statusObject.setString("message", message));
+ status.progress().ifPresent(progress -> statusObject.setString("progress", progress));
+ }
+
+ private static String toString(ApplicationReindexing.State state) {
+ switch (state) {
+ case PENDING: return "pending";
+ case RUNNING: return "running";
+ case FAILED: return "failed";
+ case SUCCESSFUL: return "successful";
+ default: return null;
+ }
}
/** Enables reindexing of an application in a zone. */
@@ -1914,6 +1931,15 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
object.setDouble("cost", Math.round(resources.nodes() * resources.nodeResources().cost() * 100.0 / 3.0) / 100.0);
}
+ private void scalingEventsToSlime(List<Cluster.ScalingEvent> scalingEvents, Cursor scalingEventsArray) {
+ for (Cluster.ScalingEvent scalingEvent : scalingEvents) {
+ Cursor scalingEventObject = scalingEventsArray.addObject();
+ toSlime(scalingEvent.from(), scalingEventObject.setObject("from"));
+ toSlime(scalingEvent.to(), scalingEventObject.setObject("to"));
+ scalingEventObject.setLong("at", scalingEvent.at().toEpochMilli());
+ }
+ }
+
private void toSlime(NodeResources resources, Cursor object) {
object.setDouble("vcpu", resources.vcpu());
object.setDouble("memoryGb", resources.memoryGb());
@@ -2054,7 +2080,6 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
for (RefeedAction refeedAction : result.prepareResponse().configChangeActions.refeedActions) {
Cursor refeedActionObject = refeedActionsArray.addObject();
refeedActionObject.setString("name", refeedAction.name);
- refeedActionObject.setBool("allowed", refeedAction.allowed);
refeedActionObject.setString("documentType", refeedAction.documentType);
refeedActionObject.setString("clusterName", refeedAction.clusterName);
serviceInfosToSlime(refeedAction.services, refeedActionObject.setArray("services"));
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 7b248052eac..d54971f5b1d 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
@@ -125,34 +125,7 @@ public class InternalStepRunnerTest {
}
@Test
- public void reindexRequirementBlocksDeployment() {
- tester.configServer().setConfigChangeActions(new ConfigChangeActions(List.of(),
- List.of(),
- List.of(new ReindexAction("Reindex",
- false,
- "doctype",
- "cluster",
- Collections.emptyList(),
- List.of("Reindex it!")))));
- tester.jobs().deploy(app.instanceId(), JobType.devUsEast1, Optional.empty(), applicationPackage);
- assertEquals(failed, tester.jobs().last(app.instanceId(), JobType.devUsEast1).get().stepStatuses().get(Step.deployReal));
- }
-
- @Test
- public void refeedRequirementBlocksDeployment() {
- tester.configServer().setConfigChangeActions(new ConfigChangeActions(List.of(),
- List.of(new RefeedAction("Refeed",
- false,
- "doctype",
- "cluster",
- Collections.emptyList(),
- singletonList("Refeed it!"))),
- List.of()));
- tester.jobs().deploy(app.instanceId(), JobType.devUsEast1, Optional.empty(), applicationPackage);
- assertEquals(failed, tester.jobs().last(app.instanceId(), JobType.devUsEast1).get().stepStatuses().get(Step.deployReal));
- }
-
- @Test
+ // TODO jonmv: Change to only wait for restarts, and remove triggering of restarts from runner.
public void restartsServicesAndWaitsForRestartAndReboot() {
RunId id = app.newRun(JobType.productionUsCentral1);
ZoneId zone = id.type().zone(system());
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 8acce352d5a..2b9bad4899f 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
@@ -13,6 +13,7 @@ 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.documentapi.ProgressToken;
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.DeploymentData;
@@ -108,12 +109,17 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
/** Assigns a reserved tenant node to the given deployment, with initial versions. */
public void provision(ZoneId zone, ApplicationId application, ClusterSpec.Id clusterId) {
+ var current = new ClusterResources(2, 1, new NodeResources(2, 8, 50, 1, slow, remote));
Cluster cluster = new Cluster(clusterId,
new ClusterResources(2, 1, new NodeResources(1, 4, 20, 1, slow, remote)),
new ClusterResources(2, 1, new NodeResources(4, 16, 90, 1, slow, remote)),
- new ClusterResources(2, 1, new NodeResources(2, 8, 50, 1, slow, remote)),
+ current,
Optional.of(new ClusterResources(2, 1, new NodeResources(3, 8, 50, 1, slow, remote))),
- Optional.empty());
+ Optional.empty(),
+ List.of(new Cluster.ScalingEvent(new ClusterResources(0, 0, NodeResources.unspecified()),
+ current,
+ Instant.ofEpochMilli(1234))),
+ "the autoscaling status");
nodeRepository.putApplication(zone,
new com.yahoo.vespa.hosted.controller.api.integration.configserver.Application(application,
List.of(cluster)));
@@ -425,9 +431,17 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
Map.of("cluster",
new ApplicationReindexing.Cluster(new Status(Instant.ofEpochMilli(234)),
Map.of("type", 100L),
- Map.of("type", new Status(Instant.ofEpochMilli(345)))))));
+ Map.of("type", new Status(Instant.ofEpochMilli(345),
+ Instant.ofEpochMilli(456),
+ Instant.ofEpochMilli(567),
+ ApplicationReindexing.State.FAILED,
+ "(#`д´)ノ",
+ "some"))))));
+
+
}
+
@Override
public void disableReindexing(DeploymentId deployment) { }
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 5e98ac0d3ee..d303d1a9b83 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
@@ -608,7 +608,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// GET to get reindexing status
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/reindexing", GET)
.userIdentity(USER_ID),
- "{\"enabled\":true,\"status\":{\"readyAtMillis\":123},\"clusters\":[{\"name\":\"cluster\",\"status\":{\"readyAtMillis\":234},\"pending\":[{\"type\":\"type\",\"requiredGeneration\":100}],\"ready\":[{\"type\":\"type\",\"readyAtMillis\":345}]}]}");
+ "{\"enabled\":true,\"status\":{\"readyAtMillis\":123},\"clusters\":[{\"name\":\"cluster\",\"status\":{\"readyAtMillis\":234},\"pending\":[{\"type\":\"type\",\"requiredGeneration\":100}],\"ready\":[{\"type\":\"type\",\"readyAtMillis\":345,\"startedAtMillis\":456,\"endedAtMillis\":567,\"state\":\"failed\",\"message\":\"(#`д´)ノ\",\"progress\":\"some\"}]}]}");
// POST a 'restart application' command
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/restart", POST)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json
index 65fa2a4bf70..817cee7732a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json
@@ -52,7 +52,39 @@
"storageType": "remote"
},
"cost": "(ignore)"
- }
+ },
+ "scalingEvents": [
+ {
+ "from": {
+ "nodes": 0,
+ "groups": 0,
+ "nodeResources": {
+ "vcpu": 0.0,
+ "memoryGb": 0.0,
+ "diskGb": 0.0,
+ "bandwidthGbps": 0.0,
+ "diskSpeed": "fast",
+ "storageType": "any"
+ },
+ "cost": "(ignore)"
+ },
+ "to": {
+ "nodes": 2,
+ "groups": 1,
+ "nodeResources": {
+ "vcpu": 2.0,
+ "memoryGb": 8.0,
+ "diskGb": 50.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "slow",
+ "storageType": "remote"
+ },
+ "cost": "(ignore)"
+ },
+ "at": 1234
+ }
+ ],
+ "autoscalingStatus": "the autoscaling status"
}
}
} \ No newline at end of file
diff --git a/document/src/test/resources/tensor/multi_cell_tensor__cpp b/document/src/test/resources/tensor/multi_cell_tensor__cpp
index 9adda236a4a..deb53463fb5 100644
--- a/document/src/test/resources/tensor/multi_cell_tensor__cpp
+++ b/document/src/test/resources/tensor/multi_cell_tensor__cpp
Binary files differ
diff --git a/document/src/vespa/document/serialization/vespadocumentdeserializer.h b/document/src/vespa/document/serialization/vespadocumentdeserializer.h
index 6792914d9da..5819c6a23cf 100644
--- a/document/src/vespa/document/serialization/vespadocumentdeserializer.h
+++ b/document/src/vespa/document/serialization/vespadocumentdeserializer.h
@@ -7,7 +7,7 @@
#include <memory>
namespace vespalib { class nbostream; }
-namespace vespalib::eval { class Value; }
+namespace vespalib::eval { struct Value; }
namespace document {
class DocumentId;
diff --git a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages60TestCase.java b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages60TestCase.java
index f83f31506e4..d420d64f461 100644
--- a/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages60TestCase.java
+++ b/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/Messages60TestCase.java
@@ -145,14 +145,13 @@ public class Messages60TestCase extends MessagesTestBase {
@Override
public void run() {
GetBucketListMessage msg = new GetBucketListMessage(new BucketId(16, 123));
- msg.setLoadType(loadTypes.getNameMap().get("foo"));
msg.setBucketSpace(BUCKET_SPACE);
assertEquals(BASE_MESSAGE_LENGTH + 12 + serializedLength(BUCKET_SPACE), serialize("GetBucketListMessage", msg));
for (Language lang : LANGUAGES) {
msg = (GetBucketListMessage)deserialize("GetBucketListMessage", DocumentProtocol.MESSAGE_GETBUCKETLIST, lang);
assertEquals(new BucketId(16, 123), msg.getBucketId());
- assertEquals("foo", msg.getLoadType().getName());
+ assertEquals("default", msg.getLoadType().getName());
assertEquals(BUCKET_SPACE, msg.getBucketSpace());
}
}
diff --git a/documentapi/src/tests/messages/messages60test.cpp b/documentapi/src/tests/messages/messages60test.cpp
index c7bb1015e02..5d25002021b 100644
--- a/documentapi/src/tests/messages/messages60test.cpp
+++ b/documentapi/src/tests/messages/messages60test.cpp
@@ -89,16 +89,13 @@ bool
Messages60Test::testGetBucketListMessage()
{
GetBucketListMessage msg(document::BucketId(16, 123));
- msg.setLoadType(_loadTypes["foo"]);
msg.setBucketSpace("beartato");
- EXPECT_EQUAL(string("foo"), msg.getLoadType().getName());
EXPECT_EQUAL(MESSAGE_BASE_LENGTH + 12u + serializedLength("beartato"), serialize("GetBucketListMessage", msg));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
mbus::Routable::UP obj = deserialize("GetBucketListMessage", DocumentProtocol::MESSAGE_GETBUCKETLIST, lang);
if (EXPECT_TRUE(obj)) {
GetBucketListMessage &ref = static_cast<GetBucketListMessage&>(*obj);
- EXPECT_EQUAL(string("foo"), ref.getLoadType().getName());
EXPECT_EQUAL(document::BucketId(16, 123), ref.getBucketId());
EXPECT_EQUAL("beartato", ref.getBucketSpace());
}
@@ -323,6 +320,7 @@ Messages60Test::testGetDocumentMessage()
{
GetDocumentMessage tmp(document::DocumentId("id:ns:testdoc::"), "foo bar");
+ EXPECT_EQUAL(280u, sizeof(GetDocumentMessage));
EXPECT_EQUAL(MESSAGE_BASE_LENGTH + (size_t)31, serialize("GetDocumentMessage", tmp));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
@@ -400,6 +398,11 @@ Messages60Test::testPutDocumentMessage()
msg.setTimestamp(666);
msg.setCondition(TestAndSetCondition("There's just one condition"));
+ EXPECT_EQUAL(64u, sizeof(vespalib::string));
+ EXPECT_EQUAL(sizeof(std::string), sizeof(TestAndSetCondition));
+ EXPECT_EQUAL(112u, sizeof(DocumentMessage));
+ EXPECT_EQUAL(sizeof(TestAndSetCondition) + sizeof(DocumentMessage), sizeof(TestAndSetMessage));
+ EXPECT_EQUAL(sizeof(TestAndSetMessage) + 24, sizeof(PutDocumentMessage));
EXPECT_EQUAL(MESSAGE_BASE_LENGTH +
45u +
serializedLength(msg.getCondition().getSelection()),
@@ -447,6 +450,7 @@ Messages60Test::testPutDocumentReply()
reply.setHighestModificationTimestamp(30);
EXPECT_EQUAL(13u, serialize("PutDocumentReply", reply));
+ EXPECT_EQUAL(112u, sizeof(WriteDocumentReply));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
mbus::Routable::UP obj = deserialize("PutDocumentReply", DocumentProtocol::REPLY_PUTDOCUMENT, lang);
@@ -466,6 +470,7 @@ Messages60Test::testUpdateDocumentReply()
reply.setHighestModificationTimestamp(30);
EXPECT_EQUAL(14u, serialize("UpdateDocumentReply", reply));
+ EXPECT_EQUAL(120u, sizeof(UpdateDocumentReply));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
mbus::Routable::UP obj = deserialize("UpdateDocumentReply", DocumentProtocol::REPLY_UPDATEDOCUMENT, lang);
@@ -485,12 +490,13 @@ Messages60Test::testRemoveDocumentMessage()
msg.setCondition(TestAndSetCondition("There's just one condition"));
+ EXPECT_EQUAL(sizeof(TestAndSetMessage) + 104, sizeof(RemoveDocumentMessage));
EXPECT_EQUAL(MESSAGE_BASE_LENGTH + size_t(20) + serializedLength(msg.getCondition().getSelection()), serialize("RemoveDocumentMessage", msg));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
auto routablePtr = deserialize("RemoveDocumentMessage", DocumentProtocol::MESSAGE_REMOVEDOCUMENT, lang);
- if (EXPECT_TRUE(routablePtr.get() != nullptr)) {
+ if (EXPECT_TRUE(routablePtr)) {
auto & ref = static_cast<RemoveDocumentMessage &>(*routablePtr);
EXPECT_EQUAL(string("id:ns:testdoc::"), ref.getDocumentId().toString());
EXPECT_EQUAL(msg.getCondition().getSelection(), ref.getCondition().getSelection());
@@ -506,6 +512,7 @@ Messages60Test::testRemoveDocumentReply()
std::vector<uint64_t> ts;
reply.setWasFound(false);
reply.setHighestModificationTimestamp(30);
+ EXPECT_EQUAL(120u, sizeof(RemoveDocumentReply));
EXPECT_EQUAL(14u, serialize("RemoveDocumentReply", reply));
@@ -663,12 +670,13 @@ Messages60Test::testUpdateDocumentMessage()
msg.setNewTimestamp(777u);
msg.setCondition(TestAndSetCondition("There's just one condition"));
+ EXPECT_EQUAL(sizeof(TestAndSetMessage) + 32, sizeof(UpdateDocumentMessage));
EXPECT_EQUAL(MESSAGE_BASE_LENGTH + 93u + serializedLength(msg.getCondition().getSelection()), serialize("UpdateDocumentMessage", msg));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
auto routableUp = deserialize("UpdateDocumentMessage", DocumentProtocol::MESSAGE_UPDATEDOCUMENT, lang);
- if (EXPECT_TRUE(routableUp.get() != nullptr)) {
+ if (EXPECT_TRUE(routableUp)) {
auto & deserializedMsg = static_cast<UpdateDocumentMessage &>(*routableUp);
EXPECT_EQUAL(msg.getDocumentUpdate(), deserializedMsg.getDocumentUpdate());
EXPECT_EQUAL(msg.getOldTimestamp(), deserializedMsg.getOldTimestamp());
@@ -885,6 +893,7 @@ Messages60Test::testGetDocumentReply()
createDoc(getTypeRepo(), "testdoc", "id:ns:testdoc::");
GetDocumentReply tmp(doc);
+ EXPECT_EQUAL(128u, sizeof(GetDocumentReply));
EXPECT_EQUAL((size_t)47, serialize("GetDocumentReply", tmp));
for (uint32_t lang = 0; lang < NUM_LANGUAGES; ++lang) {
diff --git a/documentapi/src/tests/replymerger/replymerger_test.cpp b/documentapi/src/tests/replymerger/replymerger_test.cpp
index 4626ccd0a60..f74ad23da1d 100644
--- a/documentapi/src/tests/replymerger/replymerger_test.cpp
+++ b/documentapi/src/tests/replymerger/replymerger_test.cpp
@@ -8,6 +8,7 @@
#include <vespa/documentapi/messagebus/messages/updatedocumentreply.h>
#include <vespa/documentapi/messagebus/messages/getdocumentreply.h>
#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/error.h>
using namespace documentapi;
diff --git a/documentapi/src/vespa/documentapi/loadtypes/loadtypeset.cpp b/documentapi/src/vespa/documentapi/loadtypes/loadtypeset.cpp
index cde15776b62..42aa79332db 100644
--- a/documentapi/src/vespa/documentapi/loadtypes/loadtypeset.cpp
+++ b/documentapi/src/vespa/documentapi/loadtypes/loadtypeset.cpp
@@ -38,7 +38,7 @@ LoadTypeSet::LoadTypeSet(const LoadTypeConfig& config)
configure(config);
}
-LoadTypeSet::~LoadTypeSet() { }
+LoadTypeSet::~LoadTypeSet() = default;
void
LoadTypeSet::addLoadType(uint32_t id, const string& name, Priority::Value priority) {
diff --git a/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp b/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp
index dcfc0fa5f6e..a957ce5e4ff 100644
--- a/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/documentprotocol.cpp
@@ -8,6 +8,7 @@
#include <vespa/document/util/stringutil.h>
#include <vespa/documentapi/documentapi.h>
#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/messagebus/error.h>
#include <sstream>
#include <cassert>
@@ -31,16 +32,16 @@ DocumentProtocol::DocumentProtocol(const LoadTypeSet& loadTypes,
string cfg = (configId.empty() ? "client" : configId);
// When adding factories to this list, please KEEP THEM ORDERED alphabetically like they are now.
- putRoutingPolicyFactory("AND", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::AndPolicyFactory()));
- putRoutingPolicyFactory("Content", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::ContentPolicyFactory()));
- putRoutingPolicyFactory("MessageType", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::MessageTypePolicyFactory()));
- putRoutingPolicyFactory("DocumentRouteSelector", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::DocumentRouteSelectorPolicyFactory(*_repo, cfg)));
- putRoutingPolicyFactory("Extern", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::ExternPolicyFactory()));
- putRoutingPolicyFactory("LocalService", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::LocalServicePolicyFactory()));
- putRoutingPolicyFactory("RoundRobin", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::RoundRobinPolicyFactory()));
- putRoutingPolicyFactory("Storage", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::StoragePolicyFactory()));
- putRoutingPolicyFactory("SubsetService", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::SubsetServicePolicyFactory()));
- putRoutingPolicyFactory("LoadBalancer", IRoutingPolicyFactory::SP(new RoutingPolicyFactories::LoadBalancerPolicyFactory()));
+ putRoutingPolicyFactory("AND", std::make_shared<RoutingPolicyFactories::AndPolicyFactory>());
+ putRoutingPolicyFactory("Content", std::make_shared<RoutingPolicyFactories::ContentPolicyFactory>());
+ putRoutingPolicyFactory("MessageType", std::make_shared<RoutingPolicyFactories::MessageTypePolicyFactory>());
+ putRoutingPolicyFactory("DocumentRouteSelector", std::make_shared<RoutingPolicyFactories::DocumentRouteSelectorPolicyFactory>(*_repo, cfg));
+ putRoutingPolicyFactory("Extern", std::make_shared<RoutingPolicyFactories::ExternPolicyFactory>());
+ putRoutingPolicyFactory("LocalService", std::make_shared<RoutingPolicyFactories::LocalServicePolicyFactory>());
+ putRoutingPolicyFactory("RoundRobin", std::make_shared<RoutingPolicyFactories::RoundRobinPolicyFactory>());
+ putRoutingPolicyFactory("Storage", std::make_shared<RoutingPolicyFactories::StoragePolicyFactory>());
+ putRoutingPolicyFactory("SubsetService", std::make_shared<RoutingPolicyFactories::SubsetServicePolicyFactory>());
+ putRoutingPolicyFactory("LoadBalancer", std::make_shared<RoutingPolicyFactories::LoadBalancerPolicyFactory>());
// Prepare version specifications to use when adding routable factories.
vespalib::VersionSpecification version6(6, 221);
@@ -48,42 +49,42 @@ DocumentProtocol::DocumentProtocol(const LoadTypeSet& loadTypes,
std::vector<vespalib::VersionSpecification> from6 = { version6 };
// Add 6.x serialization
- putRoutableFactory(MESSAGE_CREATEVISITOR, IRoutableFactory::SP(new RoutableFactories60::CreateVisitorMessageFactory()), from6);
- putRoutableFactory(MESSAGE_DESTROYVISITOR, IRoutableFactory::SP(new RoutableFactories60::DestroyVisitorMessageFactory()), from6);
- putRoutableFactory(MESSAGE_DOCUMENTLIST, IRoutableFactory::SP(new RoutableFactories60::DocumentListMessageFactory(*_repo)), from6);
- putRoutableFactory(MESSAGE_DOCUMENTSUMMARY, IRoutableFactory::SP(new RoutableFactories60::DocumentSummaryMessageFactory()), from6);
- putRoutableFactory(MESSAGE_EMPTYBUCKETS, IRoutableFactory::SP(new RoutableFactories60::EmptyBucketsMessageFactory()), from6);
- putRoutableFactory(MESSAGE_GETBUCKETLIST, IRoutableFactory::SP(new RoutableFactories60::GetBucketListMessageFactory()), from6);
- putRoutableFactory(MESSAGE_GETBUCKETSTATE, IRoutableFactory::SP(new RoutableFactories60::GetBucketStateMessageFactory()), from6);
- putRoutableFactory(MESSAGE_GETDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::GetDocumentMessageFactory()), from6);
- putRoutableFactory(MESSAGE_MAPVISITOR, IRoutableFactory::SP(new RoutableFactories60::MapVisitorMessageFactory()), from6);
- putRoutableFactory(MESSAGE_PUTDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::PutDocumentMessageFactory(*_repo)), from6);
- putRoutableFactory(MESSAGE_QUERYRESULT, IRoutableFactory::SP(new RoutableFactories60::QueryResultMessageFactory()), from6);
- putRoutableFactory(MESSAGE_REMOVEDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::RemoveDocumentMessageFactory()), from6);
- putRoutableFactory(MESSAGE_REMOVELOCATION, IRoutableFactory::SP(new RoutableFactories60::RemoveLocationMessageFactory(*_repo)), from6);
- putRoutableFactory(MESSAGE_SEARCHRESULT, IRoutableFactory::SP(new RoutableFactories60::SearchResultMessageFactory()), from6);
- putRoutableFactory(MESSAGE_STATBUCKET, IRoutableFactory::SP(new RoutableFactories60::StatBucketMessageFactory()), from6);
- putRoutableFactory(MESSAGE_UPDATEDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::UpdateDocumentMessageFactory(*_repo)), from6);
- putRoutableFactory(MESSAGE_VISITORINFO, IRoutableFactory::SP(new RoutableFactories60::VisitorInfoMessageFactory()), from6);
- putRoutableFactory(REPLY_CREATEVISITOR, IRoutableFactory::SP(new RoutableFactories60::CreateVisitorReplyFactory()), from6);
- putRoutableFactory(REPLY_DESTROYVISITOR, IRoutableFactory::SP(new RoutableFactories60::DestroyVisitorReplyFactory()), from6);
- putRoutableFactory(REPLY_DOCUMENTIGNORED, IRoutableFactory::SP(new RoutableFactories60::DocumentIgnoredReplyFactory()), from6);
- putRoutableFactory(REPLY_DOCUMENTLIST, IRoutableFactory::SP(new RoutableFactories60::DocumentListReplyFactory()), from6);
- putRoutableFactory(REPLY_DOCUMENTSUMMARY, IRoutableFactory::SP(new RoutableFactories60::DocumentSummaryReplyFactory()), from6);
- putRoutableFactory(REPLY_EMPTYBUCKETS, IRoutableFactory::SP(new RoutableFactories60::EmptyBucketsReplyFactory()), from6);
- putRoutableFactory(REPLY_GETBUCKETLIST, IRoutableFactory::SP(new RoutableFactories60::GetBucketListReplyFactory()), from6);
- putRoutableFactory(REPLY_GETBUCKETSTATE, IRoutableFactory::SP(new RoutableFactories60::GetBucketStateReplyFactory()), from6);
- putRoutableFactory(REPLY_GETDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::GetDocumentReplyFactory(*_repo)), from6);
- putRoutableFactory(REPLY_MAPVISITOR, IRoutableFactory::SP(new RoutableFactories60::MapVisitorReplyFactory()), from6);
- putRoutableFactory(REPLY_PUTDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::PutDocumentReplyFactory()), from6);
- putRoutableFactory(REPLY_QUERYRESULT, IRoutableFactory::SP(new RoutableFactories60::QueryResultReplyFactory()), from6);
- putRoutableFactory(REPLY_REMOVEDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::RemoveDocumentReplyFactory()), from6);
- putRoutableFactory(REPLY_REMOVELOCATION, IRoutableFactory::SP(new RoutableFactories60::RemoveLocationReplyFactory()), from6);
- putRoutableFactory(REPLY_SEARCHRESULT, IRoutableFactory::SP(new RoutableFactories60::SearchResultReplyFactory()), from6);
- putRoutableFactory(REPLY_STATBUCKET, IRoutableFactory::SP(new RoutableFactories60::StatBucketReplyFactory()), from6);
- putRoutableFactory(REPLY_UPDATEDOCUMENT, IRoutableFactory::SP(new RoutableFactories60::UpdateDocumentReplyFactory()), from6);
- putRoutableFactory(REPLY_VISITORINFO, IRoutableFactory::SP(new RoutableFactories60::VisitorInfoReplyFactory()), from6);
- putRoutableFactory(REPLY_WRONGDISTRIBUTION, IRoutableFactory::SP(new RoutableFactories60::WrongDistributionReplyFactory()), from6);
+ putRoutableFactory(MESSAGE_CREATEVISITOR, std::make_shared<RoutableFactories60::CreateVisitorMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_DESTROYVISITOR, std::make_shared<RoutableFactories60::DestroyVisitorMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_DOCUMENTLIST, std::make_shared<RoutableFactories60::DocumentListMessageFactory>(*_repo), from6);
+ putRoutableFactory(MESSAGE_DOCUMENTSUMMARY, std::make_shared<RoutableFactories60::DocumentSummaryMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_EMPTYBUCKETS, std::make_shared<RoutableFactories60::EmptyBucketsMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_GETBUCKETLIST, std::make_shared<RoutableFactories60::GetBucketListMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_GETBUCKETSTATE, std::make_shared<RoutableFactories60::GetBucketStateMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_GETDOCUMENT, std::make_shared<RoutableFactories60::GetDocumentMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_MAPVISITOR, std::make_shared<RoutableFactories60::MapVisitorMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_PUTDOCUMENT, std::make_shared<RoutableFactories60::PutDocumentMessageFactory>(*_repo), from6);
+ putRoutableFactory(MESSAGE_QUERYRESULT, std::make_shared<RoutableFactories60::QueryResultMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_REMOVEDOCUMENT, std::make_shared<RoutableFactories60::RemoveDocumentMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_REMOVELOCATION, std::make_shared<RoutableFactories60::RemoveLocationMessageFactory>(*_repo), from6);
+ putRoutableFactory(MESSAGE_SEARCHRESULT, std::make_shared<RoutableFactories60::SearchResultMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_STATBUCKET, std::make_shared<RoutableFactories60::StatBucketMessageFactory>(), from6);
+ putRoutableFactory(MESSAGE_UPDATEDOCUMENT, std::make_shared<RoutableFactories60::UpdateDocumentMessageFactory>(*_repo), from6);
+ putRoutableFactory(MESSAGE_VISITORINFO, std::make_shared<RoutableFactories60::VisitorInfoMessageFactory>(), from6);
+ putRoutableFactory(REPLY_CREATEVISITOR, std::make_shared<RoutableFactories60::CreateVisitorReplyFactory>(), from6);
+ putRoutableFactory(REPLY_DESTROYVISITOR, std::make_shared<RoutableFactories60::DestroyVisitorReplyFactory>(), from6);
+ putRoutableFactory(REPLY_DOCUMENTIGNORED, std::make_shared<RoutableFactories60::DocumentIgnoredReplyFactory>(), from6);
+ putRoutableFactory(REPLY_DOCUMENTLIST, std::make_shared<RoutableFactories60::DocumentListReplyFactory>(), from6);
+ putRoutableFactory(REPLY_DOCUMENTSUMMARY, std::make_shared<RoutableFactories60::DocumentSummaryReplyFactory>(), from6);
+ putRoutableFactory(REPLY_EMPTYBUCKETS, std::make_shared<RoutableFactories60::EmptyBucketsReplyFactory>(), from6);
+ putRoutableFactory(REPLY_GETBUCKETLIST, std::make_shared<RoutableFactories60::GetBucketListReplyFactory>(), from6);
+ putRoutableFactory(REPLY_GETBUCKETSTATE, std::make_shared<RoutableFactories60::GetBucketStateReplyFactory>(), from6);
+ putRoutableFactory(REPLY_GETDOCUMENT, std::make_shared<RoutableFactories60::GetDocumentReplyFactory>(*_repo), from6);
+ putRoutableFactory(REPLY_MAPVISITOR, std::make_shared<RoutableFactories60::MapVisitorReplyFactory>(), from6);
+ putRoutableFactory(REPLY_PUTDOCUMENT, std::make_shared<RoutableFactories60::PutDocumentReplyFactory>(), from6);
+ putRoutableFactory(REPLY_QUERYRESULT, std::make_shared<RoutableFactories60::QueryResultReplyFactory>(), from6);
+ putRoutableFactory(REPLY_REMOVEDOCUMENT, std::make_shared<RoutableFactories60::RemoveDocumentReplyFactory>(), from6);
+ putRoutableFactory(REPLY_REMOVELOCATION, std::make_shared<RoutableFactories60::RemoveLocationReplyFactory>(), from6);
+ putRoutableFactory(REPLY_SEARCHRESULT, std::make_shared<RoutableFactories60::SearchResultReplyFactory>(), from6);
+ putRoutableFactory(REPLY_STATBUCKET, std::make_shared<RoutableFactories60::StatBucketReplyFactory>(), from6);
+ putRoutableFactory(REPLY_UPDATEDOCUMENT, std::make_shared<RoutableFactories60::UpdateDocumentReplyFactory>(), from6);
+ putRoutableFactory(REPLY_VISITORINFO, std::make_shared<RoutableFactories60::VisitorInfoReplyFactory>(), from6);
+ putRoutableFactory(REPLY_WRONGDISTRIBUTION, std::make_shared<RoutableFactories60::WrongDistributionReplyFactory>(), from6);
}
DocumentProtocol::~DocumentProtocol() = default;
diff --git a/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.cpp b/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.cpp
index 199d83749c2..2bd832a5855 100644
--- a/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.cpp
@@ -2,23 +2,21 @@
#include "documentmessage.h"
#include <vespa/documentapi/messagebus/documentprotocol.h>
-#include <cassert>
namespace documentapi {
DocumentMessage::DocumentMessage() :
mbus::Message(),
_priority(Priority::PRI_NORMAL_3),
- _loadType(LoadType::DEFAULT),
_approxSize(1024)
{}
+DocumentMessage::~DocumentMessage() = default;
+
mbus::Reply::UP
DocumentMessage::createReply() const
{
- mbus::Reply::UP ret(doCreateReply().release());
- assert(ret.get() != nullptr);
- return ret;
+ return doCreateReply();
}
const mbus::string&
diff --git a/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.h b/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.h
index 6e1507068eb..d2b8f74f716 100644
--- a/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.h
+++ b/documentapi/src/vespa/documentapi/messagebus/messages/documentmessage.h
@@ -2,17 +2,14 @@
#pragma once
#include "documentreply.h"
-#include <vespa/documentapi/loadtypes/loadtype.h>
#include <vespa/messagebus/message.h>
-#include <vespa/messagebus/reply.h>
namespace documentapi {
class DocumentMessage : public mbus::Message {
private:
Priority::Value _priority;
- LoadType _loadType;
- uint32_t _approxSize; // Not sent on wire; set by deserializer or by caller.
+ uint32_t _approxSize; // Not sent on wire; set by deserializer or by caller.
protected:
/**
@@ -30,15 +27,8 @@ public:
typedef std::unique_ptr<DocumentMessage> UP;
typedef std::shared_ptr<DocumentMessage> SP;
- /**
- * Constructs a new document message with no content.
- */
DocumentMessage();
-
- /**
- * Virtual destructor required for inheritance.
- */
- virtual ~DocumentMessage() { }
+ ~DocumentMessage() override;
/**
* Creates and returns a reply to this message. This method uses the internal {@link #doCreateReply()} to
@@ -47,7 +37,7 @@ public:
*
* @return The created reply.
*/
- mbus::Reply::UP createReply() const;
+ std::unique_ptr<mbus::Reply> createReply() const;
/**
* Returns the priority of this message.
@@ -65,16 +55,6 @@ public:
*/
void setPriority(Priority::Value p) { _priority = p; };
- /**
- * @return Returns the load type for this message.
- */
- const LoadType& getLoadType() const { return _loadType; }
-
- /**
- * Sets the load type for this message.
- */
- void setLoadType(const LoadType& loadType) { _loadType = loadType; }
-
uint32_t getApproxSize() const override;
void setApproxSize(uint32_t approxSize) {
diff --git a/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.cpp b/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.cpp
index 5a2478cbd65..d16f5d5cd66 100644
--- a/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.cpp
@@ -9,9 +9,9 @@ DocumentReply::DocumentReply(uint32_t type) :
mbus::Reply(),
_type(type),
_priority(Priority::PRI_NORMAL_3)
-{
- // empty
-}
+{ }
+
+DocumentReply::~DocumentReply() = default;
const mbus::string&
DocumentReply::getProtocol() const
diff --git a/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.h b/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.h
index c166a4acb73..136c4983516 100644
--- a/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.h
+++ b/documentapi/src/vespa/documentapi/messagebus/messages/documentreply.h
@@ -32,7 +32,7 @@ public:
/**
* Virtual destructor required for inheritance.
*/
- ~DocumentReply() { }
+ ~DocumentReply() override;
/**
* Returns the priority tag for this message. This is an optional tag added for VDS that is not interpreted by the
diff --git a/documentapi/src/vespa/documentapi/messagebus/messages/testandsetcondition.h b/documentapi/src/vespa/documentapi/messagebus/messages/testandsetcondition.h
index 33f691412dc..1af0178702d 100644
--- a/documentapi/src/vespa/documentapi/messagebus/messages/testandsetcondition.h
+++ b/documentapi/src/vespa/documentapi/messagebus/messages/testandsetcondition.h
@@ -6,7 +6,7 @@ namespace documentapi {
class TestAndSetCondition {
private:
- string _selection;
+ std::string _selection;
public:
TestAndSetCondition()
@@ -23,7 +23,7 @@ public:
TestAndSetCondition(TestAndSetCondition &&) = default;
TestAndSetCondition & operator=(TestAndSetCondition &&) = default;
- const string & getSelection() const { return _selection; }
+ const std::string & getSelection() const { return _selection; }
bool isPresent() const { return !_selection.empty(); }
};
diff --git a/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.cpp b/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.cpp
index f9a48cb0755..2c6a0d08032 100644
--- a/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.cpp
@@ -4,6 +4,7 @@
namespace documentapi {
-TestAndSetMessage::~TestAndSetMessage() { }
+TestAndSetMessage::TestAndSetMessage() = default;
+TestAndSetMessage::~TestAndSetMessage() = default;
}
diff --git a/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.h b/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.h
index d3e912549c4..2e623121c85 100644
--- a/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.h
+++ b/documentapi/src/vespa/documentapi/messagebus/messages/testandsetmessage.h
@@ -12,7 +12,8 @@ private:
TestAndSetCondition _condition;
public:
- ~TestAndSetMessage();
+ TestAndSetMessage();
+ ~TestAndSetMessage() override;
void setCondition(const TestAndSetCondition & condition) { _condition = condition; }
const TestAndSetCondition & getCondition() const { return _condition; }
};
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.cpp b/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.cpp
index baca64353fc..0683c0179cc 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/andpolicy.cpp
@@ -1,8 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "andpolicy.h"
-#include <vespa/messagebus/error.h>
-#include <vespa/messagebus/errorcode.h>
-#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/routing/routingcontext.h>
#include <vespa/documentapi/messagebus/documentprotocol.h>
@@ -18,10 +15,7 @@ ANDPolicy::ANDPolicy(const string &param)
}
}
-ANDPolicy::~ANDPolicy()
-{
- // empty
-}
+ANDPolicy::~ANDPolicy() = default;
void
ANDPolicy::select(mbus::RoutingContext &context)
@@ -29,11 +23,9 @@ ANDPolicy::select(mbus::RoutingContext &context)
if (_hops.empty()) {
context.addChildren(context.getAllRecipients());
} else {
- for (std::vector<mbus::Hop>::iterator it = _hops.begin();
- it != _hops.end(); ++it)
- {
+ for (auto & hop : _hops) {
mbus::Route route = context.getRoute();
- route.setHop(0, *it);
+ route.setHop(0, hop);
context.addChild(route);
}
}
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.cpp b/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.cpp
index 82cda6c773f..21b09f32419 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.cpp
@@ -9,6 +9,7 @@
#include "asyncinitializationpolicy.h"
#include <vespa/vespalib/util/threadstackexecutor.h>
#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/error.h>
#include <vespa/documentapi/messagebus/documentprotocol.h>
#include <vespa/vespalib/text/stringtokenizer.h>
@@ -50,20 +51,23 @@ AsyncInitializationPolicy::initSynchronous()
_state = State::DONE;
}
+namespace {
+
mbus::Error
-AsyncInitializationPolicy::currentPolicyInitError() const
-{
+currentPolicyInitError(vespalib::stringref error) {
// If an init error has been recorded for the last init attempt, report
// it back until we've managed to successfully complete the init step.
- if (_error.empty()) {
+ if (error.empty()) {
return mbus::Error(DocumentProtocol::ERROR_NODE_NOT_READY,
"Waiting to initialize policy");
} else {
return mbus::Error(DocumentProtocol::ERROR_POLICY_FAILURE,
- "Error when creating policy: " + _error);
+ "Error when creating policy: " + error);
}
}
+}
+
void
AsyncInitializationPolicy::select(mbus::RoutingContext& context)
{
@@ -87,7 +91,7 @@ AsyncInitializationPolicy::select(mbus::RoutingContext& context)
if (_state != State::DONE) {
auto reply = std::make_unique<mbus::EmptyReply>();
- reply->addError(currentPolicyInitError());
+ reply->addError(currentPolicyInitError(_error));
context.setReply(std::move(reply));
return;
}
@@ -96,7 +100,7 @@ AsyncInitializationPolicy::select(mbus::RoutingContext& context)
// deadlock (executor will stall until all its tasks have finished
// executing, and any queued tasks would attempt to take the mutex
// we're currently holding, deadlocking both threads).
- _executor.reset(nullptr);
+ _executor.reset();
}
doSelect(context);
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.h b/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.h
index 0e30da8e7c8..3061eb3d337 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.h
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/asyncinitializationpolicy.h
@@ -2,7 +2,6 @@
#pragma once
#include <vespa/messagebus/routing/iroutingpolicy.h>
-#include <vespa/messagebus/error.h>
#include <vespa/vespalib/util/executor.h>
#include <vespa/documentapi/common.h>
#include <map>
@@ -42,8 +41,6 @@ public:
void needAsynchronousInit() { _syncInit = false; }
private:
- mbus::Error currentPolicyInitError() const;
-
class Task : public vespalib::Executor::Task
{
public:
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/loadbalancerpolicy.cpp b/documentapi/src/vespa/documentapi/messagebus/policies/loadbalancerpolicy.cpp
index 3bf17213b26..feee7db8640 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/loadbalancerpolicy.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/loadbalancerpolicy.cpp
@@ -2,6 +2,7 @@
#include "loadbalancerpolicy.h"
#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/error.h>
#include <vespa/messagebus/routing/ihopdirective.h>
#include <vespa/messagebus/routing/routingcontext.h>
#include <vespa/messagebus/routing/verbatimdirective.h>
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.cpp b/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.cpp
index a58f3439df2..e780806c8c0 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/roundrobinpolicy.cpp
@@ -3,7 +3,7 @@
#include "roundrobinpolicy.h"
#include <vespa/documentapi/messagebus/documentprotocol.h>
#include <vespa/messagebus/emptyreply.h>
-#include <vespa/messagebus/routing/verbatimdirective.h>
+#include <vespa/messagebus/error.h>
namespace documentapi {
@@ -18,7 +18,7 @@ RoundRobinPolicy::RoundRobinPolicy(const string &) :
_cache()
{}
-RoundRobinPolicy::~RoundRobinPolicy() {}
+RoundRobinPolicy::~RoundRobinPolicy() = default;
void
RoundRobinPolicy::select(mbus::RoutingContext &ctx)
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/storagepolicy.cpp b/documentapi/src/vespa/documentapi/messagebus/policies/storagepolicy.cpp
index e49d412fee1..3fc1df0352a 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/storagepolicy.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/storagepolicy.cpp
@@ -4,7 +4,7 @@
#include <vespa/document/base/documentid.h>
#include <vespa/document/update/documentupdate.h>
#include <vespa/messagebus/emptyreply.h>
-#include <vespa/messagebus/routing/verbatimdirective.h>
+#include <vespa/messagebus/error.h>
#include <vespa/documentapi/documentapi.h>
#include <vespa/vdslib/state/clusterstate.h>
#include <vespa/vespalib/stllike/asciistream.h>
diff --git a/documentapi/src/vespa/documentapi/messagebus/routablefactories60.cpp b/documentapi/src/vespa/documentapi/messagebus/routablefactories60.cpp
index 7bae7dd7e77..26e609c79fa 100644
--- a/documentapi/src/vespa/documentapi/messagebus/routablefactories60.cpp
+++ b/documentapi/src/vespa/documentapi/messagebus/routablefactories60.cpp
@@ -22,24 +22,23 @@ RoutableFactories60::DocumentMessageFactory::encode(const mbus::Routable &obj, v
{
const auto &msg = static_cast<const DocumentMessage&>(obj);
out.putByte(msg.getPriority());
- out.putInt(msg.getLoadType().getId());
+ out.putInt(LoadType::DEFAULT.getId());
return doEncode(msg, out);
}
mbus::Routable::UP
-RoutableFactories60::DocumentMessageFactory::decode(document::ByteBuffer &in, const LoadTypeSet& loadTypes) const
+RoutableFactories60::DocumentMessageFactory::decode(document::ByteBuffer &in, const LoadTypeSet&) const
{
uint8_t pri;
in.getByte(pri);
- uint32_t loadClass = decodeInt(in);
+ (void) decodeInt(in);
DocumentMessage::UP msg = doDecode(in);
if (msg) {
msg->setPriority((Priority::Value)pri);
- msg->setLoadType(loadTypes[loadClass]);
}
- return mbus::Routable::UP(msg.release());
+ return msg;
}
bool
diff --git a/documentapi/src/vespa/documentapi/messagebus/routablefactories60.h b/documentapi/src/vespa/documentapi/messagebus/routablefactories60.h
index 0a997f3ffd9..579abbda291 100644
--- a/documentapi/src/vespa/documentapi/messagebus/routablefactories60.h
+++ b/documentapi/src/vespa/documentapi/messagebus/routablefactories60.h
@@ -137,7 +137,7 @@ public:
virtual bool encodeBucketSpace(vespalib::stringref bucketSpace, vespalib::GrowableByteBuffer& buf) const;
virtual string decodeBucketSpace(document::ByteBuffer&) const;
public:
- CreateVisitorMessageFactory() : DocumentMessageFactory() {}
+ CreateVisitorMessageFactory() noexcept : DocumentMessageFactory() {}
};
class CreateVisitorReplyFactory : public DocumentReplyFactory {
protected:
@@ -164,7 +164,7 @@ public:
DocumentMessage::UP doDecode(document::ByteBuffer &buf) const override;
bool doEncode(const DocumentMessage &msg, vespalib::GrowableByteBuffer &buf) const override;
public:
- DocumentListMessageFactory(const document::DocumentTypeRepo &r)
+ DocumentListMessageFactory(const document::DocumentTypeRepo &r) noexcept
: _repo(r) {}
};
class DocumentListReplyFactory : public DocumentReplyFactory {
@@ -224,14 +224,14 @@ public:
DocumentReply::UP doDecode(document::ByteBuffer &buf) const override;
bool doEncode(const DocumentReply &msg, vespalib::GrowableByteBuffer &buf) const override;
public:
- GetDocumentReplyFactory(const document::DocumentTypeRepo &r) : _repo(r) {}
+ GetDocumentReplyFactory(const document::DocumentTypeRepo &r) noexcept : _repo(r) {}
};
class MapVisitorMessageFactory : public DocumentMessageFactory {
protected:
DocumentMessage::UP doDecode(document::ByteBuffer &buf) const override;
bool doEncode(const DocumentMessage &msg, vespalib::GrowableByteBuffer &buf) const override;
public:
- MapVisitorMessageFactory() : DocumentMessageFactory() {}
+ MapVisitorMessageFactory() noexcept : DocumentMessageFactory() {}
};
class MapVisitorReplyFactory : public DocumentReplyFactory {
protected:
@@ -248,7 +248,7 @@ public:
bool doEncode(const DocumentMessage &msg, vespalib::GrowableByteBuffer &buf) const override;
public:
void decodeInto(PutDocumentMessage & msg, document::ByteBuffer & buf) const;
- PutDocumentMessageFactory(const document::DocumentTypeRepo &r) : _repo(r) {}
+ PutDocumentMessageFactory(const document::DocumentTypeRepo &r) noexcept : _repo(r) {}
};
class PutDocumentReplyFactory : public DocumentReplyFactory {
protected:
@@ -275,7 +275,7 @@ public:
DocumentMessage::UP doDecode(document::ByteBuffer &buf) const override;
bool doEncode(const DocumentMessage &msg, vespalib::GrowableByteBuffer &buf) const override;
public:
- RemoveLocationMessageFactory(const document::DocumentTypeRepo &r) : _repo(r) {}
+ RemoveLocationMessageFactory(const document::DocumentTypeRepo &r) noexcept : _repo(r) {}
};
class RemoveLocationReplyFactory : public DocumentReplyFactory {
protected:
@@ -324,7 +324,7 @@ public:
bool doEncode(const DocumentMessage &msg, vespalib::GrowableByteBuffer &buf) const override;
public:
void decodeInto(UpdateDocumentMessage & msg, document::ByteBuffer & buf) const;
- UpdateDocumentMessageFactory(const document::DocumentTypeRepo &r) : _repo(r) {}
+ UpdateDocumentMessageFactory(const document::DocumentTypeRepo &r) noexcept : _repo(r) {}
};
class UpdateDocumentReplyFactory : public DocumentReplyFactory {
protected:
diff --git a/documentapi/test/crosslanguagefiles/6.221-cpp-GetBucketListMessage.dat b/documentapi/test/crosslanguagefiles/6.221-cpp-GetBucketListMessage.dat
index c42c61460ac..591e55e5849 100644
--- a/documentapi/test/crosslanguagefiles/6.221-cpp-GetBucketListMessage.dat
+++ b/documentapi/test/crosslanguagefiles/6.221-cpp-GetBucketListMessage.dat
Binary files differ
diff --git a/documentapi/test/crosslanguagefiles/6.221-java-GetBucketListMessage.dat b/documentapi/test/crosslanguagefiles/6.221-java-GetBucketListMessage.dat
index c42c61460ac..591e55e5849 100644
--- a/documentapi/test/crosslanguagefiles/6.221-java-GetBucketListMessage.dat
+++ b/documentapi/test/crosslanguagefiles/6.221-java-GetBucketListMessage.dat
Binary files differ
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index 76c76d64b79..ee8509fcf19 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -53,7 +53,7 @@ vespa_define_module(
src/tests/instruction/dense_tensor_peek_function
src/tests/instruction/index_lookup_table
src/tests/instruction/join_with_number
- src/tests/tensor/default_value_builder_factory
+ src/tests/streamed/value
src/tests/tensor/dense_add_dimension_optimizer
src/tests/tensor/dense_dimension_combiner
src/tests/tensor/dense_fast_rename_optimizer
@@ -71,7 +71,6 @@ vespa_define_module(
src/tests/tensor/direct_sparse_tensor_builder
src/tests/tensor/instruction_benchmark
src/tests/tensor/onnx_wrapper
- src/tests/tensor/packed_mappings
src/tests/tensor/partial_add
src/tests/tensor/partial_modify
src/tests/tensor/partial_remove
@@ -92,9 +91,9 @@ vespa_define_module(
src/vespa/eval/eval/value_cache
src/vespa/eval/gp
src/vespa/eval/instruction
+ src/vespa/eval/streamed
src/vespa/eval/tensor
src/vespa/eval/tensor/dense
- src/vespa/eval/tensor/mixed
src/vespa/eval/tensor/serialization
src/vespa/eval/tensor/sparse
)
diff --git a/eval/src/tests/eval/engine_or_factory/engine_or_factory_override_test.cpp b/eval/src/tests/eval/engine_or_factory/engine_or_factory_override_test.cpp
index 8480bb7d39e..3aa804b0722 100644
--- a/eval/src/tests/eval/engine_or_factory/engine_or_factory_override_test.cpp
+++ b/eval/src/tests/eval/engine_or_factory/engine_or_factory_override_test.cpp
@@ -10,16 +10,16 @@ using namespace vespalib::eval;
using namespace vespalib::tensor;
TEST(EngineOrFactoryOverrideTest, set_can_override_get_result) {
- EngineOrFactory::set(FastValueBuilderFactory::get());
- EXPECT_EQ(EngineOrFactory::get().to_string(), "FastValueBuilderFactory");
+ EngineOrFactory::set(DefaultTensorEngine::ref());
+ EXPECT_EQ(EngineOrFactory::get().to_string(), "DefaultTensorEngine");
}
TEST(EngineOrFactoryOverrideTest, set_with_same_value_is_allowed) {
- EngineOrFactory::set(FastValueBuilderFactory::get());
+ EngineOrFactory::set(DefaultTensorEngine::ref());
}
TEST(EngineOrFactoryOverrideTest, set_with_another_value_is_not_allowed) {
- EXPECT_THROW(EngineOrFactory::set(DefaultTensorEngine::ref()), vespalib::IllegalStateException);
+ EXPECT_THROW(EngineOrFactory::set(FastValueBuilderFactory::get()), vespalib::IllegalStateException);
}
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/eval/engine_or_factory/engine_or_factory_test.cpp b/eval/src/tests/eval/engine_or_factory/engine_or_factory_test.cpp
index 6cb9cc0c89c..3acfc40f5b5 100644
--- a/eval/src/tests/eval/engine_or_factory/engine_or_factory_test.cpp
+++ b/eval/src/tests/eval/engine_or_factory/engine_or_factory_test.cpp
@@ -9,16 +9,16 @@
using namespace vespalib::eval;
using namespace vespalib::tensor;
-TEST(EngineOrFactoryTest, default_is_default_tensor_engine) {
- EXPECT_EQ(EngineOrFactory::get().to_string(), "DefaultTensorEngine");
+TEST(EngineOrFactoryTest, default_is_fast_value_builder_factory) {
+ EXPECT_EQ(EngineOrFactory::get().to_string(), "FastValueBuilderFactory");
}
TEST(EngineOrFactoryTest, set_with_same_value_is_allowed) {
- EngineOrFactory::set(DefaultTensorEngine::ref());
+ EngineOrFactory::set(FastValueBuilderFactory::get());
}
TEST(EngineOrFactoryTest, set_with_another_value_is_not_allowed) {
- EXPECT_THROW(EngineOrFactory::set(FastValueBuilderFactory::get()), vespalib::IllegalStateException);
+ EXPECT_THROW(EngineOrFactory::set(DefaultTensorEngine::ref()), vespalib::IllegalStateException);
}
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/eval/value_type/value_type_test.cpp b/eval/src/tests/eval/value_type/value_type_test.cpp
index 77801f44bb8..d58adbbcef0 100644
--- a/eval/src/tests/eval/value_type/value_type_test.cpp
+++ b/eval/src/tests/eval/value_type/value_type_test.cpp
@@ -8,8 +8,6 @@
using namespace vespalib::eval;
-using CellType = ValueType::CellType;
-
const size_t npos = ValueType::Dimension::npos;
ValueType type(const vespalib::string &type_str) {
diff --git a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
index aaea8fdcb28..c59d9783648 100644
--- a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
+++ b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
@@ -8,6 +8,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/instruction/generic_concat.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -64,63 +65,6 @@ TensorSpec perform_simpletensor_concat(const TensorSpec &a, const TensorSpec &b,
return SimpleTensorEngine::ref().to_spec(*out);
}
-bool concat_address(const TensorSpec::Address &me, const TensorSpec::Address &other,
- const std::string &concat_dim, size_t my_offset,
- TensorSpec::Address &my_out, TensorSpec::Address &other_out)
-{
- my_out.insert_or_assign(concat_dim, my_offset);
- for (const auto &my_dim: me) {
- const auto & name = my_dim.first;
- const auto & label = my_dim.second;
- if (name == concat_dim) {
- my_out.insert_or_assign(name, label.index + my_offset);
- } else {
- auto pos = other.find(name);
- if ((pos == other.end()) || (pos->second == label)) {
- my_out.insert_or_assign(name, label);
- other_out.insert_or_assign(name, label);
- } else {
- return false;
- }
- }
- }
- return true;
-}
-
-bool concat_addresses(const TensorSpec::Address &a, const TensorSpec::Address &b,
- const std::string &concat_dim, size_t b_offset,
- TensorSpec::Address &a_out, TensorSpec::Address &b_out)
-{
- return concat_address(a, b, concat_dim, 0, a_out, b_out) &&
- concat_address(b, a, concat_dim, b_offset, b_out, a_out);
-}
-
-TensorSpec reference_concat(const TensorSpec &a, const TensorSpec &b, const std::string &concat_dim) {
- ValueType a_type = ValueType::from_spec(a.type());
- ValueType b_type = ValueType::from_spec(b.type());
- ValueType res_type = ValueType::concat(a_type, b_type, concat_dim);
- EXPECT_FALSE(res_type.is_error());
- size_t b_offset = 1;
- size_t concat_dim_index = a_type.dimension_index(concat_dim);
- if (concat_dim_index != ValueType::Dimension::npos) {
- const auto &dim = a_type.dimensions()[concat_dim_index];
- EXPECT_TRUE(dim.is_indexed());
- b_offset = dim.size;
- }
- TensorSpec result(res_type.to_spec());
- for (const auto &cell_a: a.cells()) {
- for (const auto &cell_b: b.cells()) {
- TensorSpec::Address addr_a;
- TensorSpec::Address addr_b;
- if (concat_addresses(cell_a.first, cell_b.first, concat_dim, b_offset, addr_a, addr_b)) {
- result.add(addr_a, cell_a.second);
- result.add(addr_b, cell_b.second);
- }
- }
- }
- return result;
-}
-
TensorSpec perform_generic_concat(const TensorSpec &a, const TensorSpec &b,
const std::string &concat_dim, const ValueBuilderFactory &factory)
{
@@ -138,7 +82,7 @@ TEST(GenericConcatTest, generic_reference_concat_works) {
const TensorSpec lhs = spec(concat_layouts[i], N());
const TensorSpec rhs = spec(concat_layouts[i + 1], Div16(N()));
SCOPED_TRACE(fmt("\n===\nin LHS: %s\nin RHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
- auto actual = reference_concat(lhs, rhs, "y");
+ auto actual = ReferenceOperations::concat(lhs, rhs, "y");
auto expect = perform_simpletensor_concat(lhs, rhs, "y");
EXPECT_EQ(actual, expect);
}
@@ -151,7 +95,7 @@ void test_generic_concat_with(const ValueBuilderFactory &factory) {
const TensorSpec rhs = spec(concat_layouts[i + 1], Div16(N()));
SCOPED_TRACE(fmt("\n===\nin LHS: %s\nin RHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
auto actual = perform_generic_concat(lhs, rhs, "y", factory);
- auto expect = reference_concat(lhs, rhs, "y");
+ auto expect = ReferenceOperations::concat(lhs, rhs, "y");
EXPECT_EQ(actual, expect);
}
}
@@ -202,7 +146,7 @@ TEST(GenericConcatTest, immediate_generic_concat_works) {
const TensorSpec rhs = spec(concat_layouts[i + 1], Div16(N()));
SCOPED_TRACE(fmt("\n===\nin LHS: %s\nin RHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
auto actual = immediate_generic_concat(lhs, rhs, "y");
- auto expect = reference_concat(lhs, rhs, "y");
+ auto expect = ReferenceOperations::concat(lhs, rhs, "y");
EXPECT_EQ(actual, expect);
}
}
diff --git a/eval/src/tests/instruction/generic_create/generic_create_test.cpp b/eval/src/tests/instruction/generic_create/generic_create_test.cpp
index e07db870ad2..42af4ba6621 100644
--- a/eval/src/tests/instruction/generic_create/generic_create_test.cpp
+++ b/eval/src/tests/instruction/generic_create/generic_create_test.cpp
@@ -5,6 +5,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/instruction/generic_create.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -53,6 +54,19 @@ bool operator< (const NumberedCellSpec &a, const NumberedCellSpec &b) {
return a.num < b.num;
}
+TensorSpec reference_create(const TensorSpec &a) {
+ std::vector<TensorSpec> children;
+ ReferenceOperations::CreateSpec spec;
+ for (const auto & [addr, value] : a.cells()) {
+ size_t child_idx = children.size();
+ spec.emplace(addr, child_idx);
+ TensorSpec child("double");
+ child.add({}, value);
+ children.push_back(child);
+ }
+ return ReferenceOperations::create(a.type(), spec, children);
+}
+
TensorSpec perform_generic_create(const TensorSpec &a, const ValueBuilderFactory &factory)
{
ValueType res_type = ValueType::from_spec(a.type());
@@ -80,12 +94,16 @@ void test_generic_create_with(const ValueBuilderFactory &factory) {
for (const auto & layout : create_layouts) {
TensorSpec full = spec(layout, N());
auto actual = perform_generic_create(full, factory);
- EXPECT_EQ(actual, full);
+ auto ref_spec = reference_create(full);
+ // use SimpleValue to add implicit cells with default value
+ auto expect = spec_from_value(*value_from_spec(ref_spec, SimpleValueBuilderFactory::get()));
+ EXPECT_EQ(actual, expect);
for (size_t n : {2, 3, 4, 5}) {
TensorSpec partial = remove_each(full, n);
actual = perform_generic_create(partial, factory);
- auto filled = spec_from_value(*value_from_spec(partial, SimpleValueBuilderFactory::get()));
- EXPECT_EQ(actual, filled);
+ ref_spec = reference_create(partial);
+ expect = spec_from_value(*value_from_spec(ref_spec, SimpleValueBuilderFactory::get()));
+ EXPECT_EQ(actual, expect);
}
}
}
diff --git a/eval/src/tests/instruction/generic_join/generic_join_test.cpp b/eval/src/tests/instruction/generic_join/generic_join_test.cpp
index 558f20d2e10..a81294c8d25 100644
--- a/eval/src/tests/instruction/generic_join/generic_join_test.cpp
+++ b/eval/src/tests/instruction/generic_join/generic_join_test.cpp
@@ -5,6 +5,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/instruction/generic_join.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -53,23 +54,6 @@ bool join_address(const TensorSpec::Address &a, const TensorSpec::Address &b, Te
return true;
}
-TensorSpec reference_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
- ValueType res_type = ValueType::join(ValueType::from_spec(a.type()), ValueType::from_spec(b.type()));
- EXPECT_FALSE(res_type.is_error());
- TensorSpec result(res_type.to_spec());
- for (const auto &cell_a: a.cells()) {
- for (const auto &cell_b: b.cells()) {
- TensorSpec::Address addr;
- if (join_address(cell_a.first, cell_b.first, addr) &&
- join_address(cell_b.first, cell_a.first, addr))
- {
- result.add(addr, function(cell_a.second, cell_b.second));
- }
- }
- }
- return result;
-}
-
TensorSpec perform_generic_join(const TensorSpec &a, const TensorSpec &b,
join_fun_t function, const ValueBuilderFactory &factory)
{
@@ -130,7 +114,7 @@ TEST(GenericJoinTest, generic_join_works_for_simple_and_fast_values) {
TensorSpec rhs = spec(join_layouts[i + 1], Div16(N()));
for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Div::f}) {
SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
- auto expect = reference_join(lhs, rhs, fun);
+ auto expect = ReferenceOperations::join(lhs, rhs, fun);
auto simple = perform_generic_join(lhs, rhs, fun, SimpleValueBuilderFactory::get());
auto fast = perform_generic_join(lhs, rhs, fun, FastValueBuilderFactory::get());
EXPECT_EQ(simple, expect);
@@ -154,7 +138,7 @@ TEST(GenericJoinTest, immediate_generic_join_works) {
TensorSpec rhs = spec(join_layouts[i + 1], Div16(N()));
for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Div::f}) {
SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
- auto expect = reference_join(lhs, rhs, fun);
+ auto expect = ReferenceOperations::join(lhs, rhs, fun);
auto actual = immediate_generic_join(lhs, rhs, fun);
EXPECT_EQ(actual, expect);
}
diff --git a/eval/src/tests/instruction/generic_map/generic_map_test.cpp b/eval/src/tests/instruction/generic_map/generic_map_test.cpp
index 63a9563a11b..ba6a1630777 100644
--- a/eval/src/tests/instruction/generic_map/generic_map_test.cpp
+++ b/eval/src/tests/instruction/generic_map/generic_map_test.cpp
@@ -5,6 +5,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/instruction/generic_map.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -30,16 +31,6 @@ std::vector<Layout> map_layouts = {
float_cells({x({"a","b","c"}),y(5),z({"i","j","k","l"})})
};
-TensorSpec reference_map(const TensorSpec &a, map_fun_t func) {
- ValueType res_type = ValueType::from_spec(a.type());
- EXPECT_FALSE(res_type.is_error());
- TensorSpec result(res_type.to_spec());
- for (const auto &cell: a.cells()) {
- result.add(cell.first, func(cell.second));
- }
- return result;
-}
-
TensorSpec perform_generic_map(const TensorSpec &a, map_fun_t func, const ValueBuilderFactory &factory)
{
auto lhs = value_from_spec(a, factory);
@@ -54,7 +45,7 @@ void test_generic_map_with(const ValueBuilderFactory &factory) {
ValueType lhs_type = ValueType::from_spec(lhs.type());
for (auto func : {operation::Floor::f, operation::Fabs::f, operation::Square::f, operation::Inv::f}) {
SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str()));
- auto expect = reference_map(lhs, func);
+ auto expect = ReferenceOperations::map(lhs, func);
auto actual = perform_generic_map(lhs, func, factory);
EXPECT_EQ(actual, expect);
}
@@ -82,7 +73,7 @@ TEST(GenericMapTest, immediate_generic_map_works) {
ValueType lhs_type = ValueType::from_spec(lhs.type());
for (auto func : {operation::Floor::f, operation::Fabs::f, operation::Square::f, operation::Inv::f}) {
SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str()));
- auto expect = reference_map(lhs, func);
+ auto expect = ReferenceOperations::map(lhs, func);
auto actual = immediate_generic_map(lhs, func, SimpleValueBuilderFactory::get());
EXPECT_EQ(actual, expect);
}
diff --git a/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp b/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
index 5166ef6ccc9..a43169a6959 100644
--- a/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
+++ b/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
@@ -5,6 +5,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/instruction/generic_merge.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -33,29 +34,6 @@ std::vector<Layout> merge_layouts = {
{x({"a","b","c"}),y(5)}, {x({"b","c","d"}),y(5)}
};
-
-TensorSpec reference_merge(const TensorSpec &a, const TensorSpec &b, join_fun_t fun) {
- ValueType res_type = ValueType::merge(ValueType::from_spec(a.type()),
- ValueType::from_spec(b.type()));
- EXPECT_FALSE(res_type.is_error());
- TensorSpec result(res_type.to_spec());
- for (const auto &cell: a.cells()) {
- auto other = b.cells().find(cell.first);
- if (other == b.cells().end()) {
- result.add(cell.first, cell.second);
- } else {
- result.add(cell.first, fun(cell.second, other->second));
- }
- }
- for (const auto &cell: b.cells()) {
- auto other = a.cells().find(cell.first);
- if (other == a.cells().end()) {
- result.add(cell.first, cell.second);
- }
- }
- return result;
-}
-
TensorSpec perform_generic_merge(const TensorSpec &a, const TensorSpec &b, join_fun_t fun, const ValueBuilderFactory &factory) {
Stash stash;
auto lhs = value_from_spec(a, factory);
@@ -72,7 +50,7 @@ void test_generic_merge_with(const ValueBuilderFactory &factory) {
TensorSpec rhs = spec(merge_layouts[i + 1], Div16(N()));
SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
for (auto fun: {operation::Add::f, operation::Mul::f, operation::Sub::f, operation::Max::f}) {
- auto expect = reference_merge(lhs, rhs, fun);
+ auto expect = ReferenceOperations::merge(lhs, rhs, fun);
auto actual = perform_generic_merge(lhs, rhs, fun, factory);
EXPECT_EQ(actual, expect);
}
@@ -102,7 +80,7 @@ TEST(GenericMergeTest, immediate_generic_merge_works) {
TensorSpec rhs = spec(merge_layouts[i + 1], Div16(N()));
SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
for (auto fun: {operation::Add::f, operation::Mul::f, operation::Sub::f, operation::Max::f}) {
- auto expect = reference_merge(lhs, rhs, fun);
+ auto expect = ReferenceOperations::merge(lhs, rhs, fun);
auto actual = immediate_generic_merge(lhs, rhs, fun);
EXPECT_EQ(actual, expect);
}
diff --git a/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp b/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp
index ef78d0cde68..18b1d6903dd 100644
--- a/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp
+++ b/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp
@@ -6,6 +6,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/instruction/generic_peek.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/overload.h>
@@ -36,55 +37,31 @@ std::vector<Layout> peek_layouts = {
using PeekSpec = GenericPeek::SpecMap;
-TensorSpec reference_peek(const TensorSpec &param, const vespalib::string &result_type, const PeekSpec &spec) {
- TensorSpec result(result_type);
- ValueType param_type = ValueType::from_spec(param.type());
- auto is_mapped_dim = [&](const vespalib::string &name) {
- size_t dim_idx = param_type.dimension_index(name);
- assert(dim_idx != ValueType::Dimension::npos);
- const auto &param_dim = param_type.dimensions()[dim_idx];
- return param_dim.is_mapped();
- };
- TensorSpec::Address addr;
+TensorSpec reference_peek(const TensorSpec &param, const PeekSpec &spec) {
+ std::vector<TensorSpec> children;
+ PeekSpec with_indexes;
for (const auto & [dim_name, label_or_child] : spec) {
+ const vespalib::string &dim = dim_name;
std::visit(vespalib::overload
{
- [&,&dim_name = dim_name](const TensorSpec::Label &label) {
- addr.emplace(dim_name, label);
+ [&](const TensorSpec::Label &label) {
+ with_indexes.emplace(dim, label);
},
- [&,&dim_name = dim_name](const size_t &child_value) {
+ [&](const size_t &child_value) {
// here, label_or_child is a size_t specifying the value
// we pretend a child produced
- if (is_mapped_dim(dim_name)) {
- // (but cast to signed first, to allow labels like the string "-2")
- addr.emplace(dim_name, vespalib::make_string("%zd", ssize_t(child_value)));
- } else {
- addr.emplace(dim_name, child_value);
- }
+ size_t child_idx = children.size();
+ TensorSpec child("double");
+ // (but cast to signed first, to allow labels like the string "-2")
+ child.add({}, ssize_t(child_value));
+ children.push_back(child);
+ with_indexes.emplace(dim, child_idx);
}
}, label_or_child);
}
- for (const auto &cell: param.cells()) {
- bool keep = true;
- TensorSpec::Address my_addr;
- for (const auto &binding: cell.first) {
- auto pos = addr.find(binding.first);
- if (pos == addr.end()) {
- my_addr.emplace(binding.first, binding.second);
- } else {
- if (!(pos->second == binding.second)) {
- keep = false;
- }
- }
- }
- if (keep) {
- result.add(my_addr, cell.second);
- }
- }
- return spec_from_value(*value_from_spec(result, SimpleValueBuilderFactory::get()));
+ return ReferenceOperations::peek(param, with_indexes, children);
}
-
TensorSpec perform_generic_peek(const TensorSpec &a, const ValueType &result_type,
PeekSpec spec, const ValueBuilderFactory &factory)
{
@@ -174,7 +151,9 @@ void verify_peek_equal(const TensorSpec &input,
}
if (reduce_dims.empty()) return;
ValueType result_type = param_type.reduce(reduce_dims);
- auto expect = reference_peek(input, result_type.to_spec(), spec);
+ auto ref_spec = reference_peek(input, spec);
+ // use SimpleValue to add implicit cells with default value
+ auto expect = spec_from_value(*value_from_spec(ref_spec, SimpleValueBuilderFactory::get()));
SCOPED_TRACE(fmt("peek input: %s\n peek spec: %s\n peek result %s\n",
input.to_string().c_str(),
to_str(spec).c_str(),
diff --git a/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp b/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
index d894d273f02..fa55406be3a 100644
--- a/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
+++ b/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
@@ -5,6 +5,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/instruction/generic_reduce.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -34,35 +35,6 @@ std::vector<Layout> layouts = {
float_cells({x({"a","b","c"}),y(5),z({"i","j","k","l"})})
};
-TensorSpec reference_reduce(const TensorSpec &a, const std::vector<vespalib::string> &dims, Aggr aggr) {
- Stash stash;
- ValueType res_type = ValueType::from_spec(a.type()).reduce(dims);
- EXPECT_FALSE(res_type.is_error());
- std::map<TensorSpec::Address,std::optional<Aggregator*>> my_map;
- for (const auto &cell: a.cells()) {
- TensorSpec::Address addr;
- for (const auto &dim: cell.first) {
- if (res_type.dimension_index(dim.first) != ValueType::Dimension::npos) {
- addr.insert_or_assign(dim.first, dim.second);
- }
- }
- auto [pos, is_empty] = my_map.emplace(addr, std::nullopt);
- if (is_empty) {
- pos->second = &Aggregator::create(aggr, stash);
- pos->second.value()->first(cell.second);
- } else {
- pos->second.value()->next(cell.second);
- }
- }
- TensorSpec result(res_type.to_spec());
- for (const auto &my_entry: my_map) {
- result.add(my_entry.first, my_entry.second.value()->result());
- }
- // use SimpleValue to add implicit cells with default value
- const auto &factory = SimpleValueBuilderFactory::get();
- return spec_from_value(*value_from_spec(result, factory));
-}
-
TensorSpec perform_generic_reduce(const TensorSpec &a, const std::vector<vespalib::string> &dims,
Aggr aggr, const ValueBuilderFactory &factory)
{
@@ -99,11 +71,14 @@ void test_generic_reduce_with(const ValueBuilderFactory &factory) {
TensorSpec input = spec(layout, Div16(N()));
for (Aggr aggr: {Aggr::SUM, Aggr::AVG, Aggr::MIN, Aggr::MAX}) {
for (const Domain &domain: layout) {
- auto expect = reference_reduce(input, {domain.dimension}, aggr);
+ auto ref_spec = ReferenceOperations::reduce(input, {domain.dimension}, aggr);
+ // use SimpleValue to add implicit cells with default value
+ auto expect = spec_from_value(*value_from_spec(ref_spec, SimpleValueBuilderFactory::get()));
auto actual = perform_generic_reduce(input, {domain.dimension}, aggr, factory);
EXPECT_EQ(actual, expect);
}
- auto expect = reference_reduce(input, {}, aggr);
+ auto ref_spec = ReferenceOperations::reduce(input, {}, aggr);
+ auto expect = spec_from_value(*value_from_spec(ref_spec, SimpleValueBuilderFactory::get()));
auto actual = perform_generic_reduce(input, {}, aggr, factory);
EXPECT_EQ(actual, expect);
}
@@ -130,11 +105,13 @@ TEST(GenericReduceTest, immediate_generic_reduce_works) {
TensorSpec input = spec(layout, Div16(N()));
for (Aggr aggr: {Aggr::SUM, Aggr::AVG, Aggr::MIN, Aggr::MAX}) {
for (const Domain &domain: layout) {
- auto expect = reference_reduce(input, {domain.dimension}, aggr);
+ auto ref_spec = ReferenceOperations::reduce(input, {domain.dimension}, aggr);
+ auto expect = spec_from_value(*value_from_spec(ref_spec, SimpleValueBuilderFactory::get()));
auto actual = immediate_generic_reduce(input, {domain.dimension}, aggr);
EXPECT_EQ(actual, expect);
}
- auto expect = reference_reduce(input, {}, aggr);
+ auto ref_spec = ReferenceOperations::reduce(input, {}, aggr);
+ auto expect = spec_from_value(*value_from_spec(ref_spec, SimpleValueBuilderFactory::get()));
auto actual = immediate_generic_reduce(input, {}, aggr);
EXPECT_EQ(actual, expect);
}
diff --git a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
index b2e30a8b78c..a7e6b8d807b 100644
--- a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
+++ b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
@@ -6,6 +6,7 @@
#include <vespa/eval/instruction/generic_rename.h>
#include <vespa/eval/eval/interpreted_function.h>
#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -98,20 +99,6 @@ vespalib::string rename_dimension(const vespalib::string &name, const FromTo &ft
return name;
}
-TensorSpec reference_rename(const TensorSpec &a, const FromTo &ft) {
- ValueType res_type = ValueType::from_spec(a.type()).rename(ft.from, ft.to);
- EXPECT_FALSE(res_type.is_error());
- TensorSpec result(res_type.to_spec());
- for (const auto &cell: a.cells()) {
- TensorSpec::Address addr;
- for (const auto &dim: cell.first) {
- addr.insert_or_assign(rename_dimension(dim.first, ft), dim.second);
- }
- result.add(addr, cell.second);
- }
- return result;
-}
-
TensorSpec perform_generic_rename(const TensorSpec &a,
const FromTo &ft, const ValueBuilderFactory &factory)
{
@@ -132,7 +119,7 @@ void test_generic_rename_with(const ValueBuilderFactory &factory) {
if (renamed_type.is_error()) continue;
// printf("type %s -> %s\n", lhs_type.to_spec().c_str(), renamed_type.to_spec().c_str());
SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str()));
- auto expect = reference_rename(lhs, from_to);
+ auto expect = ReferenceOperations::rename(lhs, from_to.from, from_to.to);
auto actual = perform_generic_rename(lhs, from_to, factory);
EXPECT_EQ(actual, expect);
}
@@ -165,7 +152,7 @@ TEST(GenericRenameTest, immediate_generic_rename_works) {
if (renamed_type.is_error()) continue;
// printf("type %s -> %s\n", lhs_type.to_spec().c_str(), renamed_type.to_spec().c_str());
SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str()));
- auto expect = reference_rename(lhs, from_to);
+ auto expect = ReferenceOperations::rename(lhs, from_to.from, from_to.to);
auto actual = immediate_generic_rename(lhs, from_to);
EXPECT_EQ(actual, expect);
}
diff --git a/eval/src/tests/streamed/value/CMakeLists.txt b/eval/src/tests/streamed/value/CMakeLists.txt
new file mode 100644
index 00000000000..d2ccced8c14
--- /dev/null
+++ b/eval/src/tests/streamed/value/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(eval_streamed_value_test_app TEST
+ SOURCES
+ streamed_value_test.cpp
+ DEPENDS
+ vespaeval
+ GTest::GTest
+)
+vespa_add_test(NAME eval_streamed_value_test_app COMMAND eval_streamed_value_test_app)
diff --git a/eval/src/tests/streamed/value/streamed_value_test.cpp b/eval/src/tests/streamed/value/streamed_value_test.cpp
new file mode 100644
index 00000000000..3de6ba0fb63
--- /dev/null
+++ b/eval/src/tests/streamed/value/streamed_value_test.cpp
@@ -0,0 +1,136 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/eval/streamed/streamed_value_builder_factory.h>
+#include <vespa/eval/eval/value_codec.h>
+#include <vespa/eval/instruction/generic_join.h>
+#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using namespace vespalib;
+using namespace vespalib::eval;
+using namespace vespalib::eval::instruction;
+using namespace vespalib::eval::test;
+
+using vespalib::make_string_short::fmt;
+
+using PA = std::vector<vespalib::stringref *>;
+using CPA = std::vector<const vespalib::stringref *>;
+
+std::vector<Layout> layouts = {
+ {},
+ {x(3)},
+ {x(3),y(5)},
+ {x(3),y(5),z(7)},
+ float_cells({x(3),y(5),z(7)}),
+ {x({"a","b","c"})},
+ {x({"a","b","c"}),y({"foo","bar"})},
+ {x({"a","b","c"}),y({"foo","bar"}),z({"i","j","k","l"})},
+ float_cells({x({"a","b","c"}),y({"foo","bar"}),z({"i","j","k","l"})}),
+ {x(3),y({"foo", "bar"}),z(7)},
+ {x({"a","b","c"}),y(5),z({"i","j","k","l"})},
+ float_cells({x({"a","b","c"}),y(5),z({"i","j","k","l"})})
+};
+
+std::vector<Layout> join_layouts = {
+ {}, {},
+ {x(5)}, {x(5)},
+ {x(5)}, {y(5)},
+ {x(5)}, {x(5),y(5)},
+ {y(3)}, {x(2),z(3)},
+ {x(3),y(5)}, {y(5),z(7)},
+ float_cells({x(3),y(5)}), {y(5),z(7)},
+ {x(3),y(5)}, float_cells({y(5),z(7)}),
+ float_cells({x(3),y(5)}), float_cells({y(5),z(7)}),
+ {x({"a","b","c"})}, {x({"a","b","c"})},
+ {x({"a","b","c"})}, {x({"a","b"})},
+ {x({"a","b","c"})}, {y({"foo","bar","baz"})},
+ {x({"a","b","c"})}, {x({"a","b","c"}),y({"foo","bar","baz"})},
+ {x({"a","b"}),y({"foo","bar","baz"})}, {x({"a","b","c"}),y({"foo","bar"})},
+ {x({"a","b"}),y({"foo","bar","baz"})}, {y({"foo","bar"}),z({"i","j","k","l"})},
+ float_cells({x({"a","b"}),y({"foo","bar","baz"})}), {y({"foo","bar"}),z({"i","j","k","l"})},
+ {x({"a","b"}),y({"foo","bar","baz"})}, float_cells({y({"foo","bar"}),z({"i","j","k","l"})}),
+ float_cells({x({"a","b"}),y({"foo","bar","baz"})}), float_cells({y({"foo","bar"}),z({"i","j","k","l"})}),
+ {x(3),y({"foo", "bar"})}, {y({"foo", "bar"}),z(7)},
+ {x({"a","b","c"}),y(5)}, {y(5),z({"i","j","k","l"})},
+ float_cells({x({"a","b","c"}),y(5)}), {y(5),z({"i","j","k","l"})},
+ {x({"a","b","c"}),y(5)}, float_cells({y(5),z({"i","j","k","l"})}),
+ float_cells({x({"a","b","c"}),y(5)}), float_cells({y(5),z({"i","j","k","l"})})
+};
+
+TensorSpec simple_tensor_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
+ Stash stash;
+ const auto &engine = SimpleTensorEngine::ref();
+ auto lhs = engine.from_spec(a);
+ auto rhs = engine.from_spec(b);
+ const auto &result = engine.join(*lhs, *rhs, function, stash);
+ return engine.to_spec(result);
+}
+
+TensorSpec streamed_value_new_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
+ Stash stash;
+ const auto &factory = StreamedValueBuilderFactory::get();
+ auto lhs = value_from_spec(a, factory);
+ auto rhs = value_from_spec(b, factory);
+ auto my_op = GenericJoin::make_instruction(lhs->type(), rhs->type(), function, factory, stash);
+ InterpretedFunction::EvalSingle single(factory, my_op);
+ return spec_from_value(single.eval(std::vector<Value::CREF>({*lhs,*rhs})));
+}
+
+TEST(StreamedValueTest, streamed_values_can_be_converted_from_and_to_tensor_spec) {
+ for (const auto &layout: layouts) {
+ TensorSpec expect = spec(layout, N());
+ std::unique_ptr<Value> value = value_from_spec(expect, StreamedValueBuilderFactory::get());
+ TensorSpec actual = spec_from_value(*value);
+ EXPECT_EQ(actual, expect);
+ }
+}
+
+TEST(StreamedValueTest, streamed_value_can_be_built_and_inspected) {
+ ValueType type = ValueType::from_spec("tensor<float>(x{},y[2],z{})");
+ const auto &factory = StreamedValueBuilderFactory::get();
+ std::unique_ptr<ValueBuilder<float>> builder = factory.create_value_builder<float>(type);
+ float seq = 0.0;
+ for (vespalib::string x: {"a", "b", "c"}) {
+ for (vespalib::string y: {"aa", "bb"}) {
+ std::vector<vespalib::stringref> addr = {x, y};
+ auto subspace = builder->add_subspace(addr);
+ EXPECT_EQ(subspace.size(), 2);
+ subspace[0] = seq + 1.0;
+ subspace[1] = seq + 5.0;
+ seq += 10.0;
+ }
+ seq += 100.0;
+ }
+ std::unique_ptr<Value> value = builder->build(std::move(builder));
+ EXPECT_EQ(value->index().size(), 6);
+ auto view = value->index().create_view({0});
+ vespalib::stringref query = "b";
+ vespalib::stringref label;
+ size_t subspace;
+ view->lookup(CPA{&query});
+ EXPECT_TRUE(view->next_result(PA{&label}, subspace));
+ EXPECT_EQ(label, "aa");
+ EXPECT_EQ(subspace, 2);
+ EXPECT_TRUE(view->next_result(PA{&label}, subspace));
+ EXPECT_EQ(label, "bb");
+ EXPECT_EQ(subspace, 3);
+ EXPECT_FALSE(view->next_result(PA{&label}, subspace));
+}
+
+TEST(StreamedValueTest, new_generic_join_works_for_streamed_values) {
+ ASSERT_TRUE((join_layouts.size() % 2) == 0);
+ for (size_t i = 0; i < join_layouts.size(); i += 2) {
+ TensorSpec lhs = spec(join_layouts[i], Div16(N()));
+ TensorSpec rhs = spec(join_layouts[i + 1], Div16(N()));
+ for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Max::f}) {
+ SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
+ auto expect = simple_tensor_join(lhs, rhs, fun);
+ auto actual = streamed_value_new_join(lhs, rhs, fun);
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/tensor/default_value_builder_factory/CMakeLists.txt b/eval/src/tests/tensor/default_value_builder_factory/CMakeLists.txt
deleted file mode 100644
index cd7f552ec28..00000000000
--- a/eval/src/tests/tensor/default_value_builder_factory/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_executable(eval_default_value_builder_factory_test_app TEST
- SOURCES
- default_value_builder_factory_test.cpp
- DEPENDS
- vespaeval
- GTest::GTest
-)
-vespa_add_test(NAME eval_default_value_builder_factory_test_app COMMAND eval_default_value_builder_factory_test_app )
diff --git a/eval/src/tests/tensor/default_value_builder_factory/default_value_builder_factory_test.cpp b/eval/src/tests/tensor/default_value_builder_factory/default_value_builder_factory_test.cpp
deleted file mode 100644
index bd18f3a2341..00000000000
--- a/eval/src/tests/tensor/default_value_builder_factory/default_value_builder_factory_test.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include <vespa/eval/eval/value.h>
-#include <vespa/eval/eval/value_codec.h>
-#include <vespa/eval/eval/tensor_spec.h>
-#include <vespa/eval/tensor/default_value_builder_factory.h>
-#include <vespa/eval/tensor/mixed/packed_mixed_tensor.h>
-#include <vespa/eval/tensor/sparse/sparse_tensor.h>
-#include <vespa/eval/tensor/dense/dense_tensor.h>
-#include <vespa/vespalib/gtest/gtest.h>
-
-using namespace vespalib;
-using namespace vespalib::eval;
-using namespace vespalib::tensor;
-using namespace vespalib::eval::packed_mixed_tensor;
-
-Value::UP v_of(const TensorSpec &spec) {
- return value_from_spec(spec, DefaultValueBuilderFactory::get());
-}
-
-using PA = std::vector<vespalib::stringref *>;
-using CPA = std::vector<const vespalib::stringref *>;
-
-TEST(DefaultValueBuilderFactoryTest, all_built_value_types_are_correct) {
- auto dbl = v_of(TensorSpec("double").add({}, 3.0));
- auto trivial = v_of(TensorSpec("tensor(x[1])").add({{"x",0}}, 7.0));
- auto dense = v_of(TensorSpec("tensor<float>(x[2],y[3])").add({{"x",1},{"y",2}}, 17.0));
- auto sparse = v_of(TensorSpec("tensor(x{},y{})").add({{"x","foo"},{"y","bar"}}, 31.0));
- auto mixed = v_of(TensorSpec("tensor<float>(x[2],y{})").add({{"x",1},{"y","quux"}}, 42.0));
-
- EXPECT_TRUE(dynamic_cast<DoubleValue *>(dbl.get()));
- EXPECT_TRUE(dynamic_cast<DenseTensorView *>(trivial.get()));
- EXPECT_TRUE(dynamic_cast<DenseTensorView *>(dense.get()));
- EXPECT_TRUE(dynamic_cast<SparseTensor *>(sparse.get()));
- EXPECT_TRUE(dynamic_cast<PackedMixedTensor *>(mixed.get()));
-
- EXPECT_EQ(dbl->as_double(), 3.0);
- EXPECT_EQ(trivial->cells().typify<double>()[0], 7.0);
- EXPECT_EQ(dense->cells().typify<float>()[5], 17.0);
- EXPECT_EQ(sparse->cells().typify<double>()[0], 31.0);
- EXPECT_EQ(mixed->cells().typify<float>()[1], 42.0);
-
- stringref y_look = "bar";
- stringref x_res = "xxx";
- auto view = sparse->index().create_view({1});
- view->lookup(CPA{&y_look});
- size_t ss = 12345;
- bool br = view->next_result(PA{&x_res}, ss);
- EXPECT_TRUE(br);
- EXPECT_EQ(ss, 0);
- EXPECT_EQ(x_res, "foo");
- br = view->next_result(PA{&x_res}, ss);
- EXPECT_FALSE(br);
-
- ss = 12345;
- view = mixed->index().create_view({});
- view->lookup({});
- br = view->next_result(PA{&x_res}, ss);
- EXPECT_TRUE(br);
- EXPECT_EQ(ss, 0);
- EXPECT_EQ(x_res, "quux");
-}
-
-GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/tensor/direct_sparse_tensor_builder/direct_sparse_tensor_builder_test.cpp b/eval/src/tests/tensor/direct_sparse_tensor_builder/direct_sparse_tensor_builder_test.cpp
index d9d4c221164..bcee6471f76 100644
--- a/eval/src/tests/tensor/direct_sparse_tensor_builder/direct_sparse_tensor_builder_test.cpp
+++ b/eval/src/tests/tensor/direct_sparse_tensor_builder/direct_sparse_tensor_builder_test.cpp
@@ -8,6 +8,7 @@
using namespace vespalib::tensor;
using namespace vespalib::tensor::sparse;
using vespalib::eval::TensorSpec;
+using vespalib::eval::CellType;
using vespalib::eval::ValueType;
void
@@ -36,7 +37,7 @@ assertCellValue(double expValue, const TensorAddress &address,
bool found = tensor.index().lookup_address(addressRef, idx);
EXPECT_TRUE(found);
auto cells = tensor.cells();
- if (EXPECT_TRUE(cells.type == ValueType::CellType::DOUBLE)) {
+ if (EXPECT_TRUE(cells.type == CellType::DOUBLE)) {
auto arr = cells.typify<double>();
EXPECT_EQUAL(expValue, arr[idx]);
}
diff --git a/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp b/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp
index 816923bb87c..f11678d3ee7 100644
--- a/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp
+++ b/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp
@@ -36,7 +36,6 @@
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/optimize_tensor_function.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
-#include <vespa/eval/tensor/default_value_builder_factory.h>
#include <vespa/vespalib/util/benchmark_timer.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/objects/nbostream.h>
@@ -95,7 +94,7 @@ template <typename ...Ds> void add_cells(TensorSpec &spec, double &seq, TensorSp
}
template <typename ...Ds> TensorSpec make_spec(double seq, const Ds &...ds) {
- TensorSpec spec(ValueType::tensor_type({ds...}, ValueType::CellType::FLOAT).to_spec());
+ TensorSpec spec(ValueType::tensor_type({ds...}, CellType::FLOAT).to_spec());
add_cells(spec, seq, TensorSpec::Address(), ds...);
return spec;
}
@@ -354,6 +353,7 @@ MyParam::~MyParam() = default;
struct EvalOp {
using UP = std::unique_ptr<EvalOp>;
+ Stash my_stash;
const Impl &impl;
MyParam my_param;
std::vector<Value::UP> values;
@@ -361,8 +361,8 @@ struct EvalOp {
EvalSingle single;
EvalOp(const EvalOp &) = delete;
EvalOp &operator=(const EvalOp &) = delete;
- EvalOp(Instruction op, const std::vector<CREF<TensorSpec>> &stack_spec, const Impl &impl_in)
- : impl(impl_in), my_param(), values(), stack(), single(impl.engine, op)
+ EvalOp(Stash &&stash_in, Instruction op, const std::vector<CREF<TensorSpec>> &stack_spec, const Impl &impl_in)
+ : my_stash(std::move(stash_in)), impl(impl_in), my_param(), values(), stack(), single(impl.engine, op)
{
for (const TensorSpec &spec: stack_spec) {
values.push_back(impl.create_value(spec));
@@ -371,14 +371,51 @@ struct EvalOp {
stack.push_back(*value.get());
}
}
- EvalOp(Instruction op, const TensorSpec &p0, const Impl &impl_in)
- : impl(impl_in), my_param(p0, impl), values(), stack(), single(impl.engine, op, my_param)
+ EvalOp(Stash &&stash_in, Instruction op, const TensorSpec &p0, const Impl &impl_in)
+ : my_stash(std::move(stash_in)), impl(impl_in), my_param(p0, impl), values(), stack(), single(impl.engine, op, my_param)
{
}
TensorSpec result() { return impl.create_spec(single.eval(stack)); }
- double estimate_cost_us() {
- auto actual = [&](){ single.eval(stack); };
- return BenchmarkTimer::benchmark(actual, budget) * 1000.0 * 1000.0;
+ size_t suggest_loop_cnt() {
+ size_t loop_cnt = 1;
+ auto my_loop = [&](){
+ for (size_t i = 0; i < loop_cnt; ++i) {
+ single.eval(stack);
+ }
+ };
+ for (;;) {
+ vespalib::BenchmarkTimer timer(0.0);
+ for (size_t i = 0; i < 5; ++i) {
+ timer.before();
+ my_loop();
+ timer.after();
+ }
+ double min_time = timer.min_time();
+ if (min_time > 0.004) {
+ break;
+ } else {
+ loop_cnt *= 2;
+ }
+ }
+ return std::max(loop_cnt, size_t(8));
+ }
+ double estimate_cost_us(size_t self_loop_cnt, size_t ref_loop_cnt) {
+ size_t loop_cnt = ((self_loop_cnt * 128) < ref_loop_cnt) ? self_loop_cnt : ref_loop_cnt;
+ assert((loop_cnt % 8) == 0);
+ auto my_loop = [&](){
+ for (size_t i = 0; (i + 7) < loop_cnt; i += 8) {
+ for (size_t j = 0; j < 8; ++j) {
+ single.eval(stack);
+ }
+ }
+ };
+ BenchmarkTimer timer(budget);
+ while (timer.has_budget()) {
+ timer.before();
+ my_loop();
+ timer.after();
+ }
+ return timer.min_time() * 1000.0 * 1000.0 / double(loop_cnt);
}
};
@@ -396,8 +433,12 @@ void benchmark(const vespalib::string &desc, const std::vector<EvalOp::UP> &list
}
}
BenchmarkResult result(desc, list.size());
+ std::vector<size_t> loop_cnt(list.size());
for (const auto &eval: list) {
- double time = eval->estimate_cost_us();
+ loop_cnt[eval->impl.order] = eval->suggest_loop_cnt();
+ }
+ for (const auto &eval: list) {
+ double time = eval->estimate_cost_us(loop_cnt[eval->impl.order], loop_cnt[1]);
result.sample(eval->impl.order, time);
fprintf(stderr, " %s(%s): %10.3f us\n", eval->impl.name.c_str(), eval->impl.short_name.c_str(), time);
}
@@ -420,9 +461,10 @@ void benchmark_join(const vespalib::string &desc, const TensorSpec &lhs,
ASSERT_FALSE(res_type.is_error());
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_join(lhs_type, rhs_type, function, stash);
+ Stash my_stash;
+ auto op = impl.create_join(lhs_type, rhs_type, function, my_stash);
std::vector<CREF<TensorSpec>> stack_spec({lhs, rhs});
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
benchmark(desc, list);
}
@@ -439,9 +481,10 @@ void benchmark_reduce(const vespalib::string &desc, const TensorSpec &lhs,
ASSERT_FALSE(res_type.is_error());
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_reduce(lhs_type, aggr, dims, stash);
+ Stash my_stash;
+ auto op = impl.create_reduce(lhs_type, aggr, dims, my_stash);
std::vector<CREF<TensorSpec>> stack_spec({lhs});
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
benchmark(desc, list);
}
@@ -459,9 +502,10 @@ void benchmark_rename(const vespalib::string &desc, const TensorSpec &lhs,
ASSERT_FALSE(res_type.is_error());
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_rename(lhs_type, from, to, stash);
+ Stash my_stash;
+ auto op = impl.create_rename(lhs_type, from, to, my_stash);
std::vector<CREF<TensorSpec>> stack_spec({lhs});
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
benchmark(desc, list);
}
@@ -480,9 +524,10 @@ void benchmark_merge(const vespalib::string &desc, const TensorSpec &lhs,
ASSERT_FALSE(res_type.is_error());
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_merge(lhs_type, rhs_type, function, stash);
+ Stash my_stash;
+ auto op = impl.create_merge(lhs_type, rhs_type, function, my_stash);
std::vector<CREF<TensorSpec>> stack_spec({lhs, rhs});
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
benchmark(desc, list);
}
@@ -496,9 +541,10 @@ void benchmark_map(const vespalib::string &desc, const TensorSpec &lhs, operatio
ASSERT_FALSE(lhs_type.is_error());
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_map(lhs_type, function, stash);
+ Stash my_stash;
+ auto op = impl.create_map(lhs_type, function, my_stash);
std::vector<CREF<TensorSpec>> stack_spec({lhs});
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
benchmark(desc, list);
}
@@ -517,9 +563,10 @@ void benchmark_concat(const vespalib::string &desc, const TensorSpec &lhs,
ASSERT_FALSE(res_type.is_error());
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_concat(lhs_type, rhs_type, dimension, stash);
+ Stash my_stash;
+ auto op = impl.create_concat(lhs_type, rhs_type, dimension, my_stash);
std::vector<CREF<TensorSpec>> stack_spec({lhs, rhs});
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
benchmark(desc, list);
}
@@ -536,10 +583,11 @@ void benchmark_tensor_create(const vespalib::string &desc, const TensorSpec &pro
}
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_tensor_create(proto_type, proto, stash);
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ Stash my_stash;
+ auto op = impl.create_tensor_create(proto_type, proto, my_stash);
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
- benchmark(desc, list);
+ benchmark(desc, list);
}
//-----------------------------------------------------------------------------
@@ -550,8 +598,9 @@ void benchmark_tensor_lambda(const vespalib::string &desc, const ValueType &type
ASSERT_FALSE(p0_type.is_error());
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_tensor_lambda(type, function, p0_type, stash);
- list.push_back(std::make_unique<EvalOp>(op, p0, impl));
+ Stash my_stash;
+ auto op = impl.create_tensor_lambda(type, function, p0_type, my_stash);
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, p0, impl));
}
benchmark(desc, list);
}
@@ -571,8 +620,9 @@ void benchmark_tensor_peek(const vespalib::string &desc, const TensorSpec &lhs,
}
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
- auto op = impl.create_tensor_peek(type, peek_spec, stash);
- list.push_back(std::make_unique<EvalOp>(op, stack_spec, impl));
+ Stash my_stash;
+ auto op = impl.create_tensor_peek(type, peek_spec, my_stash);
+ list.push_back(std::make_unique<EvalOp>(std::move(my_stash), op, stack_spec, impl));
}
benchmark(desc, list);
}
@@ -610,11 +660,11 @@ void benchmark_encode_decode(const vespalib::string &desc, const TensorSpec &pro
BenchmarkResult encode_result(desc + " <encode>", impl_list.size());
BenchmarkResult decode_result(desc + " <decode>", impl_list.size());
for (const Impl &impl: impl_list) {
- constexpr size_t loop_cnt = 16;
+ constexpr size_t loop_cnt = 32;
auto value = impl.create_value(proto);
BenchmarkTimer encode_timer(2 * budget);
BenchmarkTimer decode_timer(2 * budget);
- while (encode_timer.has_budget() || decode_timer.has_budget()) {
+ while (encode_timer.has_budget()) {
std::array<vespalib::nbostream, loop_cnt> data;
std::array<Value::UP, loop_cnt> object;
encode_timer.before();
diff --git a/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp b/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp
index fce7ccc6411..efab0571e62 100644
--- a/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp
+++ b/eval/src/tests/tensor/onnx_wrapper/onnx_wrapper_test.cpp
@@ -163,7 +163,7 @@ TEST(OnnxTest, simple_onnx_model_can_be_evaluated)
ctx.bind_param(2, bias);
ctx.eval();
auto cells = output.cells();
- EXPECT_EQ(cells.type, ValueType::CellType::FLOAT);
+ EXPECT_EQ(cells.type, CellType::FLOAT);
EXPECT_EQ(cells.size, 1);
EXPECT_EQ(GetCell::from(cells, 0), 79.0);
//-------------------------------------------------------------------------
@@ -209,7 +209,7 @@ TEST(OnnxTest, dynamic_onnx_model_can_be_evaluated)
ctx.bind_param(2, bias);
ctx.eval();
auto cells = output.cells();
- EXPECT_EQ(cells.type, ValueType::CellType::FLOAT);
+ EXPECT_EQ(cells.type, CellType::FLOAT);
EXPECT_EQ(cells.size, 1);
EXPECT_EQ(GetCell::from(cells, 0), 79.0);
//-------------------------------------------------------------------------
@@ -255,7 +255,7 @@ TEST(OnnxTest, int_types_onnx_model_can_be_evaluated)
ctx.bind_param(2, bias);
ctx.eval();
auto cells = output.cells();
- EXPECT_EQ(cells.type, ValueType::CellType::DOUBLE);
+ EXPECT_EQ(cells.type, CellType::DOUBLE);
EXPECT_EQ(cells.size, 1);
EXPECT_EQ(GetCell::from(cells, 0), 79.0);
//-------------------------------------------------------------------------
diff --git a/eval/src/tests/tensor/packed_mappings/CMakeLists.txt b/eval/src/tests/tensor/packed_mappings/CMakeLists.txt
deleted file mode 100644
index 2d11755a0c5..00000000000
--- a/eval/src/tests/tensor/packed_mappings/CMakeLists.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-vespa_add_executable(eval_packed_mappings_test_app TEST
- SOURCES
- packed_mappings_test.cpp
- DEPENDS
- vespaeval
- GTest::GTest
-)
-vespa_add_test(NAME eval_packed_mappings_test_app COMMAND eval_packed_mappings_test_app)
-
-vespa_add_executable(eval_packed_mixed_test_app TEST
- SOURCES
- packed_mixed_test.cpp
- DEPENDS
- vespaeval
- GTest::GTest
-)
-vespa_add_test(NAME eval_packed_mixed_test_app COMMAND eval_packed_mixed_test_app)
diff --git a/eval/src/tests/tensor/packed_mappings/packed_mappings_test.cpp b/eval/src/tests/tensor/packed_mappings/packed_mappings_test.cpp
deleted file mode 100644
index be25dd7f444..00000000000
--- a/eval/src/tests/tensor/packed_mappings/packed_mappings_test.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include <vespa/eval/tensor/mixed/packed_labels.h>
-#include <vespa/eval/tensor/mixed/packed_mappings.h>
-#include <vespa/eval/tensor/mixed/packed_mappings_builder.h>
-#include <vespa/eval/tensor/mixed/packed_mixed_tensor.h>
-#include <vespa/eval/tensor/mixed/packed_mixed_tensor_builder.h>
-#include <vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h>
-#include <vespa/vespalib/gtest/gtest.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <set>
-
-using namespace vespalib::eval;
-using namespace vespalib::eval::packed_mixed_tensor;
-
-namespace {
-
-uint32_t random_range(uint32_t from, uint32_t to) {
- assert(from + 1 < to);
- int unif = rand() % (to - from);
- return from + unif;
-}
-
-const char *mixed_tensor_types[] = {
- "tensor<float>(x{})",
- "tensor<float>(a{},b{},c{},d{},e{},f{})",
- "tensor<float>(x{},y{})",
- "tensor<float>(x{},z[3])",
- "tensor<float>(w[5],x{},y{},z[3])"
-};
-
-const char *float_tensor_types[] = {
- "tensor<float>(x{})",
- "tensor<float>(x{},y{})",
- "tensor<float>(x{},z[3])",
- "tensor<float>(w[5],x{},y{},z[3])",
- "tensor<float>(z[2])"
-};
-
- vespalib::string label1(""),
- label2("foo"),
- label3("bar");
- vespalib::string label4("foobar"),
- label5("barfoo"),
- label6("other");
- vespalib::string label7("long text number one"),
- label8("long text number two"),
- label9("long text number three");
-
-std::vector<vespalib::stringref>
-generate_random_address(uint32_t dims)
-{
- std::vector<vespalib::stringref> foo(dims, label1);
- for (auto & ref : foo) {
- size_t pct = random_range(0, 100);
- if (pct < 5) { ref = label1; }
- else if (pct < 30) { ref = label2; }
- else if (pct < 55) { ref = label3; }
- else if (pct < 65) { ref = label4; }
- else if (pct < 75) { ref = label5; }
- else if (pct < 85) { ref = label6; }
- else if (pct < 90) { ref = label7; }
- else if (pct < 95) { ref = label8; }
- else { ref = label9; }
- }
- return foo;
-}
-
-} // namespace <unnamed>
-
-class MappingsBuilderTest : public ::testing::Test {
-public:
- std::unique_ptr<PackedMappingsBuilder> builder;
- std::unique_ptr<PackedMappings> built;
-
- MappingsBuilderTest() = default;
-
- virtual ~MappingsBuilderTest() = default;
-
- void build_and_compare() {
- ASSERT_TRUE(builder);
- built = builder->build_mappings();
- ASSERT_TRUE(built);
- EXPECT_EQ(builder->num_mapped_dims(), built->num_mapped_dims());
- EXPECT_EQ(builder->size(), built->size());
- for (size_t idx = 0; idx < built->size(); ++idx) {
- std::vector<vespalib::stringref> got(builder->num_mapped_dims());
- built->fill_address_by_sortid(idx, got);
- printf("Got address:");
- for (auto ref : got) {
- printf(" '%s'", ref.data());
- }
- uint32_t subspace = built->subspace_of_address(got);
- uint32_t original = builder->add_mapping_for(got);
- printf(" -> %u\n", original);
- EXPECT_EQ(subspace, original);
- }
- }
-};
-
-TEST_F(MappingsBuilderTest, empty_mapping)
-{
- for (uint32_t dims : { 0, 1, 2, 3 }) {
- builder = std::make_unique<PackedMappingsBuilder>(dims);
- build_and_compare();
- }
-}
-
-TEST_F(MappingsBuilderTest, just_one)
-{
- vespalib::string label("foobar");
- for (uint32_t dims : { 0, 1, 2, 3, 7 }) {
- builder = std::make_unique<PackedMappingsBuilder>(dims);
- std::vector<vespalib::stringref> foo(dims, label);
- uint32_t idx = builder->add_mapping_for(foo);
- EXPECT_EQ(idx, 0);
- build_and_compare();
- }
-}
-
-TEST_F(MappingsBuilderTest, some_random)
-{
- for (uint32_t dims : { 1, 2, 5 }) {
- builder = std::make_unique<PackedMappingsBuilder>(dims);
- uint32_t cnt = random_range(dims*5, dims*20);
- printf("Generate %u addresses for %u dims\n", cnt, dims);
- for (uint32_t i = 0; i < cnt; ++i) {
- auto foo = generate_random_address(dims);
- uint32_t idx = builder->add_mapping_for(foo);
- EXPECT_LE(idx, i);
- }
- build_and_compare();
- }
-}
-
-class MixedBuilderTest : public ::testing::Test {
-public:
- std::unique_ptr<PackedMixedTensorBuilder<float>> builder;
- std::unique_ptr<Value> built;
-
- MixedBuilderTest() = default;
-
- virtual ~MixedBuilderTest() = default;
-
- size_t expected_value = 0;
-
- void build_and_compare(size_t expect_size) {
- built.reset(nullptr);
- EXPECT_FALSE(built);
- ASSERT_TRUE(builder);
- built = builder->build(std::move(builder));
- EXPECT_FALSE(builder);
- ASSERT_TRUE(built);
- EXPECT_EQ(built->index().size(), expect_size);
- auto cells = built->cells().typify<float>();
- for (float f : cells) {
- float expect = ++expected_value;
- EXPECT_EQ(f, expect);
- }
- }
-};
-
-TEST_F(MixedBuilderTest, empty_mapping)
-{
- for (auto type_spec : mixed_tensor_types) {
- ValueType type = ValueType::from_spec(type_spec);
- size_t dims = type.count_mapped_dimensions();
- size_t dsss = type.dense_subspace_size();
- EXPECT_GT(dims, 0);
- EXPECT_GT(dsss, 0);
- builder = std::make_unique<PackedMixedTensorBuilder<float>>(type, dims, dsss, 3);
- build_and_compare(0);
- }
-}
-
-TEST_F(MixedBuilderTest, just_one)
-{
- size_t counter = 0;
- for (auto type_spec : float_tensor_types) {
- ValueType type = ValueType::from_spec(type_spec);
- size_t dims = type.count_mapped_dimensions();
- size_t dsss = type.dense_subspace_size();
- EXPECT_GT(dsss, 0);
- builder = std::make_unique<PackedMixedTensorBuilder<float>>(type, dims, dsss, 3);
- auto address = generate_random_address(dims);
- auto ref = builder->add_subspace(address);
- EXPECT_EQ(ref.size(), dsss);
- for (size_t i = 0; i < ref.size(); ++i) {
- ref[i] = ++counter;
- }
- build_and_compare(1);
- }
-}
-
-TEST_F(MixedBuilderTest, some_random)
-{
- size_t counter = 0;
- for (auto type_spec : mixed_tensor_types) {
- ValueType type = ValueType::from_spec(type_spec);
- uint32_t dims = type.count_mapped_dimensions();
- uint32_t dsss = type.dense_subspace_size();
- EXPECT_GT(dims, 0);
- EXPECT_GT(dsss, 0);
- builder = std::make_unique<PackedMixedTensorBuilder<float>>(type, dims, dsss, 3);
-
- uint32_t cnt = random_range(dims*5, dims*20);
- printf("MixBuild: generate %u addresses for %u dims\n", cnt, dims);
- std::set<std::vector<vespalib::stringref>> seen;
- for (uint32_t i = 0; i < cnt; ++i) {
- auto address = generate_random_address(dims);
- if (seen.insert(address).second) {
- auto ref = builder->add_subspace(address);
- EXPECT_EQ(ref.size(), dsss);
- for (size_t j = 0; j < ref.size(); ++j) {
- ref[j] = ++counter;
- }
- }
- }
- printf("MixBuild: generated %zu unique addresses\n", seen.size());
- build_and_compare(seen.size());
- }
-}
-
-GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/tensor/packed_mappings/packed_mixed_test.cpp b/eval/src/tests/tensor/packed_mappings/packed_mixed_test.cpp
deleted file mode 100644
index e8c57384335..00000000000
--- a/eval/src/tests/tensor/packed_mappings/packed_mixed_test.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include <vespa/eval/eval/simple_value.h>
-#include <vespa/eval/eval/value_codec.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
-#include <vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h>
-#include <vespa/vespalib/gtest/gtest.h>
-
-using namespace vespalib::eval;
-using namespace vespalib::eval::test;
-
-using PA = std::vector<vespalib::stringref *>;
-using CPA = std::vector<const vespalib::stringref *>;
-
-std::vector<Layout> layouts = {
- {},
- {x(3)},
- {x(3),y(5)},
- {x(3),y(5),z(7)},
- float_cells({x(3),y(5),z(7)}),
- {x({"a","b","c"})},
- {x({"a","b","c"}),y({"foo","bar"})},
- {x({"a","b","c"}),y({"foo","bar"}),z({"i","j","k","l"})},
- float_cells({x({"a","b","c"}),y({"foo","bar"}),z({"i","j","k","l"})}),
- {x(3),y({"foo", "bar"}),z(7)},
- {x({"a","b","c"}),y(5),z({"i","j","k","l"})},
- float_cells({x({"a","b","c"}),y(5),z({"i","j","k","l"})})
-};
-
-TEST(PackedMixedTest, packed_mixed_tensors_can_be_converted_from_and_to_tensor_spec) {
- for (const auto &layout: layouts) {
- TensorSpec expect = spec(layout, N());
- std::unique_ptr<Value> value = value_from_spec(expect, PackedMixedTensorBuilderFactory::get());
- TensorSpec actual = spec_from_value(*value);
- EXPECT_EQ(actual, expect);
- }
-}
-
-TEST(PackedMixedTest, packed_mixed_tensors_can_be_built_and_inspected) {
- ValueType type = ValueType::from_spec("tensor<float>(x{},y[2],z{})");
- const auto & factory = PackedMixedTensorBuilderFactory::get();
- std::unique_ptr<ValueBuilder<float>> builder = factory.create_value_builder<float>(type);
- float seq = 0.0;
- for (vespalib::string x: {"a", "b", "c"}) {
- for (vespalib::string y: {"aa", "bb"}) {
- std::vector<vespalib::stringref> addr = {x, y};
- auto subspace = builder->add_subspace(addr);
- EXPECT_EQ(subspace.size(), 2);
- subspace[0] = seq + 1.0;
- subspace[1] = seq + 5.0;
- seq += 10.0;
- }
- seq += 100.0;
- }
- std::unique_ptr<Value> value = builder->build(std::move(builder));
- EXPECT_EQ(value->index().size(), 6);
- auto view = value->index().create_view({0});
- vespalib::stringref query = "b";
- vespalib::stringref label;
- size_t subspace;
- view->lookup(CPA{&query});
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "aa");
- EXPECT_EQ(subspace, 2);
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "bb");
- EXPECT_EQ(subspace, 3);
- EXPECT_FALSE(view->next_result(PA{&label}, subspace));
-
- query = "c";
- view->lookup(CPA{&query});
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "aa");
- EXPECT_EQ(subspace, 4);
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "bb");
- EXPECT_EQ(subspace, 5);
- EXPECT_FALSE(view->next_result(PA{&label}, subspace));
-
- query = "notpresent";
- view->lookup(CPA{&query});
- EXPECT_FALSE(view->next_result(PA{&label}, subspace));
-
- view = value->index().create_view({1});
- query = "aa";
- view->lookup(CPA{&query});
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "a");
- EXPECT_EQ(subspace, 0);
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "b");
- EXPECT_EQ(subspace, 2);
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "c");
- EXPECT_EQ(subspace, 4);
- EXPECT_FALSE(view->next_result(PA{&label}, subspace));
-
- query = "bb";
- view->lookup(CPA{&query});
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "a");
- EXPECT_EQ(subspace, 1);
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "b");
- EXPECT_EQ(subspace, 3);
- EXPECT_TRUE(view->next_result(PA{&label}, subspace));
- EXPECT_EQ(label, "c");
- EXPECT_EQ(subspace, 5);
- EXPECT_FALSE(view->next_result(PA{&label}, subspace));
-
- query = "notpresent";
- view->lookup(CPA{&query});
- EXPECT_FALSE(view->next_result(PA{&label}, subspace));
-
- view = value->index().create_view({0,1});
- vespalib::stringref query_x = "b";
- vespalib::stringref query_y = "bb";
- CPA addr = {&query_x, &query_y};
- view->lookup(addr);
- EXPECT_TRUE(view->next_result({}, subspace));
- EXPECT_EQ(subspace, 3);
- EXPECT_FALSE(view->next_result({}, subspace));
-
- view = value->index().create_view({});
- vespalib::stringref label_x;
- vespalib::stringref label_y;
- view->lookup({});
-
- const std::vector<vespalib::stringref*> out({&label_x, &label_y});
- EXPECT_TRUE(view->next_result(out, subspace));
- EXPECT_EQ(label_x, "a");
- EXPECT_EQ(label_y, "aa");
- EXPECT_EQ(subspace, 0);
- EXPECT_TRUE(view->next_result(out, subspace));
- EXPECT_EQ(label_x, "a");
- EXPECT_EQ(label_y, "bb");
- EXPECT_EQ(subspace, 1);
- EXPECT_TRUE(view->next_result(out, subspace));
- EXPECT_EQ(label_x, "b");
- EXPECT_EQ(label_y, "aa");
- EXPECT_EQ(subspace, 2);
- EXPECT_TRUE(view->next_result(out, subspace));
- EXPECT_EQ(label_x, "b");
- EXPECT_EQ(label_y, "bb");
- EXPECT_EQ(subspace, 3);
- EXPECT_TRUE(view->next_result(out, subspace));
- EXPECT_EQ(label_x, "c");
- EXPECT_EQ(label_y, "aa");
- EXPECT_EQ(subspace, 4);
- EXPECT_TRUE(view->next_result(out, subspace));
- EXPECT_EQ(label_x, "c");
- EXPECT_EQ(label_y, "bb");
- EXPECT_EQ(subspace, 5);
- EXPECT_FALSE(view->next_result(out, subspace));
-}
-
-
-GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/tensor/partial_remove/partial_remove_test.cpp b/eval/src/tests/tensor/partial_remove/partial_remove_test.cpp
index e182fffa890..5af2396f5ec 100644
--- a/eval/src/tests/tensor/partial_remove/partial_remove_test.cpp
+++ b/eval/src/tests/tensor/partial_remove/partial_remove_test.cpp
@@ -124,20 +124,31 @@ expect_partial_remove(const TensorSpec& input, const TensorSpec& remove, const T
}
TEST(PartialRemoveTest, remove_where_address_is_not_fully_specified) {
- auto input = TensorSpec("tensor(x{},y{})").
+ auto input_sparse = TensorSpec("tensor(x{},y{})").
add({{"x", "a"},{"y", "c"}}, 3.0).
add({{"x", "a"},{"y", "d"}}, 5.0).
add({{"x", "b"},{"y", "c"}}, 7.0);
- expect_partial_remove(input,TensorSpec("tensor(x{})").add({{"x", "a"}}, 1.0),
+ expect_partial_remove(input_sparse, TensorSpec("tensor(x{})").add({{"x", "a"}}, 1.0),
TensorSpec("tensor(x{},y{})").add({{"x", "b"},{"y", "c"}}, 7.0));
- expect_partial_remove(input, TensorSpec("tensor(y{})").add({{"y", "c"}}, 1.0),
+ expect_partial_remove(input_sparse, TensorSpec("tensor(y{})").add({{"y", "c"}}, 1.0),
TensorSpec("tensor(x{},y{})").add({{"x", "a"},{"y", "d"}}, 5.0));
- expect_partial_remove(input, TensorSpec("tensor(y{})").add({{"y", "d"}}, 1.0),
+ expect_partial_remove(input_sparse, TensorSpec("tensor(y{})").add({{"y", "d"}}, 1.0),
TensorSpec("tensor(x{},y{})").add({{"x", "a"},{"y", "c"}}, 3.0)
.add({{"x", "b"},{"y", "c"}}, 7.0));
+
+ auto input_mixed = TensorSpec("tensor(x{},y{},z[1])").
+ add({{"x", "a"},{"y", "c"},{"z", 0}}, 3.0).
+ add({{"x", "a"},{"y", "d"},{"z", 0}}, 5.0).
+ add({{"x", "b"},{"y", "c"},{"z", 0}}, 7.0);
+
+ expect_partial_remove(input_mixed,TensorSpec("tensor(x{})").add({{"x", "a"}}, 1.0),
+ TensorSpec("tensor(x{},y{},z[1])").add({{"x", "b"},{"y", "c"},{"z", 0}}, 7.0));
+
+ expect_partial_remove(input_mixed, TensorSpec("tensor(y{})").add({{"y", "c"}}, 1.0),
+ TensorSpec("tensor(x{},y{},z[1])").add({{"x", "a"},{"y", "d"},{"z", 0}}, 5.0));
}
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp b/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp
index 233aff0e425..6468f50a00e 100644
--- a/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp
+++ b/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp
@@ -3,11 +3,13 @@
#include <vespa/eval/eval/test/tensor_conformance.h>
#include <vespa/eval/eval/simple_tensor_engine.h>
#include <vespa/eval/eval/simple_value.h>
+#include <vespa/eval/streamed/streamed_value_builder_factory.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/vespalib/util/stringfmt.h>
using vespalib::eval::SimpleValueBuilderFactory;
+using vespalib::eval::StreamedValueBuilderFactory;
using vespalib::eval::FastValueBuilderFactory;
using vespalib::eval::SimpleTensorEngine;
using vespalib::eval::test::TensorConformance;
@@ -29,6 +31,10 @@ TEST("require that SimpleValue implementation passes all conformance tests") {
TEST_DO(TensorConformance::run_tests(module_src_path, SimpleValueBuilderFactory::get()));
}
+TEST("require that StreamedValue implementation passes all conformance tests") {
+ TEST_DO(TensorConformance::run_tests(module_src_path, StreamedValueBuilderFactory::get()));
+}
+
TEST("require that FastValue implementation passes all conformance tests") {
TEST_DO(TensorConformance::run_tests(module_src_path, FastValueBuilderFactory::get()));
}
diff --git a/eval/src/vespa/eval/CMakeLists.txt b/eval/src/vespa/eval/CMakeLists.txt
index ee9a793bba0..952640195b1 100644
--- a/eval/src/vespa/eval/CMakeLists.txt
+++ b/eval/src/vespa/eval/CMakeLists.txt
@@ -7,9 +7,9 @@ vespa_add_library(vespaeval
$<TARGET_OBJECTS:eval_eval_test>
$<TARGET_OBJECTS:eval_eval_value_cache>
$<TARGET_OBJECTS:eval_gp>
+ $<TARGET_OBJECTS:eval_streamed>
$<TARGET_OBJECTS:eval_tensor>
$<TARGET_OBJECTS:eval_tensor_dense>
- $<TARGET_OBJECTS:eval_tensor_mixed>
$<TARGET_OBJECTS:eval_tensor_serialization>
$<TARGET_OBJECTS:eval_tensor_sparse>
INSTALL lib64
diff --git a/eval/src/vespa/eval/eval/cell_type.cpp b/eval/src/vespa/eval/eval/cell_type.cpp
index e5729c547b0..365a3f59a56 100644
--- a/eval/src/vespa/eval/eval/cell_type.cpp
+++ b/eval/src/vespa/eval/eval/cell_type.cpp
@@ -1,3 +1,19 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "cell_type.h"
+#include <stdio.h>
+#include <cstdlib>
+#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/vespalib/util/stringfmt.h>
+
+using vespalib::make_string_short::fmt;
+
+namespace vespalib::eval {
+
+void
+CellTypeUtils::bad_argument(uint32_t id)
+{
+ throw IllegalArgumentException(fmt("Unknown CellType id=%u", id));
+}
+
+}
diff --git a/eval/src/vespa/eval/eval/cell_type.h b/eval/src/vespa/eval/eval/cell_type.h
index 0e878f26f47..49114d04bfe 100644
--- a/eval/src/vespa/eval/eval/cell_type.h
+++ b/eval/src/vespa/eval/eval/cell_type.h
@@ -3,7 +3,7 @@
#pragma once
#include <vespa/vespalib/util/typify.h>
-#include <cstdlib>
+#include <cstdint>
namespace vespalib::eval {
@@ -25,6 +25,26 @@ template <typename CT> inline CellType get_cell_type();
template <> inline CellType get_cell_type<double>() { return CellType::DOUBLE; }
template <> inline CellType get_cell_type<float>() { return CellType::FLOAT; }
+struct CellTypeUtils {
+ static void bad_argument [[ noreturn ]] (uint32_t id);
+
+ static constexpr uint32_t alignment(CellType cell_type) {
+ switch (cell_type) {
+ case CellType::DOUBLE: return sizeof(double);
+ case CellType::FLOAT: return sizeof(float);
+ }
+ bad_argument((uint32_t)cell_type);
+ }
+
+ static constexpr size_t mem_size(CellType cell_type, size_t sz) {
+ switch (cell_type) {
+ case CellType::DOUBLE: return sz * sizeof(double);
+ case CellType::FLOAT: return sz * sizeof(float);
+ }
+ bad_argument((uint32_t)cell_type);
+ }
+};
+
struct TypifyCellType {
template <typename T> using Result = TypifyResultType<T>;
template <typename F> static decltype(auto) resolve(CellType value, F &&f) {
@@ -32,7 +52,7 @@ struct TypifyCellType {
case CellType::DOUBLE: return f(Result<double>());
case CellType::FLOAT: return f(Result<float>());
}
- abort();
+ CellTypeUtils::bad_argument((uint32_t)value);
}
};
diff --git a/eval/src/vespa/eval/eval/engine_or_factory.cpp b/eval/src/vespa/eval/eval/engine_or_factory.cpp
index 4a95a57e10e..36251820c23 100644
--- a/eval/src/vespa/eval/eval/engine_or_factory.cpp
+++ b/eval/src/vespa/eval/eval/engine_or_factory.cpp
@@ -12,8 +12,6 @@
#include <vespa/eval/instruction/generic_reduce.h>
#include <vespa/eval/instruction/generic_rename.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
-#include <vespa/eval/tensor/default_value_builder_factory.h>
-#include <vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h>
#include <vespa/vespalib/data/memory.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/exceptions.h>
@@ -26,7 +24,7 @@ using namespace vespalib::eval::instruction;
namespace vespalib::eval {
-EngineOrFactory EngineOrFactory::_default{tensor::DefaultTensorEngine::ref()};
+EngineOrFactory EngineOrFactory::_default{FastValueBuilderFactory::get()};
EngineOrFactory
@@ -173,12 +171,6 @@ EngineOrFactory::to_string() const
if (&factory() == &SimpleValueBuilderFactory::get()) {
return "SimpleValueBuilderFactory";
}
- if (&factory() == &tensor::DefaultValueBuilderFactory::get()) {
- return "DefaultValueBuilderFactory";
- }
- if (&factory() == &PackedMixedTensorBuilderFactory::get()) {
- return "PackedMixedTensorBuilderFactory";
- }
}
return "???";
}
diff --git a/eval/src/vespa/eval/eval/fast_sparse_map.cpp b/eval/src/vespa/eval/eval/fast_sparse_map.cpp
index 2e95934286c..e5ffbb5c515 100644
--- a/eval/src/vespa/eval/eval/fast_sparse_map.cpp
+++ b/eval/src/vespa/eval/eval/fast_sparse_map.cpp
@@ -7,6 +7,12 @@ namespace vespalib::eval {
FastSparseMap::~FastSparseMap() = default;
+FastSparseMap&
+FastSparseMap::operator=(const FastSparseMap& rhs) = default;
+
+FastSparseMap&
+FastSparseMap::operator=(FastSparseMap&& rhs) = default;
+
const FastSparseMap::HashedLabel FastSparseMap::empty_label;
}
diff --git a/eval/src/vespa/eval/eval/fast_sparse_map.h b/eval/src/vespa/eval/eval/fast_sparse_map.h
index 0d7597a19a0..99e01e8c823 100644
--- a/eval/src/vespa/eval/eval/fast_sparse_map.h
+++ b/eval/src/vespa/eval/eval/fast_sparse_map.h
@@ -97,6 +97,9 @@ public:
}
~FastSparseMap();
+ FastSparseMap& operator=(const FastSparseMap& rhs);
+ FastSparseMap& operator=(FastSparseMap&& rhs);
+
MemoryUsage estimate_extra_memory_usage() const {
MemoryUsage extra_usage;
size_t map_self_size = sizeof(_map);
diff --git a/eval/src/vespa/eval/eval/fast_value.hpp b/eval/src/vespa/eval/eval/fast_value.hpp
index ff94f94efbc..9914378cc9e 100644
--- a/eval/src/vespa/eval/eval/fast_value.hpp
+++ b/eval/src/vespa/eval/eval/fast_value.hpp
@@ -390,20 +390,12 @@ FastValueIndex::sparse_only_merge(const ValueType &res_type, const Fun &fun,
const FastValueIndex &lhs, const FastValueIndex &rhs,
ConstArrayRef<LCT> lhs_cells, ConstArrayRef<RCT> rhs_cells, Stash &stash)
{
- auto &result = stash.create<FastValue<OCT>>(res_type, lhs.map.num_dims(), 1, lhs.map.size()+rhs.map.size());
- lhs.map.each_map_entry([&](auto lhs_subspace, auto hash)
- {
- auto idx = result.my_index.map.add_mapping(lhs.map.make_addr(lhs_subspace), hash);
- if (__builtin_expect((idx == result.my_cells.size), true)) {
- auto rhs_subspace = rhs.map.lookup(hash);
- if (rhs_subspace != FastSparseMap::npos()) {
- auto cell_value = fun(lhs_cells[lhs_subspace], rhs_cells[rhs_subspace]);
- result.my_cells.push_back_fast(cell_value);
- } else {
- result.my_cells.push_back_fast(lhs_cells[lhs_subspace]);
- }
- }
- });
+ size_t guess_size = lhs.map.size() + rhs.map.size();
+ auto &result = stash.create<FastValue<OCT>>(res_type, lhs.map.num_dims(), 1, guess_size);
+ result.my_index = lhs;
+ for (auto val : lhs_cells) {
+ result.my_cells.push_back_fast(val);
+ }
rhs.map.each_map_entry([&](auto rhs_subspace, auto hash)
{
auto lhs_subspace = lhs.map.lookup(hash);
@@ -412,9 +404,11 @@ FastValueIndex::sparse_only_merge(const ValueType &res_type, const Fun &fun,
if (__builtin_expect((idx == result.my_cells.size), true)) {
result.my_cells.push_back_fast(rhs_cells[rhs_subspace]);
}
+ } else {
+ auto cell_value = fun(lhs_cells[lhs_subspace], rhs_cells[rhs_subspace]);
+ *result.my_cells.get(lhs_subspace) = cell_value;
}
});
-
return result;
}
diff --git a/eval/src/vespa/eval/eval/llvm/llvm_wrapper.cpp b/eval/src/vespa/eval/eval/llvm/llvm_wrapper.cpp
index ad182115054..f6c09f94fc9 100644
--- a/eval/src/vespa/eval/eval/llvm/llvm_wrapper.cpp
+++ b/eval/src/vespa/eval/eval/llvm/llvm_wrapper.cpp
@@ -253,7 +253,7 @@ struct FunctionBuilder : public NodeVisitor, public NodeTraverser {
llvm::Value *eval_fun = builder.CreateIntToPtr(builder.getInt64((uint64_t)eval_ptr), eval_funptr_t, "inject_eval");
llvm::Value *ctx = builder.CreateIntToPtr(builder.getInt64((uint64_t)forest), builder.getVoidTy()->getPointerTo(), "inject_ctx");
if (pass_params == PassParams::ARRAY) {
- push(builder.CreateCall(llvm::cast<llvm::FunctionType>(eval_fun->getType()->getPointerElementType()),
+ push(builder.CreateCall(llvm::cast<llvm::FunctionType>(eval_fun->getType()->getPointerElementType()),
eval_fun, {ctx, params[0]}, "call_eval"));
} else {
assert(pass_params == PassParams::LAZY);
diff --git a/eval/src/vespa/eval/eval/simple_tensor.cpp b/eval/src/vespa/eval/eval/simple_tensor.cpp
index 64b2b6f8865..98e3bc325cb 100644
--- a/eval/src/vespa/eval/eval/simple_tensor.cpp
+++ b/eval/src/vespa/eval/eval/simple_tensor.cpp
@@ -18,7 +18,6 @@ using Cells = SimpleTensor::Cells;
using IndexList = std::vector<size_t>;
using Label = SimpleTensor::Label;
using CellRef = std::reference_wrapper<const Cell>;
-using CellType = ValueType::CellType;
namespace {
diff --git a/eval/src/vespa/eval/eval/simple_value.cpp b/eval/src/vespa/eval/eval/simple_value.cpp
index 766a4f1eb23..17faa635941 100644
--- a/eval/src/vespa/eval/eval/simple_value.cpp
+++ b/eval/src/vespa/eval/eval/simple_value.cpp
@@ -3,8 +3,6 @@
#include "simple_value.h"
#include "inline_operation.h"
#include <vespa/vespalib/util/typify.h>
-#include <vespa/vespalib/util/visit_ranges.h>
-#include <vespa/vespalib/util/overload.h>
#include <vespa/vespalib/stllike/hash_map.hpp>
#include <vespa/log/log.h>
diff --git a/eval/src/vespa/eval/eval/tensor_function.cpp b/eval/src/vespa/eval/eval/tensor_function.cpp
index 77ca6c1b8f0..614ef8389d8 100644
--- a/eval/src/vespa/eval/eval/tensor_function.cpp
+++ b/eval/src/vespa/eval/eval/tensor_function.cpp
@@ -125,7 +125,7 @@ void op_tensor_create(State &state, uint64_t param) {
const Create &self = unwrap_param<Create>(param);
TensorSpec spec(self.result_type().to_spec());
size_t i = 0;
- for (auto pos = self.spec().rbegin(); pos != self.spec().rend(); ++pos) {
+ for (auto pos = self.map().rbegin(); pos != self.map().rend(); ++pos) {
spec.add(pos->first, state.peek(i++).as_double());
}
const Value &result = *state.stash.create<Value::UP>(state.engine.from_spec(spec));
@@ -180,7 +180,7 @@ void op_tensor_peek(State &state, uint64_t param) {
const Peek &self = unwrap_param<Peek>(param);
TensorSpec::Address addr;
size_t child_cnt = 0;
- for (auto pos = self.spec().rbegin(); pos != self.spec().rend(); ++pos) {
+ for (auto pos = self.map().rbegin(); pos != self.map().rend(); ++pos) {
std::visit(vespalib::overload
{
[&](const TensorSpec::Label &label) {
@@ -388,21 +388,27 @@ Concat::visit_self(vespalib::ObjectVisitor &visitor) const
void
Create::push_children(std::vector<Child::CREF> &children) const
{
- for (const auto &cell: _spec) {
+ for (const auto &cell: _map) {
children.emplace_back(cell.second);
}
}
+Create::Spec
+Create::make_spec() const
+{
+ Spec generic_spec;
+ size_t child_idx = 0;
+ for (const auto & kv : map()) {
+ generic_spec[kv.first] = child_idx++;
+ }
+ return generic_spec;
+}
+
Instruction
Create::compile_self(EngineOrFactory engine, Stash &stash) const
{
if (engine.is_factory()) {
- std::map<TensorSpec::Address, size_t> generic_spec;
- size_t child_idx = 0;
- for (const auto & kv : spec()) {
- generic_spec[kv.first] = child_idx++;
- }
- return instruction::GenericCreate::make_instruction(result_type(), generic_spec, engine.factory(), stash);
+ return instruction::GenericCreate::make_instruction(result_type(), make_spec(), engine.factory(), stash);
}
return Instruction(op_tensor_create, wrap_param<Create>(*this));
}
@@ -410,7 +416,7 @@ Create::compile_self(EngineOrFactory engine, Stash &stash) const
void
Create::visit_children(vespalib::ObjectVisitor &visitor) const
{
- for (const auto &cell: _spec) {
+ for (const auto &cell: _map) {
::visit(visitor, ::vespalib::eval::as_string(cell.first), cell.second.get());
}
}
@@ -487,7 +493,7 @@ void
Peek::push_children(std::vector<Child::CREF> &children) const
{
children.emplace_back(_param);
- for (const auto &dim: _spec) {
+ for (const auto &dim: _map) {
std::visit(vespalib::overload
{
[&](const Child &child) {
@@ -498,23 +504,29 @@ Peek::push_children(std::vector<Child::CREF> &children) const
}
}
+Peek::Spec
+Peek::make_spec() const
+{
+ Spec generic_spec;
+ size_t child_idx = 0;
+ for (const auto & [dim_name, label_or_child] : map()) {
+ std::visit(vespalib::overload {
+ [&,&dim_name = dim_name](const TensorSpec::Label &label) {
+ generic_spec.emplace(dim_name, label);
+ },
+ [&,&dim_name = dim_name](const TensorFunction::Child &) {
+ generic_spec.emplace(dim_name, child_idx++);
+ }
+ }, label_or_child);
+ }
+ return generic_spec;
+}
+
Instruction
Peek::compile_self(EngineOrFactory engine, Stash &stash) const
{
if (engine.is_factory()) {
- instruction::GenericPeek::SpecMap generic_spec;
- size_t child_idx = 0;
- for (const auto & [dim_name, label_or_child] : spec()) {
- std::visit(vespalib::overload {
- [&,&dim_name = dim_name](const TensorSpec::Label &label) {
- generic_spec.emplace(dim_name, label);
- },
- [&,&dim_name = dim_name](const TensorFunction::Child &) {
- generic_spec.emplace(dim_name, child_idx++);
- }
- }, label_or_child);
- }
- return instruction::GenericPeek::make_instruction(param_type(), result_type(), generic_spec, engine.factory(), stash);
+ return instruction::GenericPeek::make_instruction(param_type(), result_type(), make_spec(), engine.factory(), stash);
}
return Instruction(op_tensor_peek, wrap_param<Peek>(*this));
}
@@ -523,7 +535,7 @@ void
Peek::visit_children(vespalib::ObjectVisitor &visitor) const
{
::visit(visitor, "param", _param.get());
- for (const auto &dim: _spec) {
+ for (const auto &dim: _map) {
std::visit(vespalib::overload
{
[&](const TensorSpec::Label &label) {
diff --git a/eval/src/vespa/eval/eval/tensor_function.h b/eval/src/vespa/eval/eval/tensor_function.h
index d6158f8eb4a..3c4eb6c53a4 100644
--- a/eval/src/vespa/eval/eval/tensor_function.h
+++ b/eval/src/vespa/eval/eval/tensor_function.h
@@ -310,16 +310,19 @@ class Create : public Node
{
using Super = Node;
private:
- std::map<TensorSpec::Address, Child> _spec;
+ std::map<TensorSpec::Address, Child> _map;
public:
Create(const ValueType &result_type_in, const std::map<TensorSpec::Address, TensorFunction::CREF> &spec_in)
- : Super(result_type_in), _spec()
+ : Super(result_type_in), _map()
{
for (const auto &cell: spec_in) {
- _spec.emplace(cell.first, Child(cell.second));
+ _map.emplace(cell.first, Child(cell.second));
}
}
- const std::map<TensorSpec::Address, Child> &spec() const { return _spec; }
+ const std::map<TensorSpec::Address, Child> &map() const { return _map; }
+ // mapping from cell address to index of child that computes the cell value
+ using Spec = std::map<TensorSpec::Address, size_t>;
+ Spec make_spec() const;
bool result_is_mutable() const override { return true; }
InterpretedFunction::Instruction compile_self(EngineOrFactory engine, Stash &stash) const final override;
void push_children(std::vector<Child::CREF> &children) const final override;
@@ -359,25 +362,30 @@ public:
using MyLabel = std::variant<TensorSpec::Label, Child>;
private:
Child _param;
- std::map<vespalib::string, MyLabel> _spec;
+ std::map<vespalib::string, MyLabel> _map;
public:
Peek(const ValueType &result_type_in, const TensorFunction &param,
const std::map<vespalib::string, std::variant<TensorSpec::Label, TensorFunction::CREF>> &spec)
- : Super(result_type_in), _param(param), _spec()
+ : Super(result_type_in), _param(param), _map()
{
for (const auto &dim: spec) {
std::visit(vespalib::overload
{
[&](const TensorSpec::Label &label) {
- _spec.emplace(dim.first, label);
+ _map.emplace(dim.first, label);
},
[&](const TensorFunction::CREF &ref) {
- _spec.emplace(dim.first, ref.get());
+ _map.emplace(dim.first, ref.get());
}
}, dim.second);
}
}
- const std::map<vespalib::string, MyLabel> &spec() const { return _spec; }
+ const std::map<vespalib::string, MyLabel> &map() const { return _map; }
+ // a verbatim label or the index of a child that computes the label value:
+ using LabelOrChildIndex = std::variant<TensorSpec::Label, size_t>;
+ // mapping from dimension name to verbatim label or child index:
+ using Spec = std::map<vespalib::string, LabelOrChildIndex>;
+ Spec make_spec() const;
const ValueType &param_type() const { return _param.get().result_type(); }
bool result_is_mutable() const override { return true; }
InterpretedFunction::Instruction compile_self(EngineOrFactory engine, Stash &stash) const final override;
diff --git a/eval/src/vespa/eval/eval/test/CMakeLists.txt b/eval/src/vespa/eval/eval/test/CMakeLists.txt
index 6e88beab9b7..f3b0750d503 100644
--- a/eval/src/vespa/eval/eval/test/CMakeLists.txt
+++ b/eval/src/vespa/eval/eval/test/CMakeLists.txt
@@ -3,6 +3,7 @@ vespa_add_library(eval_eval_test OBJECT
SOURCES
eval_fixture.cpp
eval_spec.cpp
+ reference_operations.cpp
tensor_conformance.cpp
test_io.cpp
value_compare.cpp
diff --git a/eval/src/vespa/eval/eval/test/reference_operations.cpp b/eval/src/vespa/eval/eval/test/reference_operations.cpp
new file mode 100644
index 00000000000..89b99526d55
--- /dev/null
+++ b/eval/src/vespa/eval/eval/test/reference_operations.cpp
@@ -0,0 +1,287 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "reference_operations.h"
+#include <vespa/vespalib/util/overload.h>
+#include <vespa/vespalib/util/visit_ranges.h>
+#include <vespa/vespalib/util/stash.h>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <cassert>
+
+namespace vespalib::eval {
+
+namespace {
+
+bool concat_address(const TensorSpec::Address &me, const TensorSpec::Address &other,
+ const std::string &concat_dim, size_t my_offset,
+ TensorSpec::Address &my_out, TensorSpec::Address &other_out)
+{
+ my_out.insert_or_assign(concat_dim, my_offset);
+ for (const auto &my_dim: me) {
+ const auto & name = my_dim.first;
+ const auto & label = my_dim.second;
+ if (name == concat_dim) {
+ my_out.insert_or_assign(name, label.index + my_offset);
+ } else {
+ auto pos = other.find(name);
+ if ((pos == other.end()) || (pos->second == label)) {
+ my_out.insert_or_assign(name, label);
+ other_out.insert_or_assign(name, label);
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool concat_addresses(const TensorSpec::Address &a, const TensorSpec::Address &b,
+ const std::string &concat_dim, size_t b_offset,
+ TensorSpec::Address &a_out, TensorSpec::Address &b_out)
+{
+ return concat_address(a, b, concat_dim, 0, a_out, b_out) &&
+ concat_address(b, a, concat_dim, b_offset, b_out, a_out);
+}
+
+double value_from_child(const TensorSpec &child) {
+ double sum = 0.0;
+ for (const auto & [addr, value] : child.cells()) {
+ sum += value;
+ }
+ return sum;
+}
+
+bool join_address(const TensorSpec::Address &a, const TensorSpec::Address &b, TensorSpec::Address &addr) {
+ for (const auto &dim_a: a) {
+ auto pos_b = b.find(dim_a.first);
+ if ((pos_b != b.end()) && !(pos_b->second == dim_a.second)) {
+ return false;
+ }
+ addr.insert_or_assign(dim_a.first, dim_a.second);
+ }
+ return true;
+}
+
+vespalib::string rename_dimension(const vespalib::string &name, const std::vector<vespalib::string> &from, const std::vector<vespalib::string> &to) {
+ for (size_t i = 0; i < from.size(); ++i) {
+ if (name == from[i]) {
+ return to[i];
+ }
+ }
+ return name;
+}
+
+} // namespace <unnamed>
+
+
+TensorSpec ReferenceOperations::concat(const TensorSpec &a, const TensorSpec &b, const std::string &concat_dim) {
+ ValueType a_type = ValueType::from_spec(a.type());
+ ValueType b_type = ValueType::from_spec(b.type());
+ ValueType res_type = ValueType::concat(a_type, b_type, concat_dim);
+ TensorSpec result(res_type.to_spec());
+ if (res_type.is_error()) {
+ return result;
+ }
+ size_t b_offset = 1;
+ size_t concat_dim_index = a_type.dimension_index(concat_dim);
+ if (concat_dim_index != ValueType::Dimension::npos) {
+ const auto &dim = a_type.dimensions()[concat_dim_index];
+ assert(dim.is_indexed()); // type resolving (above) should catch this
+ b_offset = dim.size;
+ }
+ for (const auto &cell_a: a.cells()) {
+ for (const auto &cell_b: b.cells()) {
+ TensorSpec::Address addr_a;
+ TensorSpec::Address addr_b;
+ if (concat_addresses(cell_a.first, cell_b.first, concat_dim, b_offset, addr_a, addr_b)) {
+ result.add(addr_a, cell_a.second);
+ result.add(addr_b, cell_b.second);
+ }
+ }
+ }
+ return result;
+}
+
+
+TensorSpec ReferenceOperations::create(const vespalib::string &type, const CreateSpec &spec, const std::vector<TensorSpec> &children) {
+ TensorSpec result(type);
+ if (ValueType::from_spec(type).is_error()) {
+ return result;
+ }
+ for (const auto & [addr, child_idx] : spec) {
+ assert(child_idx < children.size());
+ const auto &child = children[child_idx];
+ double val = value_from_child(child);
+ result.add(addr, val);
+ }
+ return result;
+}
+
+
+TensorSpec ReferenceOperations::join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
+ ValueType res_type = ValueType::join(ValueType::from_spec(a.type()), ValueType::from_spec(b.type()));
+ TensorSpec result(res_type.to_spec());
+ if (res_type.is_error()) {
+ return result;
+ }
+ for (const auto &cell_a: a.cells()) {
+ for (const auto &cell_b: b.cells()) {
+ TensorSpec::Address addr;
+ if (join_address(cell_a.first, cell_b.first, addr) &&
+ join_address(cell_b.first, cell_a.first, addr))
+ {
+ result.add(addr, function(cell_a.second, cell_b.second));
+ }
+ }
+ }
+ return result;
+}
+
+
+TensorSpec ReferenceOperations::map(const TensorSpec &a, map_fun_t func) {
+ ValueType res_type = ValueType::from_spec(a.type());
+ TensorSpec result(res_type.to_spec());
+ if (res_type.is_error()) {
+ return result;
+ }
+ for (const auto & [ addr, value ]: a.cells()) {
+ result.add(addr, func(value));
+ }
+ return result;
+}
+
+
+TensorSpec ReferenceOperations::merge(const TensorSpec &a, const TensorSpec &b, join_fun_t fun) {
+ ValueType res_type = ValueType::merge(ValueType::from_spec(a.type()),
+ ValueType::from_spec(b.type()));
+ TensorSpec result(res_type.to_spec());
+ if (res_type.is_error()) {
+ return result;
+ }
+ for (const auto & [ addr, value ]: a.cells()) {
+ auto other = b.cells().find(addr);
+ if (other == b.cells().end()) {
+ result.add(addr, value);
+ } else {
+ result.add(addr, fun(value, other->second));
+ }
+ }
+ for (const auto & [ addr, value ]: b.cells()) {
+ auto other = a.cells().find(addr);
+ if (other == a.cells().end()) {
+ result.add(addr, value);
+ }
+ }
+ return result;
+}
+
+
+TensorSpec ReferenceOperations::peek(const TensorSpec &param, const PeekSpec &peek_spec, const std::vector<TensorSpec> &children) {
+ if (peek_spec.empty()) {
+ return TensorSpec(ValueType::error_type().to_spec());
+ }
+ std::vector<vespalib::string> peek_dims;
+ for (const auto & [dim_name, label_or_child] : peek_spec) {
+ peek_dims.push_back(dim_name);
+ }
+ ValueType param_type = ValueType::from_spec(param.type());
+ ValueType result_type = param_type.reduce(peek_dims);
+ TensorSpec result(result_type.to_spec());
+ if (result_type.is_error()) {
+ return result;
+ }
+ auto is_mapped_dim = [&](const vespalib::string &name) {
+ size_t dim_idx = param_type.dimension_index(name);
+ assert(dim_idx != ValueType::Dimension::npos);
+ const auto &param_dim = param_type.dimensions()[dim_idx];
+ return param_dim.is_mapped();
+ };
+ TensorSpec::Address addr;
+ for (const auto & [dim_name, label_or_child] : peek_spec) {
+ const vespalib::string &dim = dim_name;
+ std::visit(vespalib::overload
+ {
+ [&](const TensorSpec::Label &label) {
+ addr.emplace(dim, label);
+ },
+ [&](const size_t &child_idx) {
+ assert(child_idx < children.size());
+ const auto &child = children[child_idx];
+ double child_value = value_from_child(child);
+ if (is_mapped_dim(dim)) {
+ addr.emplace(dim, vespalib::make_string("%zd", int64_t(child_value)));
+ } else {
+ addr.emplace(dim, child_value);
+ }
+ }
+ }, label_or_child);
+ }
+ for (const auto &cell: param.cells()) {
+ bool keep = true;
+ TensorSpec::Address my_addr;
+ for (const auto &binding: cell.first) {
+ auto pos = addr.find(binding.first);
+ if (pos == addr.end()) {
+ my_addr.emplace(binding.first, binding.second);
+ } else {
+ if (!(pos->second == binding.second)) {
+ keep = false;
+ }
+ }
+ }
+ if (keep) {
+ result.add(my_addr, cell.second);
+ }
+ }
+ return result;
+}
+
+
+TensorSpec ReferenceOperations::reduce(const TensorSpec &a, const std::vector<vespalib::string> &dims, Aggr aggr) {
+ ValueType res_type = ValueType::from_spec(a.type()).reduce(dims);
+ TensorSpec result(res_type.to_spec());
+ if (res_type.is_error()) {
+ return result;
+ }
+ Stash stash;
+ std::map<TensorSpec::Address,std::optional<Aggregator*>> my_map;
+ for (const auto &cell: a.cells()) {
+ TensorSpec::Address addr;
+ for (const auto &dim: cell.first) {
+ if (res_type.dimension_index(dim.first) != ValueType::Dimension::npos) {
+ addr.insert_or_assign(dim.first, dim.second);
+ }
+ }
+ auto [pos, is_empty] = my_map.emplace(addr, std::nullopt);
+ if (is_empty) {
+ pos->second = &Aggregator::create(aggr, stash);
+ pos->second.value()->first(cell.second);
+ } else {
+ pos->second.value()->next(cell.second);
+ }
+ }
+ for (const auto &my_entry: my_map) {
+ result.add(my_entry.first, my_entry.second.value()->result());
+ }
+ return result;
+}
+
+
+TensorSpec ReferenceOperations::rename(const TensorSpec &a, const std::vector<vespalib::string> &from, const std::vector<vespalib::string> &to) {
+ assert(from.size() == to.size());
+ ValueType res_type = ValueType::from_spec(a.type()).rename(from, to);
+ TensorSpec result(res_type.to_spec());
+ if (res_type.is_error()) {
+ return result;
+ }
+ for (const auto &cell: a.cells()) {
+ TensorSpec::Address addr;
+ for (const auto &dim: cell.first) {
+ addr.insert_or_assign(rename_dimension(dim.first, from, to), dim.second);
+ }
+ result.add(addr, cell.second);
+ }
+ return result;
+}
+
+
+} // namespace
diff --git a/eval/src/vespa/eval/eval/test/reference_operations.h b/eval/src/vespa/eval/eval/test/reference_operations.h
new file mode 100644
index 00000000000..735454b486a
--- /dev/null
+++ b/eval/src/vespa/eval/eval/test/reference_operations.h
@@ -0,0 +1,37 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/eval/aggr.h>
+#include <vespa/eval/eval/operation.h>
+#include <vespa/eval/eval/tensor_spec.h>
+#include <vespa/eval/eval/value_type.h>
+#include <vespa/eval/eval/tensor_function.h>
+
+#include <vector>
+#include <map>
+#include <variant>
+
+namespace vespalib::eval {
+
+struct ReferenceOperations {
+ using map_fun_t = vespalib::eval::operation::op1_t;
+ using join_fun_t = vespalib::eval::operation::op2_t;
+
+ // mapping from cell address to index of child that computes the cell value
+ using CreateSpec = tensor_function::Create::Spec;
+
+ // mapping from dimension name to verbatim label or child
+ using PeekSpec = tensor_function::Peek::Spec;
+
+ static TensorSpec concat(const TensorSpec &a, const TensorSpec &b, const std::string &concat_dim);
+ static TensorSpec create(const vespalib::string &type, const CreateSpec &spec, const std::vector<TensorSpec> &children);
+ static TensorSpec join(const TensorSpec &a, const TensorSpec &b, join_fun_t function);
+ static TensorSpec map(const TensorSpec &a, map_fun_t func);
+ static TensorSpec merge(const TensorSpec &a, const TensorSpec &b, join_fun_t fun);
+ static TensorSpec peek(const TensorSpec &param, const PeekSpec &spec, const std::vector<TensorSpec> &children);
+ static TensorSpec reduce(const TensorSpec &a, const std::vector<vespalib::string> &dims, Aggr aggr);
+ static TensorSpec rename(const TensorSpec &a, const std::vector<vespalib::string> &from, const std::vector<vespalib::string> &to);
+};
+
+} // namespace
diff --git a/eval/src/vespa/eval/eval/test/tensor_model.hpp b/eval/src/vespa/eval/eval/test/tensor_model.hpp
index 59653954c9e..78d6798ac4c 100644
--- a/eval/src/vespa/eval/eval/test/tensor_model.hpp
+++ b/eval/src/vespa/eval/eval/test/tensor_model.hpp
@@ -16,7 +16,6 @@ namespace vespalib {
namespace eval {
namespace test {
-using CellType = ValueType::CellType;
using map_fun_t = vespalib::eval::operation::op1_t;
using join_fun_t = vespalib::eval::operation::op2_t;
diff --git a/eval/src/vespa/eval/eval/typed_cells.h b/eval/src/vespa/eval/eval/typed_cells.h
index 09d5c080cf7..a478a419f95 100644
--- a/eval/src/vespa/eval/eval/typed_cells.h
+++ b/eval/src/vespa/eval/eval/typed_cells.h
@@ -11,8 +11,6 @@ namespace vespalib::eval {
// Low-level typed cells reference
struct TypedCells {
- using CellType = vespalib::eval::ValueType::CellType;
-
const void *data;
CellType type;
size_t size:56;
diff --git a/eval/src/vespa/eval/eval/value_codec.cpp b/eval/src/vespa/eval/eval/value_codec.cpp
index 2de95657f72..923d3f29cd3 100644
--- a/eval/src/vespa/eval/eval/value_codec.cpp
+++ b/eval/src/vespa/eval/eval/value_codec.cpp
@@ -14,8 +14,6 @@ namespace vespalib::eval {
namespace {
-using CellType = ValueType::CellType;
-
constexpr uint32_t DOUBLE_CELL_TYPE = 0;
constexpr uint32_t FLOAT_CELL_TYPE = 1;
@@ -118,7 +116,7 @@ ValueType decode_type(nbostream &input, const Format &format) {
}
}
if (dim_list.empty()) {
- assert(cell_type == ValueType::CellType::DOUBLE);
+ assert(cell_type == CellType::DOUBLE);
}
return ValueType::tensor_type(std::move(dim_list), cell_type);
}
diff --git a/eval/src/vespa/eval/eval/value_type.cpp b/eval/src/vespa/eval/eval/value_type.cpp
index c7d77c766bc..05ec65bf292 100644
--- a/eval/src/vespa/eval/eval/value_type.cpp
+++ b/eval/src/vespa/eval/eval/value_type.cpp
@@ -8,7 +8,6 @@ namespace vespalib::eval {
namespace {
-using CellType = ValueType::CellType;
using Dimension = ValueType::Dimension;
using DimensionList = std::vector<Dimension>;
diff --git a/eval/src/vespa/eval/eval/value_type.h b/eval/src/vespa/eval/eval/value_type.h
index ae69b5a3349..6d9316e76ed 100644
--- a/eval/src/vespa/eval/eval/value_type.h
+++ b/eval/src/vespa/eval/eval/value_type.h
@@ -16,7 +16,6 @@ namespace vespalib::eval {
class ValueType
{
public:
- using CellType = vespalib::eval::CellType;
struct Dimension {
using size_type = uint32_t;
static constexpr size_type npos = -1;
diff --git a/eval/src/vespa/eval/eval/value_type_spec.cpp b/eval/src/vespa/eval/eval/value_type_spec.cpp
index 847203db3b1..a4575e33c2f 100644
--- a/eval/src/vespa/eval/eval/value_type_spec.cpp
+++ b/eval/src/vespa/eval/eval/value_type_spec.cpp
@@ -8,8 +8,6 @@
namespace vespalib::eval::value_type {
-using CellType = ValueType::CellType;
-
namespace {
const char *to_name(CellType cell_type) {
@@ -188,7 +186,7 @@ parse_spec(const char *pos_in, const char *end_in, const char *&pos_out,
} else if (type_name == "float") {
return ValueType::make_type(CellType::FLOAT, {});
} else if (type_name == "tensor") {
- ValueType::CellType cell_type = parse_cell_type(ctx);
+ CellType cell_type = parse_cell_type(ctx);
std::vector<ValueType::Dimension> list = parse_dimension_list(ctx);
if (!ctx.failed()) {
if (unsorted != nullptr) {
diff --git a/eval/src/vespa/eval/instruction/dense_dot_product_function.cpp b/eval/src/vespa/eval/instruction/dense_dot_product_function.cpp
index cc746c4db83..5dcfcba025d 100644
--- a/eval/src/vespa/eval/instruction/dense_dot_product_function.cpp
+++ b/eval/src/vespa/eval/instruction/dense_dot_product_function.cpp
@@ -45,12 +45,12 @@ struct MyDotProductOp {
static auto invoke() { return my_dot_product_op<LCT,RCT>; }
};
-InterpretedFunction::op_function my_select(ValueType::CellType lct, ValueType::CellType rct) {
+InterpretedFunction::op_function my_select(CellType lct, CellType rct) {
if (lct == rct) {
- if (lct == ValueType::CellType::DOUBLE) {
+ if (lct == CellType::DOUBLE) {
return my_cblas_double_dot_product_op;
}
- if (lct == ValueType::CellType::FLOAT) {
+ if (lct == CellType::FLOAT) {
return my_cblas_float_dot_product_op;
}
}
diff --git a/eval/src/vespa/eval/instruction/dense_lambda_peek_function.cpp b/eval/src/vespa/eval/instruction/dense_lambda_peek_function.cpp
index 0abcd452645..5e9ff6a0ef0 100644
--- a/eval/src/vespa/eval/instruction/dense_lambda_peek_function.cpp
+++ b/eval/src/vespa/eval/instruction/dense_lambda_peek_function.cpp
@@ -27,7 +27,7 @@ void my_lambda_peek_op(InterpretedFunction::State &state, uint64_t param) {
const auto &self = unwrap_param<Self>(param);
const std::vector<uint32_t> &lookup_table = self.table_token->get();
auto src_cells = state.peek(0).cells().typify<SRC_CT>();
- ArrayRef<DST_CT> dst_cells = state.stash.create_array<DST_CT>(lookup_table.size());
+ ArrayRef<DST_CT> dst_cells = state.stash.create_uninitialized_array<DST_CT>(lookup_table.size());
DST_CT *dst = &dst_cells[0];
for (uint32_t idx: lookup_table) {
*dst++ = src_cells[idx];
diff --git a/eval/src/vespa/eval/instruction/dense_matmul_function.cpp b/eval/src/vespa/eval/instruction/dense_matmul_function.cpp
index 554122a67b4..5d4ebb88931 100644
--- a/eval/src/vespa/eval/instruction/dense_matmul_function.cpp
+++ b/eval/src/vespa/eval/instruction/dense_matmul_function.cpp
@@ -32,7 +32,7 @@ void my_matmul_op(InterpretedFunction::State &state, uint64_t param) {
using OCT = typename UnifyCellTypes<LCT,RCT>::type;
auto lhs_cells = state.peek(1).cells().typify<LCT>();
auto rhs_cells = state.peek(0).cells().typify<RCT>();
- auto dst_cells = state.stash.create_array<OCT>(self.lhs_size * self.rhs_size);
+ auto dst_cells = state.stash.create_uninitialized_array<OCT>(self.lhs_size * self.rhs_size);
OCT *dst = dst_cells.begin();
const LCT *lhs = lhs_cells.cbegin();
for (size_t i = 0; i < self.lhs_size; ++i) {
diff --git a/eval/src/vespa/eval/instruction/dense_multi_matmul_function.cpp b/eval/src/vespa/eval/instruction/dense_multi_matmul_function.cpp
index cbca2ff14f2..42e7deb9523 100644
--- a/eval/src/vespa/eval/instruction/dense_multi_matmul_function.cpp
+++ b/eval/src/vespa/eval/instruction/dense_multi_matmul_function.cpp
@@ -60,11 +60,11 @@ void my_cblas_float_multi_matmul_op(InterpretedFunction::State &state, uint64_t
state.pop_pop_push(state.stash.create<tensor::DenseTensorView>(self.result_type(), TypedCells(dst_cells)));
}
-InterpretedFunction::op_function my_select(ValueType::CellType cell_type) {
- if (cell_type == ValueType::CellType::DOUBLE) {
+InterpretedFunction::op_function my_select(CellType cell_type) {
+ if (cell_type == CellType::DOUBLE) {
return my_cblas_double_multi_matmul_op;
}
- if (cell_type == ValueType::CellType::FLOAT) {
+ if (cell_type == CellType::FLOAT) {
return my_cblas_float_multi_matmul_op;
}
abort();
@@ -117,7 +117,7 @@ struct DimPrefix {
bool check_input_type(const ValueType &type, const DimList &relevant) {
return (type.is_dense() &&
(relevant.size() >= 2) &&
- ((type.cell_type() == ValueType::CellType::FLOAT) || (type.cell_type() == ValueType::CellType::DOUBLE)));
+ ((type.cell_type() == CellType::FLOAT) || (type.cell_type() == CellType::DOUBLE)));
}
bool is_multi_matmul(const ValueType &a, const ValueType &b, const vespalib::string &reduce_dim) {
diff --git a/eval/src/vespa/eval/instruction/dense_tensor_peek_function.cpp b/eval/src/vespa/eval/instruction/dense_tensor_peek_function.cpp
index daad4da947b..fd93cd62fa9 100644
--- a/eval/src/vespa/eval/instruction/dense_tensor_peek_function.cpp
+++ b/eval/src/vespa/eval/instruction/dense_tensor_peek_function.cpp
@@ -75,10 +75,10 @@ DenseTensorPeekFunction::optimize(const TensorFunction &expr, Stash &stash)
const ValueType &peek_type = peek->param_type();
if (expr.result_type().is_double() && peek_type.is_dense()) {
std::vector<std::pair<int64_t,size_t>> spec;
- assert(peek_type.dimensions().size() == peek->spec().size());
+ assert(peek_type.dimensions().size() == peek->map().size());
for (auto dim = peek_type.dimensions().rbegin(); dim != peek_type.dimensions().rend(); ++dim) {
- auto dim_spec = peek->spec().find(dim->name);
- assert(dim_spec != peek->spec().end());
+ auto dim_spec = peek->map().find(dim->name);
+ assert(dim_spec != peek->map().end());
std::visit(vespalib::overload
{
diff --git a/eval/src/vespa/eval/instruction/dense_xw_product_function.cpp b/eval/src/vespa/eval/instruction/dense_xw_product_function.cpp
index fdcef97e277..44332bbacee 100644
--- a/eval/src/vespa/eval/instruction/dense_xw_product_function.cpp
+++ b/eval/src/vespa/eval/instruction/dense_xw_product_function.cpp
@@ -33,7 +33,7 @@ void my_xw_product_op(InterpretedFunction::State &state, uint64_t param) {
using OCT = typename UnifyCellTypes<LCT,RCT>::type;
auto vector_cells = state.peek(1).cells().typify<LCT>();
auto matrix_cells = state.peek(0).cells().typify<RCT>();
- auto dst_cells = state.stash.create_array<OCT>(self.result_size);
+ auto dst_cells = state.stash.create_uninitialized_array<OCT>(self.result_size);
OCT *dst = dst_cells.begin();
const RCT *matrix = matrix_cells.cbegin();
for (size_t i = 0; i < self.result_size; ++i) {
diff --git a/eval/src/vespa/eval/instruction/generic_create.h b/eval/src/vespa/eval/instruction/generic_create.h
index dc3cebc1086..dfd858613fe 100644
--- a/eval/src/vespa/eval/instruction/generic_create.h
+++ b/eval/src/vespa/eval/instruction/generic_create.h
@@ -5,6 +5,7 @@
#include <vespa/eval/eval/value_type.h>
#include <vespa/eval/eval/tensor_spec.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/tensor_function.h>
#include <map>
namespace vespalib { class Stash; }
@@ -15,7 +16,8 @@ namespace vespalib::eval::instruction {
//-----------------------------------------------------------------------------
struct GenericCreate {
- using SpecMap = std::map<TensorSpec::Address, size_t>;
+ // mapping from cell address to index of child that computes the cell value
+ using SpecMap = tensor_function::Create::Spec;
static InterpretedFunction::Instruction
make_instruction(const ValueType &res_type,
diff --git a/eval/src/vespa/eval/instruction/generic_lambda.cpp b/eval/src/vespa/eval/instruction/generic_lambda.cpp
index 1ba2f710909..5685f199b9e 100644
--- a/eval/src/vespa/eval/instruction/generic_lambda.cpp
+++ b/eval/src/vespa/eval/instruction/generic_lambda.cpp
@@ -103,7 +103,7 @@ void my_interpreted_lambda_op(eval::InterpretedFunction::State &state, uint64_t
std::vector<double> labels(params.result_type.dimensions().size(), 0.0);
ParamProxy param_proxy(labels, *state.params, params.bindings);
InterpretedFunction::Context ctx(params.fun);
- ArrayRef<CT> dst_cells = state.stash.create_array<CT>(params.num_cells);
+ ArrayRef<CT> dst_cells = state.stash.create_uninitialized_array<CT>(params.num_cells);
CT *dst = &dst_cells[0];
do {
*dst++ = params.fun.eval(ctx, param_proxy).as_double();
diff --git a/eval/src/vespa/eval/instruction/generic_merge.cpp b/eval/src/vespa/eval/instruction/generic_merge.cpp
index 87be47a9c2e..8de4ea1adeb 100644
--- a/eval/src/vespa/eval/instruction/generic_merge.cpp
+++ b/eval/src/vespa/eval/instruction/generic_merge.cpp
@@ -127,9 +127,19 @@ void my_sparse_merge_op(State &state, uint64_t param_in) {
if (auto indexes = detect_type<FastValueIndex>(lhs.index(), rhs.index())) {
auto lhs_cells = lhs.cells().typify<LCT>();
auto rhs_cells = rhs.cells().typify<RCT>();
- return state.pop_pop_push(
+ if (lhs_cells.size() < rhs_cells.size()) {
+ return state.pop_pop_push(
+ FastValueIndex::sparse_only_merge<RCT,LCT,OCT,Fun>(
+ param.res_type, Fun(param.function),
+ indexes.get<1>(), indexes.get<0>(),
+ rhs_cells, lhs_cells, state.stash));
+ } else {
+ return state.pop_pop_push(
FastValueIndex::sparse_only_merge<LCT,RCT,OCT,Fun>(
- param.res_type, Fun(param.function), indexes.get<0>(), indexes.get<1>(), lhs_cells, rhs_cells, state.stash));
+ param.res_type, Fun(param.function),
+ indexes.get<0>(), indexes.get<1>(),
+ lhs_cells, rhs_cells, state.stash));
+ }
}
auto up = generic_mixed_merge<LCT, RCT, OCT, Fun>(lhs, rhs, param);
auto &result = state.stash.create<std::unique_ptr<Value>>(std::move(up));
diff --git a/eval/src/vespa/eval/instruction/generic_peek.cpp b/eval/src/vespa/eval/instruction/generic_peek.cpp
index 5802a60d43a..d8ae9241f44 100644
--- a/eval/src/vespa/eval/instruction/generic_peek.cpp
+++ b/eval/src/vespa/eval/instruction/generic_peek.cpp
@@ -35,7 +35,7 @@ size_t count_children(const Spec &spec)
struct DimSpec {
vespalib::stringref name;
- GenericPeek::MyLabel child_or_label;
+ GenericPeek::SpecMap::mapped_type child_or_label;
bool has_child() const {
return std::holds_alternative<size_t>(child_or_label);
}
diff --git a/eval/src/vespa/eval/instruction/generic_peek.h b/eval/src/vespa/eval/instruction/generic_peek.h
index d31b47238cb..3fe7aa9d270 100644
--- a/eval/src/vespa/eval/instruction/generic_peek.h
+++ b/eval/src/vespa/eval/instruction/generic_peek.h
@@ -5,6 +5,7 @@
#include <vespa/eval/eval/value_type.h>
#include <vespa/eval/eval/tensor_spec.h>
#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/tensor_function.h>
#include <map>
namespace vespalib { class Stash; }
@@ -15,8 +16,8 @@ namespace vespalib::eval::instruction {
//-----------------------------------------------------------------------------
struct GenericPeek {
- using MyLabel = std::variant<TensorSpec::Label, size_t>;
- using SpecMap = std::map<vespalib::string, MyLabel>;
+ // mapping from dimension name to verbatim label or child
+ using SpecMap = tensor_function::Peek::Spec;
static InterpretedFunction::Instruction
make_instruction(const ValueType &input_type,
diff --git a/eval/src/vespa/eval/streamed/CMakeLists.txt b/eval/src/vespa/eval/streamed/CMakeLists.txt
new file mode 100644
index 00000000000..ee928d7b2c9
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+vespa_add_library(eval_streamed OBJECT
+ SOURCES
+ streamed_value.cpp
+ streamed_value_index.cpp
+ streamed_value_utils.cpp
+ streamed_value_builder.cpp
+ streamed_value_builder_factory.cpp
+ streamed_value_view.cpp
+)
diff --git a/eval/src/vespa/eval/streamed/streamed_value.cpp b/eval/src/vespa/eval/streamed/streamed_value.cpp
new file mode 100644
index 00000000000..bdfe5fd4e27
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value.cpp
@@ -0,0 +1,28 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "streamed_value.h"
+#include <vespa/log/log.h>
+
+LOG_SETUP(".vespalib.eval.streamed.streamed_value");
+
+namespace vespalib::eval {
+
+template <typename T>
+StreamedValue<T>::~StreamedValue() = default;
+
+template <typename T>
+MemoryUsage
+StreamedValue<T>::get_memory_usage() const
+{
+ MemoryUsage usage = self_memory_usage<StreamedValue<T>>();
+ usage.merge(vector_extra_memory_usage(_my_cells));
+ usage.incUsedBytes(_label_buf.byteSize());
+ usage.incAllocatedBytes(_label_buf.byteCapacity());
+ return usage;
+}
+
+template class StreamedValue<double>;
+template class StreamedValue<float>;
+
+} // namespace
+
diff --git a/eval/src/vespa/eval/streamed/streamed_value.h b/eval/src/vespa/eval/streamed/streamed_value.h
new file mode 100644
index 00000000000..258802a53e8
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value.h
@@ -0,0 +1,48 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/eval/value_type.h>
+#include <vespa/eval/eval/value.h>
+#include "streamed_value_index.h"
+#include <cassert>
+
+namespace vespalib::eval {
+
+/**
+ * A very simple Value implementation.
+ * Cheap to construct from serialized data,
+ * and cheap to serialize or iterate through.
+ * Slow for full or partial lookups.
+ **/
+template <typename T>
+class StreamedValue : public Value
+{
+private:
+ ValueType _type;
+ std::vector<T> _my_cells;
+ Array<char> _label_buf;
+ StreamedValueIndex _my_index;
+
+public:
+ StreamedValue(ValueType type, size_t num_mapped_dimensions,
+ std::vector<T> cells, size_t num_subspaces, Array<char> && label_buf)
+ : _type(std::move(type)),
+ _my_cells(std::move(cells)),
+ _label_buf(std::move(label_buf)),
+ _my_index(num_mapped_dimensions,
+ num_subspaces,
+ ConstArrayRef<char>(_label_buf.begin(), _label_buf.size()))
+ {
+ assert(num_subspaces * _type.dense_subspace_size() == _my_cells.size());
+ }
+
+ ~StreamedValue();
+ const ValueType &type() const final override { return _type; }
+ TypedCells cells() const final override { return TypedCells(_my_cells); }
+ const Value::Index &index() const final override { return _my_index; }
+ MemoryUsage get_memory_usage() const final override;
+ auto get_data_reference() const { return _my_index.get_data_reference(); }
+};
+
+} // namespace
diff --git a/eval/src/vespa/eval/streamed/streamed_value_builder.cpp b/eval/src/vespa/eval/streamed/streamed_value_builder.cpp
new file mode 100644
index 00000000000..957121c42b7
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_builder.cpp
@@ -0,0 +1,13 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "streamed_value_builder.h"
+
+namespace vespalib::eval {
+
+template<typename T>
+StreamedValueBuilder<T>::~StreamedValueBuilder() = default;
+
+template class StreamedValueBuilder<double>;
+template class StreamedValueBuilder<float>;
+
+} // namespace
diff --git a/eval/src/vespa/eval/streamed/streamed_value_builder.h b/eval/src/vespa/eval/streamed/streamed_value_builder.h
new file mode 100644
index 00000000000..5698c805756
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_builder.h
@@ -0,0 +1,66 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "streamed_value.h"
+#include <vespa/vespalib/objects/nbostream.h>
+
+namespace vespalib::eval {
+
+ /**
+ * Builder for StreamedValue objects.
+ **/
+template <typename T>
+class StreamedValueBuilder : public ValueBuilder<T>
+{
+private:
+ ValueType _type;
+ size_t _num_mapped_dimensions;
+ size_t _dense_subspace_size;
+ std::vector<T> _cells;
+ size_t _num_subspaces;
+ nbostream _labels;
+public:
+ StreamedValueBuilder(const ValueType &type,
+ size_t num_mapped_in,
+ size_t subspace_size_in,
+ size_t expected_subspaces)
+ : _type(type),
+ _num_mapped_dimensions(num_mapped_in),
+ _dense_subspace_size(subspace_size_in),
+ _cells(),
+ _num_subspaces(0),
+ _labels()
+ {
+ _cells.reserve(subspace_size_in * expected_subspaces);
+ // assume small sized label strings:
+ _labels.reserve(num_mapped_in * expected_subspaces * 3);
+ };
+
+ ~StreamedValueBuilder();
+
+ ArrayRef<T> add_subspace(ConstArrayRef<vespalib::stringref> addr) override {
+ for (auto label : addr) {
+ _labels.writeSmallString(label);
+ }
+ size_t old_sz = _cells.size();
+ _cells.resize(old_sz + _dense_subspace_size);
+ _num_subspaces++;
+ return ArrayRef<T>(&_cells[old_sz], _dense_subspace_size);
+ }
+
+ std::unique_ptr<Value> build(std::unique_ptr<ValueBuilder<T>>) override {
+ if (_num_mapped_dimensions == 0) {
+ assert(_num_subspaces == 1);
+ }
+ assert(_num_subspaces * _dense_subspace_size == _cells.size());
+ return std::make_unique<StreamedValue<T>>(std::move(_type),
+ _num_mapped_dimensions,
+ std::move(_cells),
+ _num_subspaces,
+ _labels.extract_buffer());
+ }
+
+};
+
+} // namespace
diff --git a/eval/src/vespa/eval/streamed/streamed_value_builder_factory.cpp b/eval/src/vespa/eval/streamed/streamed_value_builder_factory.cpp
new file mode 100644
index 00000000000..aa6347a2c51
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_builder_factory.cpp
@@ -0,0 +1,36 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "streamed_value_builder_factory.h"
+#include "streamed_value_builder.h"
+
+namespace vespalib::eval {
+
+struct SelectStreamedValueBuilder {
+ template <typename T>
+ static std::unique_ptr<ValueBuilderBase> invoke(
+ const ValueType &type, size_t num_mapped,
+ size_t subspace_size, size_t expected_subspaces)
+ {
+ assert(check_cell_type<T>(type.cell_type()));
+ return std::make_unique<StreamedValueBuilder<T>>(
+ type, num_mapped, subspace_size, expected_subspaces);
+ }
+};
+
+std::unique_ptr<ValueBuilderBase>
+StreamedValueBuilderFactory::create_value_builder_base(const ValueType &type,
+ size_t num_mapped,
+ size_t subspace_size,
+ size_t expected_subspaces) const
+{
+ return typify_invoke<1,TypifyCellType,SelectStreamedValueBuilder>(
+ type.cell_type(),
+ type, num_mapped, subspace_size, expected_subspaces);
+}
+
+StreamedValueBuilderFactory::~StreamedValueBuilderFactory() = default;
+StreamedValueBuilderFactory StreamedValueBuilderFactory::_factory;
+
+} // namespace
+
+
diff --git a/eval/src/vespa/eval/streamed/streamed_value_builder_factory.h b/eval/src/vespa/eval/streamed/streamed_value_builder_factory.h
new file mode 100644
index 00000000000..3f81981f429
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_builder_factory.h
@@ -0,0 +1,24 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "streamed_value.h"
+
+namespace vespalib::eval {
+
+/**
+ * A factory that can generate appropriate ValueBuilder instances
+ */
+struct StreamedValueBuilderFactory : ValueBuilderFactory {
+private:
+ StreamedValueBuilderFactory() {}
+ static StreamedValueBuilderFactory _factory;
+ std::unique_ptr<ValueBuilderBase> create_value_builder_base(
+ const ValueType &type, size_t num_mapped_in,
+ size_t subspace_size_in, size_t expected_subspaces) const override;
+public:
+ static const StreamedValueBuilderFactory &get() { return _factory; }
+ ~StreamedValueBuilderFactory();
+};
+
+}
diff --git a/eval/src/vespa/eval/streamed/streamed_value_index.cpp b/eval/src/vespa/eval/streamed/streamed_value_index.cpp
new file mode 100644
index 00000000000..38b57e9c660
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_index.cpp
@@ -0,0 +1,100 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "streamed_value_index.h"
+#include "streamed_value_utils.h"
+
+#include <vespa/vespalib/objects/nbostream.h>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/util/visit_ranges.h>
+#include <vespa/log/log.h>
+
+LOG_SETUP(".searchlib.tensor.streamed_value_index");
+
+namespace vespalib::eval {
+
+namespace {
+
+struct StreamedFilterView : Value::Index::View
+{
+ LabelBlockStream label_blocks;
+ std::vector<size_t> view_dims;
+ std::vector<vespalib::stringref> to_match;
+
+ StreamedFilterView(LabelBlockStream labels, std::vector<size_t> view_dims_in)
+ : label_blocks(std::move(labels)),
+ view_dims(std::move(view_dims_in)),
+ to_match()
+ {
+ to_match.reserve(view_dims.size());
+ }
+
+ void lookup(ConstArrayRef<const vespalib::stringref*> addr) override {
+ label_blocks.reset();
+ to_match.clear();
+ for (auto ptr : addr) {
+ to_match.push_back(*ptr);
+ }
+ assert(view_dims.size() == to_match.size());
+ }
+
+ bool next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out) override {
+ while (const auto block = label_blocks.next_block()) {
+ idx_out = block.ss_idx;
+ bool matches = true;
+ size_t out_idx = 0;
+ size_t vdm_idx = 0;
+ for (size_t dim = 0; dim < block.address.size(); ++dim) {
+ if (vdm_idx < view_dims.size() && (view_dims[vdm_idx] == dim)) {
+ matches &= (block.address[dim] == to_match[vdm_idx++]);
+ } else {
+ *addr_out[out_idx++] = block.address[dim];
+ }
+ }
+ assert(out_idx == addr_out.size());
+ assert(vdm_idx == view_dims.size());
+ if (matches) return true;
+ }
+ return false;
+ }
+};
+
+struct StreamedIterationView : Value::Index::View
+{
+ LabelBlockStream label_blocks;
+
+ StreamedIterationView(LabelBlockStream labels)
+ : label_blocks(std::move(labels))
+ {}
+
+ void lookup(ConstArrayRef<const vespalib::stringref*> addr) override {
+ label_blocks.reset();
+ assert(addr.size() == 0);
+ }
+
+ bool next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out) override {
+ if (auto block = label_blocks.next_block()) {
+ idx_out = block.ss_idx;
+ size_t i = 0;
+ assert(addr_out.size() == block.address.size());
+ for (auto ptr : addr_out) {
+ *ptr = block.address[i++];
+ }
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace <unnamed>
+
+std::unique_ptr<Value::Index::View>
+StreamedValueIndex::create_view(const std::vector<size_t> &dims) const
+{
+ LabelBlockStream label_stream(_data.num_subspaces, _data.labels_buffer, _data.num_mapped_dims);
+ if (dims.empty()) {
+ return std::make_unique<StreamedIterationView>(std::move(label_stream));
+ }
+ return std::make_unique<StreamedFilterView>(std::move(label_stream), dims);
+}
+
+} // namespace vespalib::eval
diff --git a/eval/src/vespa/eval/streamed/streamed_value_index.h b/eval/src/vespa/eval/streamed/streamed_value_index.h
new file mode 100644
index 00000000000..8fd561200c3
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_index.h
@@ -0,0 +1,36 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/eval/value.h>
+
+namespace vespalib::eval {
+
+ /**
+ * Implements Value::Index by reading a stream of serialized
+ * labels.
+ **/
+class StreamedValueIndex : public Value::Index
+{
+public:
+ struct SerializedDataRef {
+ uint32_t num_mapped_dims;
+ uint32_t num_subspaces;
+ ConstArrayRef<char> labels_buffer;
+ };
+ StreamedValueIndex(uint32_t num_mapped_dims, uint32_t num_subspaces, ConstArrayRef<char> labels_buf)
+ : _data{num_mapped_dims, num_subspaces, labels_buf}
+ {}
+
+ // index API:
+ size_t size() const override { return _data.num_subspaces; }
+ std::unique_ptr<View> create_view(const std::vector<size_t> &dims) const override;
+
+ SerializedDataRef get_data_reference() const { return _data; }
+
+private:
+ SerializedDataRef _data;
+};
+
+} // namespace
+
diff --git a/eval/src/vespa/eval/streamed/streamed_value_utils.cpp b/eval/src/vespa/eval/streamed/streamed_value_utils.cpp
new file mode 100644
index 00000000000..1b4a91a9080
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_utils.cpp
@@ -0,0 +1,9 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "streamed_value_utils.h"
+
+namespace vespalib::eval {
+
+LabelBlockStream::~LabelBlockStream() = default;
+
+} // namespace
diff --git a/eval/src/vespa/eval/streamed/streamed_value_utils.h b/eval/src/vespa/eval/streamed/streamed_value_utils.h
new file mode 100644
index 00000000000..3e3da82dd22
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_utils.h
@@ -0,0 +1,76 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/eval/value.h>
+#include <vespa/vespalib/objects/nbostream.h>
+
+namespace vespalib::eval {
+
+/**
+ * Reads a stream of serialized labels.
+ * Reading more labels than available will
+ * throw an exception.
+ **/
+struct LabelStream {
+ nbostream source;
+ LabelStream(ConstArrayRef<char> data) : source(data.begin(), data.size()) {}
+ vespalib::stringref next_label() {
+ size_t str_size = source.getInt1_4Bytes();
+ vespalib::stringref label(source.peek(), str_size);
+ source.adjustReadPos(str_size);
+ return label;
+ }
+ void reset() { source.rp(0); }
+};
+
+/**
+ * Represents an address (set of labels) mapping to a subspace index
+ **/
+struct LabelBlock {
+ static constexpr size_t npos = -1;
+ size_t ss_idx;
+ ConstArrayRef<vespalib::stringref> address;
+ operator bool() const { return ss_idx != npos; }
+};
+
+/**
+ * Utility for reading a buffer with serialized labels
+ * as a stream of LabelBlock objects.
+ **/
+class LabelBlockStream {
+private:
+ size_t _num_subspaces;
+ LabelStream _labels;
+ size_t _subspace_index;
+ std::vector<vespalib::stringref> _current_address;
+public:
+ LabelBlock next_block() {
+ if (_subspace_index < _num_subspaces) {
+ for (auto & label : _current_address) {
+ label = _labels.next_label();
+ }
+ return LabelBlock{_subspace_index++, _current_address};
+ } else {
+ return LabelBlock{LabelBlock::npos, {}};
+ }
+ }
+
+ void reset() {
+ _subspace_index = 0;
+ _labels.reset();
+ }
+
+ LabelBlockStream(uint32_t num_subspaces,
+ ConstArrayRef<char> label_buf,
+ uint32_t num_mapped_dims)
+ : _num_subspaces(num_subspaces),
+ _labels(label_buf),
+ _subspace_index(num_subspaces),
+ _current_address(num_mapped_dims)
+ {}
+
+ ~LabelBlockStream();
+};
+
+} // namespace
diff --git a/eval/src/vespa/eval/streamed/streamed_value_view.cpp b/eval/src/vespa/eval/streamed/streamed_value_view.cpp
new file mode 100644
index 00000000000..87e1e676692
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_view.cpp
@@ -0,0 +1,9 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "streamed_value_view.h"
+
+namespace vespalib::eval {
+
+StreamedValueView::~StreamedValueView() = default;
+
+} // namespace
diff --git a/eval/src/vespa/eval/streamed/streamed_value_view.h b/eval/src/vespa/eval/streamed/streamed_value_view.h
new file mode 100644
index 00000000000..e37f442dd9a
--- /dev/null
+++ b/eval/src/vespa/eval/streamed/streamed_value_view.h
@@ -0,0 +1,45 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/eval/value_type.h>
+#include <vespa/eval/eval/value.h>
+#include "streamed_value_index.h"
+#include <cassert>
+
+namespace vespalib::eval {
+
+ /**
+ * Same characteristics as StreamedValue, but does not
+ * own its data - refers to type, cells and serialized
+ * labels that must be kept outside the Value.
+ **/
+class StreamedValueView : public Value
+{
+private:
+ const ValueType &_type;
+ TypedCells _cells_ref;
+ StreamedValueIndex _my_index;
+
+public:
+ StreamedValueView(const ValueType &type, size_t num_mapped_dimensions,
+ TypedCells cells, size_t num_subspaces,
+ ConstArrayRef<char> labels_buf)
+ : _type(type),
+ _cells_ref(cells),
+ _my_index(num_mapped_dimensions, num_subspaces, labels_buf)
+ {
+ assert(num_subspaces * _type.dense_subspace_size() == _cells_ref.size);
+ }
+
+ ~StreamedValueView();
+ const ValueType &type() const final override { return _type; }
+ TypedCells cells() const final override { return _cells_ref; }
+ const Value::Index &index() const final override { return _my_index; }
+ MemoryUsage get_memory_usage() const final override {
+ return self_memory_usage<StreamedValueView>();
+ }
+ auto get_data_reference() const { return _my_index.get_data_reference(); }
+};
+
+} // namespace
diff --git a/eval/src/vespa/eval/tensor/CMakeLists.txt b/eval/src/vespa/eval/tensor/CMakeLists.txt
index 77ae1daec88..75be3e802ea 100644
--- a/eval/src/vespa/eval/tensor/CMakeLists.txt
+++ b/eval/src/vespa/eval/tensor/CMakeLists.txt
@@ -2,7 +2,6 @@
vespa_add_library(eval_tensor OBJECT
SOURCES
default_tensor_engine.cpp
- default_value_builder_factory.cpp
partial_update.cpp
tensor.cpp
tensor_address.cpp
diff --git a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
index 04aad776e43..68c8dc990c6 100644
--- a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
+++ b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
@@ -46,7 +46,6 @@ using eval::TensorFunction;
using eval::TensorSpec;
using eval::Value;
using eval::ValueType;
-using CellType = eval::ValueType::CellType;
using vespalib::IllegalArgumentException;
using vespalib::make_string;
@@ -451,7 +450,7 @@ void append_vector(OCT *&pos, const Value &value) {
template <typename OCT>
const Value &concat_vectors(const Value &a, const Value &b, const vespalib::string &dimension, size_t vector_size, Stash &stash) {
- ArrayRef<OCT> cells = stash.create_array<OCT>(vector_size);
+ ArrayRef<OCT> cells = stash.create_uninitialized_array<OCT>(vector_size);
OCT *pos = cells.begin();
append_vector<OCT>(pos, a);
append_vector<OCT>(pos, b);
diff --git a/eval/src/vespa/eval/tensor/default_value_builder_factory.cpp b/eval/src/vespa/eval/tensor/default_value_builder_factory.cpp
deleted file mode 100644
index 74fd371e9a0..00000000000
--- a/eval/src/vespa/eval/tensor/default_value_builder_factory.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "default_value_builder_factory.h"
-#include <vespa/vespalib/util/typify.h>
-#include <vespa/eval/eval/value.h>
-#include <vespa/eval/eval/double_value_builder.h>
-#include <vespa/eval/tensor/dense/dense_tensor_value_builder.h>
-#include <vespa/eval/tensor/mixed/packed_mixed_tensor_builder.h>
-#include <vespa/eval/tensor/sparse/sparse_tensor_value_builder.h>
-
-using namespace vespalib::eval;
-
-namespace vespalib::tensor {
-
-//-----------------------------------------------------------------------------
-
-namespace {
-
-struct CreateDefaultValueBuilderBase {
- template <typename T> static std::unique_ptr<ValueBuilderBase> invoke(const ValueType &type,
- size_t num_mapped_dims,
- size_t subspace_size,
- size_t expected_subspaces)
- {
- assert(check_cell_type<T>(type.cell_type()));
- if (type.is_double()) {
- return std::make_unique<DoubleValueBuilder>();
- }
- if (num_mapped_dims == 0) {
- return std::make_unique<DenseTensorValueBuilder<T>>(type, subspace_size);
- }
- if (subspace_size == 1) {
- return std::make_unique<SparseTensorValueBuilder<T>>(type, num_mapped_dims, expected_subspaces);
- }
- return std::make_unique<packed_mixed_tensor::PackedMixedTensorBuilder<T>>(type, num_mapped_dims, subspace_size, expected_subspaces);
- }
-};
-
-} // namespace <unnamed>
-
-//-----------------------------------------------------------------------------
-
-DefaultValueBuilderFactory::DefaultValueBuilderFactory() = default;
-DefaultValueBuilderFactory DefaultValueBuilderFactory::_factory;
-
-std::unique_ptr<ValueBuilderBase>
-DefaultValueBuilderFactory::create_value_builder_base(const ValueType &type,
- size_t num_mapped_dims,
- size_t subspace_size,
- size_t expected_subspaces) const
-{
- return typify_invoke<1,TypifyCellType,CreateDefaultValueBuilderBase>(type.cell_type(), type, num_mapped_dims, subspace_size, expected_subspaces);
-}
-
-//-----------------------------------------------------------------------------
-
-}
diff --git a/eval/src/vespa/eval/tensor/default_value_builder_factory.h b/eval/src/vespa/eval/tensor/default_value_builder_factory.h
deleted file mode 100644
index 67b1391ed78..00000000000
--- a/eval/src/vespa/eval/tensor/default_value_builder_factory.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include <vespa/eval/eval/value.h>
-#include <vespa/eval/eval/value_type.h>
-
-namespace vespalib::tensor {
-
-/**
- * A factory that can generate ValueBuilder
- * objects appropriate for the requested type.
- */
-struct DefaultValueBuilderFactory : eval::ValueBuilderFactory {
-private:
- DefaultValueBuilderFactory();
- static DefaultValueBuilderFactory _factory;
- ~DefaultValueBuilderFactory() override {}
-protected:
- std::unique_ptr<eval::ValueBuilderBase> create_value_builder_base(const eval::ValueType &type,
- size_t num_mapped_in, size_t subspace_size_in, size_t expect_subspaces) const override;
-public:
- static const DefaultValueBuilderFactory &get() { return _factory; }
-};
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/dense/dense_lambda_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_lambda_function.cpp
index 0005a56736d..95d90a02a9e 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_lambda_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_lambda_function.cpp
@@ -83,7 +83,7 @@ void my_compiled_lambda_op(eval::InterpretedFunction::State &state, uint64_t par
*bind_next++ = state.params->resolve(binding, state.stash).as_double();
}
auto fun = params.token->get().get_function();
- ArrayRef<CT> dst_cells = state.stash.create_array<CT>(params.num_cells);
+ ArrayRef<CT> dst_cells = state.stash.create_uninitialized_array<CT>(params.num_cells);
CT *dst = &dst_cells[0];
do {
*dst++ = fun(&args[0]);
@@ -119,7 +119,7 @@ void my_interpreted_lambda_op(eval::InterpretedFunction::State &state, uint64_t
std::vector<double> labels(params.result_type.dimensions().size(), 0.0);
ParamProxy param_proxy(labels, *state.params, params.bindings);
InterpretedFunction::Context ctx(params.fun);
- ArrayRef<CT> dst_cells = state.stash.create_array<CT>(params.num_cells);
+ ArrayRef<CT> dst_cells = state.stash.create_uninitialized_array<CT>(params.num_cells);
CT *dst = &dst_cells[0];
do {
*dst++ = params.fun.eval(ctx, param_proxy).as_double();
diff --git a/eval/src/vespa/eval/tensor/dense/dense_number_join_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_number_join_function.cpp
index d6995256411..c41743200da 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_number_join_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_number_join_function.cpp
@@ -11,6 +11,7 @@ namespace vespalib::tensor {
using vespalib::ArrayRef;
+using eval::CellType;
using eval::Value;
using eval::ValueType;
using eval::TensorFunction;
@@ -34,7 +35,7 @@ ArrayRef<CT> make_dst_cells(ConstArrayRef<CT> src_cells, Stash &stash) {
if (inplace) {
return unconstify(src_cells);
} else {
- return stash.create_array<CT>(src_cells.size());
+ return stash.create_uninitialized_array<CT>(src_cells.size());
}
}
@@ -66,7 +67,7 @@ using MyTypify = TypifyValue<TypifyCellType,TypifyOp2,TypifyBool>;
bool is_dense(const TensorFunction &tf) { return tf.result_type().is_dense(); }
bool is_double(const TensorFunction &tf) { return tf.result_type().is_double(); }
-ValueType::CellType cell_type(const TensorFunction &tf) { return tf.result_type().cell_type(); }
+CellType cell_type(const TensorFunction &tf) { return tf.result_type().cell_type(); }
} // namespace vespalib::tensor::<unnamed>
diff --git a/eval/src/vespa/eval/tensor/dense/dense_simple_join_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_simple_join_function.cpp
index 5aca3799258..f492d12f05a 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_simple_join_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_simple_join_function.cpp
@@ -14,6 +14,7 @@ namespace vespalib::tensor {
using vespalib::ArrayRef;
+using eval::CellType;
using eval::Value;
using eval::ValueType;
using eval::TensorFunction;
@@ -58,7 +59,7 @@ ArrayRef<OCT> make_dst_cells(ConstArrayRef<PCT> pri_cells, Stash &stash) {
if constexpr (pri_mut && std::is_same<PCT,OCT>::value) {
return unconstify(pri_cells);
} else {
- return stash.create_array<OCT>(pri_cells.size());
+ return stash.create_uninitialized_array<OCT>(pri_cells.size());
}
}
@@ -106,11 +107,11 @@ using MyTypify = TypifyValue<TypifyCellType,TypifyOp2,TypifyBool,TypifyOverlap>;
//-----------------------------------------------------------------------------
-bool can_use_as_output(const TensorFunction &fun, ValueType::CellType result_cell_type) {
+bool can_use_as_output(const TensorFunction &fun, CellType result_cell_type) {
return (fun.result_is_mutable() && (fun.result_type().cell_type() == result_cell_type));
}
-Primary select_primary(const TensorFunction &lhs, const TensorFunction &rhs, ValueType::CellType result_cell_type) {
+Primary select_primary(const TensorFunction &lhs, const TensorFunction &rhs, CellType result_cell_type) {
size_t lhs_size = lhs.result_type().dense_subspace_size();
size_t rhs_size = rhs.result_type().dense_subspace_size();
if (lhs_size > rhs_size) {
diff --git a/eval/src/vespa/eval/tensor/dense/dense_simple_map_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_simple_map_function.cpp
index 1086af91ec4..d3297b335d3 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_simple_map_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_simple_map_function.cpp
@@ -32,7 +32,7 @@ ArrayRef<CT> make_dst_cells(ConstArrayRef<CT> src_cells, Stash &stash) {
if (inplace) {
return unconstify(src_cells);
} else {
- return stash.create_array<CT>(src_cells.size());
+ return stash.create_uninitialized_array<CT>(src_cells.size());
}
}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp
index 1ad6e00f279..4b7f4936815 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp
@@ -23,7 +23,7 @@ template <typename CT>
void my_tensor_create_op(eval::InterpretedFunction::State &state, uint64_t param) {
const auto &self = unwrap_param<DenseTensorCreateFunction::Self>(param);
size_t pending_cells = self.result_size;
- ArrayRef<CT> cells = state.stash.create_array<CT>(pending_cells);
+ ArrayRef<CT> cells = state.stash.create_uninitialized_array<CT>(pending_cells);
while (pending_cells-- > 0) {
cells[pending_cells] = (CT) state.peek(0).as_double();
state.stack.pop_back();
@@ -85,7 +85,7 @@ DenseTensorCreateFunction::optimize(const eval::TensorFunction &expr, Stash &sta
const auto &zero_value = stash.create<DoubleValue>(0.0);
const auto &zero_node = const_value(zero_value, stash);
std::vector<Child> children(num_cells, zero_node);
- for (const auto &cell: create->spec()) {
+ for (const auto &cell: create->map()) {
size_t cell_idx = get_index(cell.first, expr.result_type());
children[cell_idx] = cell.second;
}
diff --git a/eval/src/vespa/eval/tensor/dense/onnx_wrapper.cpp b/eval/src/vespa/eval/tensor/dense/onnx_wrapper.cpp
index 5db533a4655..c49809f265f 100644
--- a/eval/src/vespa/eval/tensor/dense/onnx_wrapper.cpp
+++ b/eval/src/vespa/eval/tensor/dense/onnx_wrapper.cpp
@@ -19,6 +19,7 @@ LOG_SETUP(".eval.onnx_wrapper");
using vespalib::ArrayRef;
using vespalib::ConstArrayRef;
+using vespalib::eval::CellType;
using vespalib::eval::ValueType;
using vespalib::eval::TypifyCellType;
@@ -110,18 +111,18 @@ auto convert_optimize(Onnx::Optimize optimize) {
abort();
}
-ValueType::CellType to_cell_type(Onnx::ElementType type) {
+CellType to_cell_type(Onnx::ElementType type) {
switch (type) {
case Onnx::ElementType::INT8: [[fallthrough]];
case Onnx::ElementType::INT16: [[fallthrough]];
case Onnx::ElementType::UINT8: [[fallthrough]];
case Onnx::ElementType::UINT16: [[fallthrough]];
- case Onnx::ElementType::FLOAT: return ValueType::CellType::FLOAT;
+ case Onnx::ElementType::FLOAT: return CellType::FLOAT;
case Onnx::ElementType::INT32: [[fallthrough]];
case Onnx::ElementType::INT64: [[fallthrough]];
case Onnx::ElementType::UINT32: [[fallthrough]];
case Onnx::ElementType::UINT64: [[fallthrough]];
- case Onnx::ElementType::DOUBLE: return ValueType::CellType::DOUBLE;
+ case Onnx::ElementType::DOUBLE: return CellType::DOUBLE;
}
abort();
}
@@ -381,21 +382,21 @@ Onnx::EvalContext::convert_result(EvalContext &self, size_t idx)
struct Onnx::EvalContext::SelectAdaptParam {
template <typename ...Ts> static auto invoke() { return adapt_param<Ts...>; }
- auto operator()(eval::ValueType::CellType ct) {
+ auto operator()(eval::CellType ct) {
return typify_invoke<1,MyTypify,SelectAdaptParam>(ct);
}
};
struct Onnx::EvalContext::SelectConvertParam {
template <typename ...Ts> static auto invoke() { return convert_param<Ts...>; }
- auto operator()(eval::ValueType::CellType ct, Onnx::ElementType et) {
+ auto operator()(eval::CellType ct, Onnx::ElementType et) {
return typify_invoke<2,MyTypify,SelectConvertParam>(ct, et);
}
};
struct Onnx::EvalContext::SelectConvertResult {
template <typename ...Ts> static auto invoke() { return convert_result<Ts...>; }
- auto operator()(Onnx::ElementType et, eval::ValueType::CellType ct) {
+ auto operator()(Onnx::ElementType et, eval::CellType ct) {
return typify_invoke<2,MyTypify,SelectConvertResult>(et, ct);
}
};
diff --git a/eval/src/vespa/eval/tensor/dense/typed_cells_dispatch.h b/eval/src/vespa/eval/tensor/dense/typed_cells_dispatch.h
index 87b1a5b47ed..7a35966cef8 100644
--- a/eval/src/vespa/eval/tensor/dense/typed_cells_dispatch.h
+++ b/eval/src/vespa/eval/tensor/dense/typed_cells_dispatch.h
@@ -6,7 +6,7 @@
namespace vespalib::tensor {
-using CellType = vespalib::eval::ValueType::CellType;
+using vespalib::eval::CellType;
using TypedCells = vespalib::eval::TypedCells;
template <typename TGT, typename... Args>
@@ -30,7 +30,7 @@ decltype(auto) dispatch_2(A1 &&a, const TypedCells &b, Args &&...args) {
struct GetCell {
template<typename T>
static double call(ConstArrayRef<T> arr, size_t idx) {
- return arr[idx];
+ return arr[idx];
}
static double from(TypedCells src, size_t idx) {
return dispatch_1<GetCell>(src, idx);
diff --git a/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp b/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp
index 3c006fe64a0..ac91d073fd7 100644
--- a/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp
+++ b/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp
@@ -20,7 +20,7 @@ struct CallVectorFromDoubles {
template <typename CT>
static TypedCells
invoke(eval::InterpretedFunction::State &state, size_t numCells) {
- ArrayRef<CT> outputCells = state.stash.create_array<CT>(numCells);
+ ArrayRef<CT> outputCells = state.stash.create_uninitialized_array<CT>(numCells);
for (size_t i = numCells; i-- > 0; ) {
outputCells[i] = (CT) state.peek(0).as_double();
state.stack.pop_back();
diff --git a/eval/src/vespa/eval/tensor/mixed/CMakeLists.txt b/eval/src/vespa/eval/tensor/mixed/CMakeLists.txt
deleted file mode 100644
index ceded3a7380..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-vespa_add_library(eval_tensor_mixed OBJECT
- SOURCES
- packed_labels.cpp
- packed_mappings.cpp
- packed_mappings_builder.cpp
- packed_mixed_tensor_builder_factory.cpp
- packed_mixed_tensor.cpp
- packed_mixed_tensor_builder.cpp
-)
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_labels.cpp b/eval/src/vespa/eval/tensor/mixed/packed_labels.cpp
deleted file mode 100644
index 07c07bbb989..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_labels.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "packed_labels.h"
-#include <assert.h>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-int32_t
-PackedLabels::find_label(vespalib::stringref to_find) const
-{
- uint32_t lo = 0;
- uint32_t hi = num_labels();
- while (lo < hi) {
- uint32_t mid = (lo + hi) / 2;
- if (get_label(mid) < to_find) {
- lo = mid + 1;
- } else {
- hi = mid;
- }
- }
- assert(lo == hi);
- if (lo < num_labels() && get_label(lo) == to_find) {
- return lo;
- }
- return -1;
-}
-
-vespalib::stringref
-PackedLabels::get_label(uint32_t index) const
-{
- assert(index < num_labels());
-
- uint32_t this_offset = _offsets[index];
- uint32_t next_offset = _offsets[index+1];
- auto p = &_label_store[this_offset];
- size_t sz = next_offset - this_offset - 1;
- return vespalib::stringref(p, sz);
-}
-
-MemoryUsage
-PackedLabels::extra_memory_usage() const
-{
- MemoryUsage extra_usage;
- size_t offsets_size = _offsets.size() * sizeof(uint32_t);
- size_t labels_size = _label_store.size() * sizeof(char);
- extra_usage.merge(MemoryUsage(offsets_size, offsets_size, 0, 0));
- extra_usage.merge(MemoryUsage(labels_size, labels_size, 0, 0));
- return extra_usage;
-}
-
-void
-PackedLabels::validate_labels(uint32_t num_labels)
-{
- assert(num_labels == _offsets.size()-1);
- for (uint32_t i = 0; i < num_labels; ++i) {
- assert(_offsets[i] < _offsets[i+1]);
- uint32_t last_byte_index = _offsets[i+1] - 1;
- assert(last_byte_index < _label_store.size());
- assert(_label_store[last_byte_index] == 0);
- }
- assert(_label_store.size() == _offsets[num_labels]);
- for (uint32_t i = 0; i+1 < num_labels; ++i) {
- assert(get_label(i) < get_label(i+1));
- }
-}
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_labels.h b/eval/src/vespa/eval/tensor/mixed/packed_labels.h
deleted file mode 100644
index 86024708568..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_labels.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include <vespa/eval/eval/memory_usage_stuff.h>
-#include <vespa/vespalib/stllike/string.h>
-#include <vespa/vespalib/util/arrayref.h>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-/**
- * Stores labels for sparse (mapped) tensor dimensions,
- * where each unique label value is stored only once,
- * and the values are sorted. References data that
- * must be constant and owned by some object with
- * enclosing lifetime.
- **/
-class PackedLabels {
-public:
- PackedLabels(uint32_t num_labels,
- ConstArrayRef<uint32_t> offsets,
- ConstArrayRef<char> label_store)
- : _offsets(offsets),
- _label_store(label_store)
- {
- validate_labels(num_labels);
- }
-
- uint32_t num_labels() const { return _offsets.size() - 1; }
-
- // returns -1 if the given label value cannot be found
- int32_t find_label(vespalib::stringref value) const;
-
- vespalib::stringref get_label(uint32_t index) const;
-
- MemoryUsage extra_memory_usage() const;
-private:
- const ConstArrayRef<uint32_t> _offsets;
- const ConstArrayRef<char> _label_store;
-
- void validate_labels(uint32_t num_labels);
-};
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mappings.cpp b/eval/src/vespa/eval/tensor/mixed/packed_mappings.cpp
deleted file mode 100644
index 9a3112f2a4f..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mappings.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "packed_mappings.h"
-#include <assert.h>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-
-int32_t
-PackedMappings::subspace_of_address(const Address &address) const
-{
- int32_t idx = sortid_of_address(address);
- if (idx < 0) {
- return -1;
- }
- uint32_t internal_idx = idx;
- assert (internal_idx < _num_mappings);
- return subspace_of_sortid(internal_idx);
-}
-
-int32_t
-PackedMappings::subspace_of_enums(const InternalAddress &address) const
-{
- int32_t idx = sortid_of_enums(address);
- if (idx < 0) {
- return -1;
- }
- uint32_t internal_idx = idx;
- assert (internal_idx < _num_mappings);
- return subspace_of_sortid(internal_idx);
-}
-
-int32_t
-PackedMappings::sortid_of_address(const Address &address) const
-{
- if (_num_dims == 0) return 0;
- assert(address.size() == _num_dims);
- std::vector<uint32_t> to_find;
- to_find.reserve(_num_dims);
- for (const auto & label_value : address) {
- int32_t label_idx = _label_store.find_label(label_value);
- if (label_idx < 0) {
- return -1;
- }
- to_find.push_back(label_idx);
- }
- return sortid_of_enums(to_find);
-}
-
-int32_t
-PackedMappings::sortid_of_enums(const InternalAddress &address) const
-{
- if (_num_dims == 0) return 0;
- assert(address.size() == _num_dims);
- const uint32_t * to_find = &address[0];
- uint32_t lo = 0;
- uint32_t hi = _num_mappings;
- while (lo < hi) {
- uint32_t mid = (lo + hi) / 2;
- if (enums_compare(ptr_of_sortid(mid), to_find) < 0) {
- lo = mid + 1;
- } else {
- hi = mid;
- }
- }
- assert(lo == hi);
- if ((lo < _num_mappings) &&
- (enums_compare(ptr_of_sortid(lo), to_find) == 0))
- {
- return lo;
- }
- return -1;
-}
-
-/** returns subspace_index */
-uint32_t
-PackedMappings::fill_enums_by_sortid(uint32_t internal_index, InternalAddress &address) const
-{
- assert(internal_index < _num_mappings);
- uint32_t offset = offset_of_mapping_data(internal_index);
- address.resize(_num_dims);
- for (uint32_t i = 0; i < _num_dims; ++i) {
- address[i] = _int_store[offset++];
- }
- return _int_store[offset];
-}
-
-/** returns subspace_index */
-uint32_t
-PackedMappings::fill_address_by_sortid(uint32_t internal_index, Address &address) const
-{
- assert(internal_index < _num_mappings);
- uint32_t offset = offset_of_mapping_data(internal_index);
- address.resize(_num_dims);
- for (uint32_t i = 0; i < _num_dims; ++i) {
- uint32_t label_idx = _int_store[offset++];
- address[i] = _label_store.get_label(label_idx);
- }
- return _int_store[offset];
-}
-
-MemoryUsage
-PackedMappings::extra_memory_usage() const
-{
- MemoryUsage extra_usage;
- size_t store_size = _int_store.size() * sizeof(uint32_t);
- extra_usage.merge(MemoryUsage(store_size, store_size, 0, 0));
- extra_usage.merge(_label_store.extra_memory_usage());
- return extra_usage;
-}
-
-void
-PackedMappings::validate() const
-{
- assert((_num_mappings * (1 + _num_dims)) == _int_store.size());
- auto iter = _int_store.cbegin();
- std::vector<uint32_t> prev;
- std::vector<uint32_t> next;
- for (uint32_t i = 0; i < _num_mappings; ++i) {
- next.clear();
- for (uint32_t j = 0; j < _num_dims; ++j) {
- uint32_t label_index = *iter++;
- next.push_back(label_index);
- assert(label_index < _label_store.num_labels());
- }
- if (_num_dims == 0) {
- assert(next == prev);
- assert(i == 0);
- assert(_num_mappings == 1);
- } else {
- assert(prev < next);
- }
- std::swap(prev, next);
- uint32_t subspace_index = *iter++;
- assert(subspace_index < _num_mappings);
- }
- assert(iter == _int_store.cend());
-}
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mappings.h b/eval/src/vespa/eval/tensor/mixed/packed_mappings.h
deleted file mode 100644
index 2c9f4b04a00..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mappings.h
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include "packed_labels.h"
-#include <vector>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-/**
- * Mappings for sparse tensor dimensions.
- *
- * Each address (conceptually "array of string")
- * maps to a "subspace" (currently in the
- * order that addresses were added to a builder).
- *
- * Internally addresses are lexicographically
- * sorted, and you can iterate over them in sort
- * order with the fill_*() methods.
- *
- * (Note: we may want to change this so subspaces
- * are always sorted by address, so the "subspace"
- * index and the "sortid" index become equivalent).
- *
- * Allows using the internal label enumerations
- * instead of working with strings all the time.
- *
- * NOTE: Making a copy of PackedMappings will not copy
- * the underlying data, these must then stay alive
- * and unchanged for the lifetime of the copy as well.
- **/
-class PackedMappings {
-public:
- using Address = std::vector<vespalib::stringref>;
- using InternalAddress = std::vector<uint32_t>;
-
- uint32_t size() const { return _num_mappings; }
- uint32_t num_mapped_dims() const { return _num_dims; }
-
- // returns -1 if mapping does not contain address
- int32_t subspace_of_enums(const InternalAddress &address) const;
- int32_t subspace_of_address(const Address &address) const;
-
- /** returns "subspace" index */
- uint32_t fill_address_by_sortid(uint32_t sortid, Address &address) const;
- uint32_t fill_enums_by_sortid(uint32_t sortid, InternalAddress &address) const;
-
- // mapping from label enum to stringref (and vice versa)
- const PackedLabels & label_store() const { return _label_store; }
-
- MemoryUsage extra_memory_usage() const;
-
-private:
- PackedMappings(uint32_t num_dims, uint32_t num_mappings,
- ConstArrayRef<uint32_t> int_store,
- PackedLabels label_store)
- : _num_dims(num_dims),
- _num_mappings(num_mappings),
- _int_store(int_store),
- _label_store(label_store)
- {
- validate();
- }
- friend class PackedMappingsBuilder;
-
- void validate() const;
-
- const uint32_t _num_dims;
- const uint32_t _num_mappings;
- /*
- _int_store contains data corresponding to this model:
- struct IntStore {
- // sorted lexicographically by label_enums:
- struct MappingData {
- uint32_t label_enums[num_dims];
- uint32_t subspace_index;
- } mappings[num_mappings];
- };
- */
- const ConstArrayRef<uint32_t> _int_store;
- const PackedLabels _label_store;
-
- int enums_compare(const uint32_t *a, const uint32_t *b) const {
- for (size_t i = 0; i < _num_dims; ++i) {
- if (a[i] < b[i]) return -1;
- if (a[i] > b[i]) return 1;
- }
- return 0;
- }
-
- uint32_t offset_of_mapping_data(uint32_t idx) const {
- return (idx * (1 + _num_dims));
- }
- uint32_t subspace_of_sortid(uint32_t internal_index) const {
- uint32_t offset = offset_of_mapping_data(internal_index);
- return _int_store[offset + _num_dims];
- }
- const uint32_t * ptr_of_sortid(uint32_t internal_index) const {
- return &_int_store[offset_of_mapping_data(internal_index)];
- }
-
- int32_t sortid_of_address(const Address &address) const;
- int32_t sortid_of_enums(const InternalAddress &address) const;
-public:
- static void operator delete(void *ptr, size_t sz) {
- if (sz != sizeof(PackedMappings)) {
- abort();
- }
- size_t extra = ((const PackedMappings *)ptr)->extra_memory_usage().usedBytes();
- ::operator delete(ptr, sz + extra);
- }
-};
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.cpp b/eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.cpp
deleted file mode 100644
index abf142f9650..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "packed_mappings_builder.h"
-#include <assert.h>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-PackedMappingsBuilder::~PackedMappingsBuilder() = default;
-
-uint32_t
-PackedMappingsBuilder::add_mapping_for(ConstArrayRef<vespalib::stringref> address_in)
-{
- SparseAddress address;
- for (auto & label_value : address_in) {
- // store label string in our own set:
- auto iter = _labels.insert(label_value).first;
- address.push_back(*iter);
- }
- assert(address.size() == _num_dims);
- uint32_t next_index = _mappings.size();
- auto iter = _mappings.emplace(address, next_index).first;
- return iter->second;
-}
-
-
-size_t
-PackedMappingsBuilder::extra_memory() const
-{
- size_t int_store_cnt = (1 + _num_dims) * _mappings.size();
- size_t int_store_size = int_store_cnt * sizeof(uint32_t);
- size_t label_cnt = _labels.size();
- size_t label_offsets_size = (1 + label_cnt) * sizeof(uint32_t);
- size_t label_bytes = 0;
- for (const auto & label_value : _labels) {
- label_bytes += (label_value.size() + 1);
- }
- size_t extra_size = int_store_size + label_offsets_size + label_bytes;
- return extra_size;
-}
-
-PackedMappings
-PackedMappingsBuilder::target_memory(char *mem_start, char *mem_end) const
-{
- size_t int_store_cnt = (1 + _num_dims) * _mappings.size();
- size_t int_store_size = int_store_cnt * sizeof(uint32_t);
- size_t label_cnt = _labels.size();
- size_t label_offsets_size = (1 + label_cnt) * sizeof(uint32_t);
-
- size_t label_bytes = 0;
- for (const auto & label_value : _labels) {
- label_bytes += (label_value.size() + 1);
- }
-
- ssize_t needs_sz = int_store_size + label_offsets_size + label_bytes;
- ssize_t avail_sz = mem_end - mem_start;
- assert(needs_sz <= avail_sz);
-
- uint32_t * int_store_mem = (uint32_t *) (void *) mem_start;
- uint32_t * offsets_mem = (uint32_t *) (void *) (mem_start + int_store_size);
- char * labels_mem = mem_start + int_store_size + label_offsets_size;
-
- ArrayRef<uint32_t> int_store_data(int_store_mem, int_store_cnt);
- ArrayRef<uint32_t> label_offsets(offsets_mem, 1 + label_cnt);
- ArrayRef<char> labels_data(labels_mem, label_bytes);
- assert(labels_data.end() <= mem_end);
-
- size_t byte_idx = 0;
- size_t label_num = 0;
- for (const auto & label_value : _labels) {
- label_offsets[label_num++] = byte_idx;
- size_t len_with_zero = label_value.size() + 1;
- memcpy(&labels_data[byte_idx], label_value.data(), len_with_zero);
- byte_idx += len_with_zero;
- }
- assert(label_num == label_cnt);
- label_offsets[label_num] = byte_idx;
-
- assert(labels_data.begin() + byte_idx == labels_data.end());
-
- PackedLabels stored_labels(label_cnt, label_offsets, labels_data);
-
- size_t int_store_offset = 0;
- for (const auto & kv : _mappings) {
- const SparseAddress & k = kv.first;
- uint32_t v = kv.second;
- for (const auto & label_value : k) {
- int32_t label_idx = stored_labels.find_label(label_value);
- assert(label_idx >= 0);
- assert(uint32_t(label_idx) < label_num);
- int_store_data[int_store_offset++] = label_idx;
- }
- int_store_data[int_store_offset++] = v;
- }
- assert(int_store_offset == int_store_cnt);
-
- return PackedMappings(_num_dims, _mappings.size(),
- int_store_data, stored_labels);
-}
-
-std::unique_ptr<PackedMappings>
-PackedMappingsBuilder::build_mappings() const
-{
- size_t self_size = sizeof(PackedMappings);
- size_t total_size = self_size + extra_memory();
-
- char * mem = (char *) operator new(total_size);
- auto self_data = target_memory(mem + self_size, mem + total_size);
-
- PackedMappings * built = new (mem) PackedMappings(self_data);
-
- return std::unique_ptr<PackedMappings>(built);
-}
-
-} // namespace
-
-
-
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.h b/eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.h
deleted file mode 100644
index 01f16a8b8e1..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mappings_builder.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include "packed_mappings.h"
-#include <vespa/vespalib/stllike/string.h>
-#include <map>
-#include <memory>
-#include <set>
-#include <vector>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-/**
- * Builder for PackedMappings.
- * Copies label values in all addresses added
- * and packs all resulting data into a block of memory
- * held by the built object, usually part of a larger
- * aggregating object by using target_memory() method.
- **/
-class PackedMappingsBuilder {
-public:
- PackedMappingsBuilder(uint32_t num_mapped_dims)
- : _num_dims(num_mapped_dims),
- _labels(),
- _mappings()
- {}
-
- ~PackedMappingsBuilder();
-
- // returns a new index for new addresses
- // may be called multiple times with same address,
- // will then return the same index for that address.
- uint32_t add_mapping_for(ConstArrayRef<vespalib::stringref> address);
-
- // how much extra memory is needed by target_memory
- // not including sizeof(PackedMappings)
- size_t extra_memory() const;
-
- // put data that PackedMappings can refer to in the given
- // memory block, and return an object referring to it.
- PackedMappings target_memory(char *mem_start, char *mem_end) const;
-
- // number of dimensions
- uint32_t num_mapped_dims() const { return _num_dims; }
-
- // how many unique addresses have been added?
- size_t size() const { return _mappings.size(); }
-
- // build a self-contained PackedMappings object;
- // used for unit testing.
- std::unique_ptr<PackedMappings> build_mappings() const;
-
-private:
- uint32_t _num_dims;
- std::set<vespalib::string> _labels;
- using SparseAddress = std::vector<vespalib::stringref>;
- using IndexMap = std::map<SparseAddress, uint32_t>;
- IndexMap _mappings;
-};
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.cpp b/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.cpp
deleted file mode 100644
index e22828cbf0c..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "packed_mixed_tensor.h"
-#include <vespa/vespalib/util/typify.h>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-namespace {
-
-struct MySize {
- template <typename CT> static size_t invoke(TypedCells cells) {
- return (sizeof(CT) * cells.size);
- }
-};
-
-} // namespace <unnamed>
-
-/*********************************************************************************/
-
-class PackedMixedTensorIndexView : public Value::Index::View
-{
-private:
- const PackedMappings& _mappings;
- const std::vector<size_t> _view_dims;
- std::vector<uint32_t> _lookup_enums;
- std::vector<uint32_t> _full_enums;
- size_t _index;
-
- size_t num_full_dims() const { return _mappings.num_mapped_dims(); }
- size_t num_view_dims() const { return _view_dims.size(); }
- size_t num_rest_dims() const { return num_full_dims() - num_view_dims(); }
-public:
- PackedMixedTensorIndexView(const PackedMappings& mappings,
- const std::vector<size_t> &dims)
- : _mappings(mappings),
- _view_dims(dims),
- _lookup_enums(),
- _index(0)
- {
- _lookup_enums.reserve(num_view_dims());
- _full_enums.resize(num_full_dims());
- }
-
- void lookup(ConstArrayRef<const vespalib::stringref*> addr) override;
- bool next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out) override;
- ~PackedMixedTensorIndexView() override = default;
-};
-
-void
-PackedMixedTensorIndexView::lookup(ConstArrayRef<const vespalib::stringref*> addr)
-{
- _index = 0;
- assert(addr.size() == num_view_dims());
- _lookup_enums.clear();
- for (const vespalib::stringref * label_ptr : addr) {
- int32_t label_enum = _mappings.label_store().find_label(*label_ptr);
- if (label_enum < 0) {
- // cannot match
- _index = _mappings.size();
- break;
- }
- _lookup_enums.push_back(label_enum);
- }
-}
-
-bool
-PackedMixedTensorIndexView::next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out)
-{
- assert(addr_out.size() == num_rest_dims());
- while (_index < _mappings.size()) {
- idx_out = _mappings.fill_enums_by_sortid(_index++, _full_enums);
- bool couldmatch = true;
- size_t vd_idx = 0;
- size_t ao_idx = 0;
- for (size_t i = 0; i < num_full_dims(); ++i) {
- if (vd_idx < num_view_dims()) {
- size_t next_view_dim = _view_dims[vd_idx];
- if (i == next_view_dim) {
- if (_lookup_enums[vd_idx] == _full_enums[i]) {
- // match in this dimension
- ++vd_idx;
- continue;
- } else {
- // does not match
- couldmatch = false;
- break;
- }
- }
- }
- // not a view dimension:
- uint32_t label_enum = _full_enums[i];
- *addr_out[ao_idx] = _mappings.label_store().get_label(label_enum);
- ++ao_idx;
- }
- if (couldmatch) {
- assert(vd_idx == num_view_dims());
- assert(ao_idx == num_rest_dims());
- return true;
- }
- }
- return false;
-}
-
-/*********************************************************************************/
-
-class PackedMixedTensorLookup : public Value::Index::View
-{
-private:
- const PackedMappings& _mappings;
- std::vector<uint32_t> _lookup_enums;
- bool _first_time;
-
- size_t num_full_dims() const { return _mappings.num_mapped_dims(); }
-public:
- PackedMixedTensorLookup(const PackedMappings& mappings)
- : _mappings(mappings),
- _lookup_enums(),
- _first_time(false)
- {
- _lookup_enums.reserve(num_full_dims());
- }
-
- void lookup(ConstArrayRef<const vespalib::stringref*> addr) override;
- bool next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out) override;
- ~PackedMixedTensorLookup() override = default;
-};
-
-void
-PackedMixedTensorLookup::lookup(ConstArrayRef<const vespalib::stringref*> addr)
-{
- assert(addr.size() == num_full_dims());
- _lookup_enums.clear();
- for (const vespalib::stringref * label_ptr : addr) {
- int32_t label_enum = _mappings.label_store().find_label(*label_ptr);
- if (label_enum < 0) {
- // cannot match
- _first_time = false;
- return;
- }
- _lookup_enums.push_back(label_enum);
- }
- _first_time = true;
-}
-
-bool
-PackedMixedTensorLookup::next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out)
-{
- assert(addr_out.size() == 0);
- if (_first_time) {
- _first_time = false;
- int32_t subspace = _mappings.subspace_of_enums(_lookup_enums);
- if (subspace >= 0) {
- idx_out = subspace;
- return true;
- }
- }
- return false;
-}
-
-/*********************************************************************************/
-
-class PackedMixedTensorAllMappings : public Value::Index::View
-{
-private:
- const PackedMappings& _mappings;
- std::vector<vespalib::stringref> _full_address;
- size_t _index;
-
-public:
- PackedMixedTensorAllMappings(const PackedMappings& mappings)
- : _mappings(mappings),
- _full_address(),
- _index(0)
- {
- _full_address.resize(_mappings.num_mapped_dims());
- }
-
- void lookup(ConstArrayRef<const vespalib::stringref*> addr) override;
- bool next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out) override;
- ~PackedMixedTensorAllMappings() override = default;
-};
-
-void
-PackedMixedTensorAllMappings::lookup(ConstArrayRef<const vespalib::stringref*> addr)
-{
- _index = 0;
- assert(addr.size() == 0);
-}
-
-bool
-PackedMixedTensorAllMappings::next_result(ConstArrayRef<vespalib::stringref*> addr_out, size_t &idx_out)
-{
- assert(addr_out.size() == _mappings.num_mapped_dims());
- while (_index < _mappings.size()) {
- idx_out = _mappings.fill_address_by_sortid(_index++, _full_address);
- for (size_t i = 0; i < _mappings.num_mapped_dims(); ++i) {
- *addr_out[i] = _full_address[i];
- }
- return true;
- }
- return false;
-}
-
-/*********************************************************************************/
-
-PackedMixedTensor::PackedMixedTensor(const ValueType &type,
- TypedCells cells,
- const PackedMappings &mappings)
- : _type(type),
- _cells(cells),
- _mappings(mappings)
-{
- assert(type.cell_type() == _cells.type);
-}
-
-PackedMixedTensor::~PackedMixedTensor() = default;
-
-MemoryUsage
-PackedMixedTensor::get_memory_usage() const {
- MemoryUsage usage = self_memory_usage<PackedMixedTensor>();
- usage.merge(_mappings.extra_memory_usage());
- size_t plus = add_for_alignment(usage.usedBytes());
- usage.merge(MemoryUsage(plus, plus, 0, 0));
- size_t cells_size = typify_invoke<1,TypifyCellType,MySize>(_cells.type, _cells);
- usage.merge(MemoryUsage(cells_size, cells_size, 0, 0));
- return usage;
-}
-
-std::unique_ptr<Value::Index::View>
-PackedMixedTensor::create_view(const std::vector<size_t> &dims) const
-{
- if (dims.size() == 0) {
- return std::make_unique<PackedMixedTensorAllMappings>(_mappings);
- }
- for (size_t i = 1; i < dims.size(); ++i) {
- assert(dims[i-1] < dims[i]);
- assert(dims[i] < _mappings.num_mapped_dims());
- }
- if (dims.size() == _mappings.num_mapped_dims()) {
- return std::make_unique<PackedMixedTensorLookup>(_mappings);
- }
- return std::make_unique<PackedMixedTensorIndexView>(_mappings, dims);
-}
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.h b/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.h
deleted file mode 100644
index 013d2d7e07c..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include <vespa/eval/eval/value.h>
-#include <vespa/eval/eval/value_type.h>
-#include <vespa/eval/eval/simple_value.h>
-
-#include <vespa/eval/tensor/mixed/packed_mappings.h>
-#include <vespa/eval/tensor/mixed/packed_mappings_builder.h>
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-/**
- * An implementation of Value modeling a mixed tensor,
- * where all the data (cells and sparse address mappings)
- * can reside in a self-contained, contigous block of memory.
- * Currently must be built by a PackedMixedTensorBuilder.
- * Immutable (all data always const).
- **/
-class PackedMixedTensor : public Value, public Value::Index
-{
-private:
- const ValueType _type;
- const TypedCells _cells;
- const PackedMappings _mappings;
-
- PackedMixedTensor(const ValueType &type,
- TypedCells cells,
- const PackedMappings &mappings);
-
- template<typename T> friend class PackedMixedTensorBuilder;
-
-public:
- ~PackedMixedTensor() override;
-
- // Value API:
- const ValueType &type() const override { return _type; }
- const Value::Index &index() const override { return *this; }
- TypedCells cells() const override { return _cells; }
-
- MemoryUsage get_memory_usage() const override;
-
- // Value::Index API:
- size_t size() const override { return _mappings.size(); }
- std::unique_ptr<View> create_view(const std::vector<size_t> &dims) const override;
-
- // memory management:
- static size_t add_for_alignment(size_t sz) {
- size_t unalign = sz & 15;
- return (unalign == 0) ? unalign : (16 - unalign);
- }
- static void operator delete(void *ptr, size_t sz) {
- if (sz != sizeof(PackedMixedTensor)) {
- abort();
- }
- size_t allocated_sz = ((PackedMixedTensor *)ptr)->get_memory_usage().usedBytes();
- ::operator delete(ptr, allocated_sz);
- }
-};
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.cpp b/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.cpp
deleted file mode 100644
index ef08ed20a9d..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "packed_mixed_tensor_builder.h"
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-template <typename T>
-ArrayRef<T>
-PackedMixedTensorBuilder<T>::add_subspace(ConstArrayRef<vespalib::stringref> addr_in)
-{
- std::vector<vespalib::stringref> addr(addr_in.begin(), addr_in.end());
- uint32_t idx = _mappings_builder.add_mapping_for(addr);
- size_t offset = idx * _subspace_size;
- assert(offset == _cells.size());
- _cells.resize(offset + _subspace_size);
- return ArrayRef<T>(&_cells[offset], _subspace_size);
-}
-
-
-template <typename T>
-std::unique_ptr<Value>
-PackedMixedTensorBuilder<T>::build(std::unique_ptr<ValueBuilder<T>>)
-{
- size_t self_size = sizeof(PackedMixedTensor);
- size_t mappings_size = _mappings_builder.extra_memory();
- // align cells:
- mappings_size += PackedMixedTensor::add_for_alignment(self_size + mappings_size);
- size_t cells_size = sizeof(T) * _cells.size();
- size_t total_size = self_size + mappings_size + cells_size;
-
- char *mem = (char *) operator new(total_size);
- char *mappings_mem = mem + self_size;
- char *cells_mem = mappings_mem + mappings_size;
-
- // fill mapping data:
- auto mappings = _mappings_builder.target_memory(mappings_mem, cells_mem);
-
- // copy cells:
- memcpy(cells_mem, &_cells[0], cells_size);
- ConstArrayRef<T> cells((T *)(void *) cells_mem, _cells.size());
-
- PackedMixedTensor * built =
- new (mem) PackedMixedTensor(_type, TypedCells(cells), mappings);
-
- return std::unique_ptr<PackedMixedTensor>(built);
-}
-
-template class PackedMixedTensorBuilder<float>;
-template class PackedMixedTensorBuilder<double>;
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.h b/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.h
deleted file mode 100644
index a683b82dd24..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include "packed_mixed_tensor.h"
-
-namespace vespalib::eval::packed_mixed_tensor {
-
-/**
- * A builder for PackedMixedTensor objects
- * appropriate for cell type T.
- **/
-template <typename T>
-class PackedMixedTensorBuilder : public ValueBuilder<T>
-{
-private:
- const ValueType & _type;
- size_t _subspace_size;
- std::vector<T> _cells;
- PackedMappingsBuilder _mappings_builder;
-public:
- PackedMixedTensorBuilder(const ValueType &type,
- size_t num_mapped_in,
- size_t subspace_size_in,
- size_t expected_subspaces)
- : _type(type),
- _subspace_size(subspace_size_in),
- _cells(),
- _mappings_builder(num_mapped_in)
- {
- _cells.reserve(_subspace_size * expected_subspaces);
- }
-
- ~PackedMixedTensorBuilder() override = default;
-
- ArrayRef<T> add_subspace(ConstArrayRef<vespalib::stringref> addr) override;
- std::unique_ptr<Value> build(std::unique_ptr<ValueBuilder<T>> self) override;
-};
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.cpp b/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.cpp
deleted file mode 100644
index 48eedd86f7f..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "packed_mixed_tensor_builder_factory.h"
-#include "packed_mixed_tensor_builder.h"
-
-#include <vespa/vespalib/util/typify.h>
-
-namespace vespalib::eval {
-
-namespace {
-
-struct CreatePackedMixedTensorBuilder {
- template <typename T, typename ...Args>
- static std::unique_ptr<ValueBuilderBase> invoke(const ValueType &type, Args &&...args)
- {
- assert(check_cell_type<T>(type.cell_type()));
- return std::make_unique<packed_mixed_tensor::PackedMixedTensorBuilder<T>>(type, std::forward<Args>(args)...);
- }
-};
-
-} // namespace <unnamed>
-
-PackedMixedTensorBuilderFactory::PackedMixedTensorBuilderFactory() = default;
-PackedMixedTensorBuilderFactory PackedMixedTensorBuilderFactory::_factory;
-
-std::unique_ptr<ValueBuilderBase>
-PackedMixedTensorBuilderFactory::create_value_builder_base(const ValueType &type,
- size_t num_mapped_in,
- size_t subspace_size_in,
- size_t expected_subspaces) const
-{
- return typify_invoke<1,TypifyCellType,CreatePackedMixedTensorBuilder>(type.cell_type(),
- type, num_mapped_in, subspace_size_in, expected_subspaces);
-}
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h b/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h
deleted file mode 100644
index 20a581e2b35..00000000000
--- a/eval/src/vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include <vespa/eval/eval/value.h>
-#include <vespa/eval/eval/value_type.h>
-#include <vespa/eval/eval/simple_value.h>
-
-namespace vespalib::eval {
-
-/**
- * A factory that can generate PackedMixedTensorBuilder
- * objects appropriate for the requested CellType.
- */
-struct PackedMixedTensorBuilderFactory : ValueBuilderFactory {
-private:
- PackedMixedTensorBuilderFactory();
- static PackedMixedTensorBuilderFactory _factory;
- ~PackedMixedTensorBuilderFactory() override {}
-protected:
- std::unique_ptr<ValueBuilderBase> create_value_builder_base(const ValueType &type,
- size_t num_mapped_in, size_t subspace_size_in, size_t expect_subspaces) const override;
-public:
- static const PackedMixedTensorBuilderFactory &get() { return _factory; }
-};
-
-} // namespace
diff --git a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp
index 13c4711668b..837b135c0aa 100644
--- a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp
+++ b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp
@@ -8,7 +8,7 @@
using vespalib::nbostream;
using vespalib::eval::ValueType;
-using CellType = vespalib::eval::ValueType::CellType;
+using vespalib::eval::CellType;
namespace vespalib::tensor {
diff --git a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h
index 21618dcb6ce..f0516e9fcc9 100644
--- a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h
+++ b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h
@@ -18,7 +18,7 @@ class DenseTensorView;
class DenseBinaryFormat
{
public:
- using CellType = eval::ValueType::CellType;
+ using CellType = vespalib::eval::CellType;
static void serialize(nbostream &stream, const DenseTensorView &tensor);
static std::unique_ptr<DenseTensorView> deserialize(nbostream &stream, CellType cell_type);
diff --git a/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.cpp b/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.cpp
index eda8f7eecc7..a4022c4f60a 100644
--- a/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.cpp
+++ b/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.cpp
@@ -12,8 +12,8 @@
#include <cassert>
using vespalib::nbostream;
+using vespalib::eval::CellType;
using vespalib::eval::ValueType;
-using CellType = vespalib::eval::ValueType::CellType;
namespace vespalib::tensor {
diff --git a/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.h b/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.h
index 0611d7d5a23..d4c7fa4bf6f 100644
--- a/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.h
+++ b/eval/src/vespa/eval/tensor/serialization/sparse_binary_format.h
@@ -17,7 +17,7 @@ class Tensor;
class SparseBinaryFormat
{
public:
- using CellType = eval::ValueType::CellType;
+ using CellType = eval::CellType;
static void serialize(nbostream &stream, const Tensor &tensor);
static std::unique_ptr<Tensor> deserialize(nbostream &stream, CellType cell_type);
diff --git a/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp b/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
index 758ceb43ab4..2d3d1f4a0ea 100644
--- a/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
+++ b/eval/src/vespa/eval/tensor/serialization/typed_binary_format.cpp
@@ -19,7 +19,7 @@ LOG_SETUP(".eval.tensor.serialization.typed_binary_format");
using vespalib::nbostream;
using vespalib::eval::ValueType;
-using CellType = vespalib::eval::ValueType::CellType;
+using vespalib::eval::CellType;
namespace vespalib::tensor {
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 cd60a082472..0ecf957d1d9 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -330,12 +330,6 @@ public class Flags {
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
- public static final UnboundBooleanFlag REGIONAL_CONTAINER_REGISTRY = defineFeatureFlag(
- "regional-container-registry",
- true,
- "Whether host-admin should download images from the zone's regional container registry",
- "Takes effect immediately");
-
public static final UnboundBooleanFlag ENABLE_AUTOMATIC_REINDEXING = defineFeatureFlag(
"enable-automatic-reindexing",
false,
@@ -343,6 +337,20 @@ public class Flags {
"Takes effect on next internal redeployment",
APPLICATION_ID);
+ public static final UnboundBooleanFlag USE_POWER_OF_TWO_CHOICES_LOAD_BALANCING = defineFeatureFlag(
+ "use-power-of-two-choices-load-balancing",
+ false,
+ "Whether to use Power of two load balancing algorithm for application",
+ "Takes effect on next internal redeployment",
+ APPLICATION_ID);
+
+ public static final UnboundBooleanFlag DYNAMIC_RECONFIGURATION_OF_ZOOKEEPER_CLUSTER = defineFeatureFlag(
+ "dynamic-reconfiguration-of-zookeeper-cluster",
+ false,
+ "Whether to allow dynamic reconfiguration of zookeeper cluster",
+ "Takes effect on next deployment",
+ 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 d074915a023..3a848b33c76 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
@@ -281,12 +281,12 @@ public abstract class ControllerHttpClient {
if (response.statusCode() / 100 == 4)
throw new IllegalArgumentException("Bad request for " + request + ": " + message);
- throw new IOException("Failed " + request + ": " + message);
+ throw new IOException(message);
}
catch (IOException e) { // Catches the above, and timeout exceptions from the client.
if (thrown == null)
- thrown = new UncheckedIOException(e);
+ thrown = new UncheckedIOException("Failed " + request + ": " + e, e);
else
thrown.addSuppressed(e);
diff --git a/http-utils/pom.xml b/http-utils/pom.xml
index 6d2e009cf8c..1f85658430f 100644
--- a/http-utils/pom.xml
+++ b/http-utils/pom.xml
@@ -26,6 +26,11 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<!-- compile scope -->
<dependency>
@@ -38,6 +43,17 @@
<artifactId>httpcore</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
<!-- test scope -->
<dependency>
diff --git a/http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java b/http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java
new file mode 100644
index 00000000000..6c53ea0dc69
--- /dev/null
+++ b/http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java
@@ -0,0 +1,96 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.util.http;
+
+import com.yahoo.security.tls.MixedMode;
+import com.yahoo.security.tls.TlsContext;
+import com.yahoo.security.tls.TransportSecurityUtils;
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
+import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
+import org.apache.hc.client5.http.routing.HttpRoutePlanner;
+import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+import javax.net.ssl.SSLParameters;
+
+/**
+ * Async http client builder for internal Vespa communications over http/https.
+ * Configures Vespa mTLS and handles TLS mixed mode automatically.
+ * Client should only be used for requests to Vespa services.
+ *
+ * Caveats:
+ * - custom connection manager must be configured through {@link #create(AsyncConnectionManagerFactory)}.
+ *
+ * @author bjorncs
+ */
+public class VespaAsyncHttpClientBuilder {
+
+ public interface AsyncConnectionManagerFactory {
+ AsyncClientConnectionManager create(TlsStrategy tlsStrategy);
+ }
+
+ public static HttpAsyncClientBuilder create() {
+ return create(
+ tlsStrategy -> PoolingAsyncClientConnectionManagerBuilder.create()
+ .setTlsStrategy(tlsStrategy)
+ .build());
+ }
+
+ public static HttpAsyncClientBuilder create(AsyncConnectionManagerFactory factory) {
+ HttpAsyncClientBuilder clientBuilder = HttpAsyncClientBuilder.create();
+ TlsContext vespaTlsContext = TransportSecurityUtils.createTlsContext().orElse(null);
+ TlsStrategy tlsStrategy;
+ if (vespaTlsContext != null) {
+ SSLParameters vespaTlsParameters = vespaTlsContext.parameters();
+ tlsStrategy = ClientTlsStrategyBuilder.create()
+ .setHostnameVerifier(new NoopHostnameVerifier())
+ .setSslContext(vespaTlsContext.context())
+ .setTlsVersions(vespaTlsParameters.getProtocols())
+ .setCiphers(vespaTlsParameters.getCipherSuites())
+ .build();
+ if (TransportSecurityUtils.getInsecureMixedMode() != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER) {
+ clientBuilder.setRoutePlanner(new HttpToHttpsRoutePlanner());
+ }
+ } else {
+ tlsStrategy = ClientTlsStrategyBuilder.create().build();
+ }
+ clientBuilder.disableConnectionState(); // Share connections between subsequent requests
+ clientBuilder.disableCookieManagement();
+ clientBuilder.disableAuthCaching();
+ clientBuilder.disableRedirectHandling();
+ clientBuilder.setConnectionManager(factory.create(tlsStrategy));
+ clientBuilder.setConnectionManagerShared(false);
+ return clientBuilder;
+ }
+
+ private static class HttpToHttpsRoutePlanner implements HttpRoutePlanner {
+
+ private final DefaultRoutePlanner defaultPlanner = new DefaultRoutePlanner(new DefaultSchemePortResolver());
+
+ @Override
+ public HttpRoute determineRoute(HttpHost target, HttpContext context) throws HttpException {
+ HttpRoute originalRoute = defaultPlanner.determineRoute(target, context);
+ HttpHost originalHost = originalRoute.getTargetHost();
+ String originalScheme = originalHost.getSchemeName();
+ String rewrittenScheme = originalScheme.equalsIgnoreCase("http") ? "https" : originalScheme;
+ boolean rewrittenSecure = target.getSchemeName().equalsIgnoreCase("https");
+ HttpHost rewrittenHost = new HttpHost(
+ rewrittenScheme, originalHost.getAddress(), originalHost.getHostName(), originalHost.getPort());
+ return new HttpRoute(
+ rewrittenHost,
+ originalRoute.getLocalAddress(),
+ originalRoute.getProxyHost(),
+ rewrittenSecure,
+ originalRoute.getTunnelType(),
+ originalRoute.getLayerType());
+ }
+ }
+
+}
diff --git a/jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java b/jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java
index 2bac7f66799..c6afa889041 100644
--- a/jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java
+++ b/jaxrs_client_utils/src/main/java/ai/vespa/util/http/VespaClientBuilderFactory.java
@@ -27,8 +27,10 @@ import static java.util.logging.Level.CONFIG;
* - hostname verification is not enabled - CN/SAN verification is assumed to be handled by the underlying x509 trust manager.
* - ssl context or hostname verifier must not be overridden by the caller
*
+ * @deprecated Use Apache httpclient based client factory instead (VespaHttpClientBuilder).
* @author bjorncs
*/
+@Deprecated(forRemoval = true)
public class VespaClientBuilderFactory implements AutoCloseable {
private static final Logger log = Logger.getLogger(VespaClientBuilderFactory.class.getName());
diff --git a/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java
index bdc89d737d4..6d1c1c71f21 100644
--- a/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java
+++ b/jaxrs_client_utils/src/main/java/com/yahoo/vespa/jaxrs/client/VespaJerseyJaxRsClientFactory.java
@@ -17,10 +17,13 @@ import java.util.List;
/**
* Factory for creating Jersey based Vespa clients from a JAX-RS resource interface.
*
+ * @deprecated Use Apache httpclient based client factory instead (VespaHttpClientBuilder).
* @author bjorncs
*/
+@Deprecated(forRemoval = true)
public class VespaJerseyJaxRsClientFactory implements JaxRsClientFactory, AutoCloseable {
+ @SuppressWarnings("removal")
private final VespaClientBuilderFactory clientBuilder = new VespaClientBuilderFactory();
// Client is a heavy-weight object with a finalizer so we create only one and re-use it
private final Client client;
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilter.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilter.java
new file mode 100644
index 00000000000..b891212031f
--- /dev/null
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilter.java
@@ -0,0 +1,21 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package com.yahoo.jdisc.http.filter.security.misc;
+
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.security.base.JsonSecurityRequestFilterBase;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Optional;
+
+public class VespaTlsFilter extends JsonSecurityRequestFilterBase {
+
+ @Override
+ protected Optional<ErrorResponse> filter(DiscFilterRequest request) {
+ return request.getClientCertificateChain().isEmpty()
+ ? Optional.of(new ErrorResponse(Response.Status.FORBIDDEN, "Forbidden to access this path"))
+ : Optional.empty();
+ }
+}
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java
new file mode 100644
index 00000000000..294126eb349
--- /dev/null
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java
@@ -0,0 +1,66 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package com.yahoo.jdisc.http.filter.security.misc;
+
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.security.KeyAlgorithm;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.SignatureAlgorithm;
+import com.yahoo.security.X509CertificateBuilder;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.security.auth.x500.X500Principal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+public class VespaTlsFilterTest {
+
+ @Test
+ public void testFilter() {
+ assertSuccess(createRequest(List.of(createCertificate())));
+ assertForbidden(createRequest(Collections.emptyList()));
+ }
+
+ private static X509Certificate createCertificate() {
+ return X509CertificateBuilder
+ .fromKeypair(
+ KeyUtils.generateKeypair(KeyAlgorithm.EC), new X500Principal("CN=test"),
+ Instant.now(), Instant.now().plus(1, ChronoUnit.DAYS),
+ SignatureAlgorithm.SHA512_WITH_ECDSA, BigInteger.valueOf(1))
+ .build();
+ }
+
+ private static DiscFilterRequest createRequest(List<X509Certificate> certChain) {
+ DiscFilterRequest request = Mockito.mock(DiscFilterRequest.class);
+ when(request.getClientCertificateChain()).thenReturn(certChain);
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getUri()).thenReturn(URI.create("http://localhost:8080/"));
+ return request;
+ }
+
+ private static void assertForbidden(DiscFilterRequest request) {
+ VespaTlsFilter filter = new VespaTlsFilter();
+ RequestHandlerTestDriver.MockResponseHandler handler = new RequestHandlerTestDriver.MockResponseHandler();
+ filter.filter(request, handler);
+ assertEquals(Response.Status.FORBIDDEN, handler.getStatus());
+ }
+
+ private static void assertSuccess(DiscFilterRequest request) {
+ VespaTlsFilter filter = new VespaTlsFilter();
+ RequestHandlerTestDriver.MockResponseHandler handler = new RequestHandlerTestDriver.MockResponseHandler();
+ filter.filter(request, handler);
+ assertNull(handler.getResponse());
+ }
+}
diff --git a/jdisc_http_service/pom.xml b/jdisc_http_service/pom.xml
index b140e66f28a..7333db96b91 100644
--- a/jdisc_http_service/pom.xml
+++ b/jdisc_http_service/pom.xml
@@ -152,6 +152,7 @@
jetty-servlet-${jetty.version}.jar,
jetty-servlets-${jetty.version}.jar,
jetty-util-${jetty.version}.jar,
+ jetty-util-ajax-${jetty.version}.jar,
component-jar-with-dependencies.jar
</discPreInstallBundle>
</configuration>
diff --git a/messagebus/src/tests/simpleprotocol/simpleprotocol.cpp b/messagebus/src/tests/simpleprotocol/simpleprotocol.cpp
index 715ef597767..fda3075fc28 100644
--- a/messagebus/src/tests/simpleprotocol/simpleprotocol.cpp
+++ b/messagebus/src/tests/simpleprotocol/simpleprotocol.cpp
@@ -1,14 +1,11 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/messagebus/testlib/receptor.h>
#include <vespa/messagebus/testlib/simpleprotocol.h>
#include <vespa/messagebus/testlib/simplemessage.h>
#include <vespa/messagebus/testlib/simplereply.h>
#include <vespa/messagebus/testlib/slobrok.h>
#include <vespa/messagebus/testlib/testserver.h>
-#include <vespa/messagebus/errorcode.h>
#include <vespa/messagebus/ireplyhandler.h>
-#include <vespa/messagebus/network/identity.h>
#include <vespa/messagebus/routing/routingcontext.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/component/vtag.h>
@@ -26,15 +23,20 @@ Test::Main()
SimpleProtocol protocol;
EXPECT_TRUE(protocol.getName() == "Simple");
+ EXPECT_EQUAL(152u, sizeof(Result));
+ EXPECT_EQUAL(136u, sizeof(Error));
+ EXPECT_EQUAL(56u, sizeof(Routable));
{
// test protocol
IRoutingPolicy::UP bogus = protocol.createPolicy("bogus", "");
- EXPECT_TRUE(bogus.get() == 0);
+ EXPECT_FALSE(bogus);
}
TEST_FLUSH();
{
// test SimpleMessage
- Message::UP msg(new SimpleMessage("test"));
+ EXPECT_EQUAL(104u, sizeof(Message));
+ EXPECT_EQUAL(184u, sizeof(SimpleMessage));
+ auto msg = std::make_unique<SimpleMessage>("test");
EXPECT_TRUE(!msg->isReply());
EXPECT_TRUE(msg->getProtocol() == SimpleProtocol::NAME);
EXPECT_TRUE(msg->getType() == SimpleProtocol::MESSAGE);
@@ -42,7 +44,7 @@ Test::Main()
Blob b = protocol.encode(version, *msg);
EXPECT_TRUE(b.size() > 0);
Routable::UP tmp = protocol.decode(version, BlobRef(b));
- ASSERT_TRUE(tmp.get() != 0);
+ ASSERT_TRUE(tmp);
EXPECT_TRUE(!tmp->isReply());
EXPECT_TRUE(tmp->getProtocol() == SimpleProtocol::NAME);
EXPECT_TRUE(tmp->getType() == SimpleProtocol::MESSAGE);
@@ -51,7 +53,9 @@ Test::Main()
TEST_FLUSH();
{
// test SimpleReply
- Reply::UP reply(new SimpleReply("reply"));
+ EXPECT_EQUAL(96u, sizeof(Reply));
+ EXPECT_EQUAL(160u, sizeof(SimpleReply));
+ auto reply = std::make_unique<SimpleReply>("reply");
EXPECT_TRUE(reply->isReply());
EXPECT_TRUE(reply->getProtocol() == SimpleProtocol::NAME);
EXPECT_TRUE(reply->getType() == SimpleProtocol::REPLY);
@@ -59,7 +63,7 @@ Test::Main()
Blob b = protocol.encode(version, *reply);
EXPECT_TRUE(b.size() > 0);
Routable::UP tmp = protocol.decode(version, BlobRef(b));
- ASSERT_TRUE(tmp.get() != 0);
+ ASSERT_TRUE(tmp);
EXPECT_TRUE(tmp->isReply());
EXPECT_TRUE(tmp->getProtocol() == SimpleProtocol::NAME);
EXPECT_TRUE(tmp->getType() == SimpleProtocol::REPLY);
diff --git a/messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp b/messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp
index 344d38c8263..f97fc6a0010 100644
--- a/messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp
+++ b/messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp
@@ -120,6 +120,6 @@ Test::Main()
.addChild("Server reply")
.addChild("Proxy reply")
.addChild("Client reply");
- EXPECT_TRUE(reply->getTrace().getRoot().encode() == t.encode());
+ EXPECT_TRUE(reply->getTrace().encode() == t.encode());
TEST_DONE();
}
diff --git a/messagebus/src/vespa/messagebus/error.cpp b/messagebus/src/vespa/messagebus/error.cpp
index 85c9b92eb19..85eec140db5 100644
--- a/messagebus/src/vespa/messagebus/error.cpp
+++ b/messagebus/src/vespa/messagebus/error.cpp
@@ -3,7 +3,7 @@
#include "errorcode.h"
#include <vespa/vespalib/util/stringfmt.h>
-using vespalib::make_string;
+using vespalib::make_string_short::fmt;
namespace mbus {
@@ -13,9 +13,9 @@ Error::Error()
_service()
{ }
-Error::~Error() {}
+Error::~Error() = default;
-Error::Error(uint32_t c, const string &m, const string &s)
+Error::Error(uint32_t c, vespalib::stringref m, vespalib::stringref s)
: _code(c),
_msg(m),
_service(s)
@@ -26,9 +26,9 @@ Error::toString() const
{
string name(ErrorCode::getName(_code));
if (name.empty()) {
- name = make_string("%u", _code);
+ name = fmt("%u", _code);
}
- return make_string("[%s @ %s]: %s", name.c_str(), _service.empty() ? "localhost" : _service.c_str(), _msg.c_str());
+ return fmt("[%s @ %s]: %s", name.c_str(), _service.empty() ? "localhost" : _service.c_str(), _msg.c_str());
}
} // namespace mbus
diff --git a/messagebus/src/vespa/messagebus/error.h b/messagebus/src/vespa/messagebus/error.h
index 638463e3665..74936962113 100644
--- a/messagebus/src/vespa/messagebus/error.h
+++ b/messagebus/src/vespa/messagebus/error.h
@@ -36,7 +36,7 @@ public:
* @param m error message
* @param s error service
**/
- Error(uint32_t c, const string &m, const string &s = "");
+ Error(uint32_t c, vespalib::stringref m, vespalib::stringref s = "");
/**
* Obtain the error code of this error.
diff --git a/messagebus/src/vespa/messagebus/message.cpp b/messagebus/src/vespa/messagebus/message.cpp
index 305ffb06aa0..8d17fd6b0ff 100644
--- a/messagebus/src/vespa/messagebus/message.cpp
+++ b/messagebus/src/vespa/messagebus/message.cpp
@@ -5,6 +5,7 @@
#include "ireplyhandler.h"
#include "emptyreply.h"
#include "errorcode.h"
+#include "error.h"
#include <vespa/vespalib/util/backtrace.h>
#include <vespa/log/log.h>
diff --git a/messagebus/src/vespa/messagebus/network/rpcsend.cpp b/messagebus/src/vespa/messagebus/network/rpcsend.cpp
index dca7f0c997f..86c9b139f1a 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsend.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcsend.cpp
@@ -178,7 +178,7 @@ RPCSend::doRequestDone(FRT_RPCRequest *req) {
}
} else {
FRT_Values &ret = *req->GetReturn();
- reply = createReply(ret, serviceName, error, trace.getRoot());
+ reply = createReply(ret, serviceName, error, trace);
}
if (trace.shouldTrace(TraceLevel::SEND_RECEIVE)) {
trace.trace(TraceLevel::SEND_RECEIVE,
diff --git a/messagebus/src/vespa/messagebus/network/rpcsend.h b/messagebus/src/vespa/messagebus/network/rpcsend.h
index f3a9177d236..1ccdea6fbc5 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsend.h
+++ b/messagebus/src/vespa/messagebus/network/rpcsend.h
@@ -12,7 +12,7 @@ class FRT_ReflectionBuilder;
namespace vespalib::slime { struct Cursor; }
namespace vespalib { struct Memory; }
-namespace vespalib { class TraceNode; }
+namespace vespalib { class Trace; }
namespace mbus {
class Error;
@@ -56,7 +56,7 @@ protected:
virtual void build(FRT_ReflectionBuilder & builder) = 0;
virtual std::unique_ptr<Reply> createReply(const FRT_Values & response, const string & serviceName,
- Error & error, vespalib::TraceNode & rootTrace) const = 0;
+ Error & error, vespalib::Trace & trace) const = 0;
virtual void encodeRequest(FRT_RPCRequest &req, const vespalib::Version &version, const Route & route,
const RPCServiceAddress & address, const Message & msg, uint32_t traceLevel,
const PayLoadFiller &filler, duration timeRemaining) const = 0;
diff --git a/messagebus/src/vespa/messagebus/network/rpcsendv1.cpp b/messagebus/src/vespa/messagebus/network/rpcsendv1.cpp
index 388ab3309c4..775c90ea9ee 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsendv1.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcsendv1.cpp
@@ -4,6 +4,7 @@
#include "rpcnetwork.h"
#include "rpcserviceaddress.h"
#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/error.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/fnet/frt/reflection.h>
@@ -115,7 +116,7 @@ RPCSendV1::toParams(const FRT_Values &args) const
std::unique_ptr<Reply>
-RPCSendV1::createReply(const FRT_Values & ret, const string & serviceName, Error & error, vespalib::TraceNode & rootTrace) const
+RPCSendV1::createReply(const FRT_Values & ret, const string & serviceName, Error & error, vespalib::Trace & trace) const
{
vespalib::Version version = vespalib::Version(ret[0]._string._str);
double retryDelay = ret[1]._double;
@@ -127,7 +128,7 @@ RPCSendV1::createReply(const FRT_Values & ret, const string & serviceName, Error
uint32_t errorServicesLen = ret[4]._string_array._len;
const char *protocolName = ret[5]._string._str;
BlobRef payload(ret[6]._data._buf, ret[6]._data._len);
- const char *trace = ret[7]._string._str;
+ const char *traceStr = ret[7]._string._str;
Reply::UP reply;
if (payload.size() > 0) {
@@ -141,7 +142,7 @@ RPCSendV1::createReply(const FRT_Values & ret, const string & serviceName, Error
reply->addError(Error(errorCodes[i], errorMessages[i]._str,
errorServices[i]._len > 0 ? errorServices[i]._str : serviceName.c_str()));
}
- rootTrace.addChild(TraceNode::decode(trace));
+ trace.addChild(TraceNode::decode(traceStr));
return reply;
}
@@ -163,7 +164,7 @@ RPCSendV1::createResponse(FRT_Values & ret, const string & version, Reply & repl
ret.AddString(reply.getProtocol().c_str());
ret.AddData(std::move(payload.payload()), payload.size());
if (reply.getTrace().getLevel() > 0) {
- ret.AddString(reply.getTrace().getRoot().encode().c_str());
+ ret.AddString(reply.getTrace().encode().c_str());
} else {
ret.AddString("");
}
diff --git a/messagebus/src/vespa/messagebus/network/rpcsendv1.h b/messagebus/src/vespa/messagebus/network/rpcsendv1.h
index 249acc50e0c..f9b4b0bdfe8 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsendv1.h
+++ b/messagebus/src/vespa/messagebus/network/rpcsendv1.h
@@ -17,7 +17,7 @@ private:
const PayLoadFiller &filler, duration timeRemaining) const override;
std::unique_ptr<Reply> createReply(const FRT_Values & response, const string & serviceName,
- Error & error, vespalib::TraceNode & rootTrace) const override;
+ Error & error, vespalib::Trace & trace) const override;
void createResponse(FRT_Values & ret, const string & version, Reply & reply, Blob payload) const override;
};
diff --git a/messagebus/src/vespa/messagebus/network/rpcsendv2.cpp b/messagebus/src/vespa/messagebus/network/rpcsendv2.cpp
index f7303ece20f..a2e3310046d 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsendv2.cpp
+++ b/messagebus/src/vespa/messagebus/network/rpcsendv2.cpp
@@ -4,6 +4,7 @@
#include "rpcnetwork.h"
#include "rpcserviceaddress.h"
#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/error.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/vespalib/data/databuffer.h>
@@ -188,7 +189,7 @@ RPCSendV2::toParams(const FRT_Values &args) const
std::unique_ptr<Reply>
RPCSendV2::createReply(const FRT_Values & ret, const string & serviceName,
- Error & error, vespalib::TraceNode & rootTrace) const
+ Error & error, vespalib::Trace & rootTrace) const
{
uint8_t encoding = ret[3]._intval8;
uint32_t uncompressedSize = ret[4]._intval32;
@@ -240,7 +241,7 @@ RPCSendV2::createResponse(FRT_Values & ret, const string & version, Reply & repl
root.setString(PROTOCOL_F, reply.getProtocol());
root.setData(BLOB_F, vespalib::Memory(payload.data(), payload.size()));
if (reply.getTrace().getLevel() > 0) {
- root.setString(TRACE_F, reply.getTrace().getRoot().encode());
+ root.setString(TRACE_F, reply.getTrace().encode());
}
if (reply.getNumErrors() > 0) {
@@ -249,7 +250,7 @@ RPCSendV2::createResponse(FRT_Values & ret, const string & version, Reply & repl
Cursor & error = array.addObject();
error.setLong(CODE_F, reply.getError(i).getCode());
error.setString(MSG_F, reply.getError(i).getMessage());
- error.setString(SERVICE_F, reply.getError(i).getService().c_str());
+ error.setString(SERVICE_F, reply.getError(i).getService());
}
}
@@ -263,7 +264,6 @@ RPCSendV2::createResponse(FRT_Values & ret, const string & version, Reply & repl
ret.AddInt32(toCompress.size());
assert(buf.getDataLen() <= INT32_MAX);
ret.AddData(std::move(buf));
-
}
} // namespace mbus
diff --git a/messagebus/src/vespa/messagebus/network/rpcsendv2.h b/messagebus/src/vespa/messagebus/network/rpcsendv2.h
index c48aa90a9fb..da4154e70a8 100644
--- a/messagebus/src/vespa/messagebus/network/rpcsendv2.h
+++ b/messagebus/src/vespa/messagebus/network/rpcsendv2.h
@@ -17,7 +17,7 @@ private:
const PayLoadFiller &filler, duration timeRemaining) const override;
std::unique_ptr<Reply> createReply(const FRT_Values & response, const string & serviceName,
- Error & error, vespalib::TraceNode & rootTrace) const override;
+ Error & error, vespalib::Trace & trace) const override;
void createResponse(FRT_Values & ret, const string & version, Reply & reply, Blob payload) const override;
};
diff --git a/messagebus/src/vespa/messagebus/reply.cpp b/messagebus/src/vespa/messagebus/reply.cpp
index bf12698c86f..68bb7db1191 100644
--- a/messagebus/src/vespa/messagebus/reply.cpp
+++ b/messagebus/src/vespa/messagebus/reply.cpp
@@ -5,6 +5,7 @@
#include "ireplyhandler.h"
#include "message.h"
#include "tracelevel.h"
+#include "error.h"
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/backtrace.h>
@@ -54,12 +55,6 @@ Reply::swapState(Routable &rhs)
}
}
-bool
-Reply::isReply() const
-{
- return true;
-}
-
void
Reply::addError(const Error &e)
{
@@ -72,16 +67,25 @@ Reply::addError(const Error &e)
bool
Reply::hasFatalErrors() const
{
- for (std::vector<Error>::const_iterator it = _errors.begin();
- it != _errors.end(); ++it)
+ for (const Error & error : _errors)
{
- if (it->getCode() >= ErrorCode::FATAL_ERROR) {
+ if (error.getCode() >= ErrorCode::FATAL_ERROR) {
return true;
}
}
return false;
}
+const Error &
+Reply::getError(uint32_t i) const {
+ return _errors[i];
+}
+
+uint32_t
+Reply::getNumErrors() const {
+ return _errors.size();
+}
+
void
Reply::setMessage(Message::UP msg) {
_msg = std::move(msg);
diff --git a/messagebus/src/vespa/messagebus/reply.h b/messagebus/src/vespa/messagebus/reply.h
index 64b9f5c0b13..72493ec0ae6 100644
--- a/messagebus/src/vespa/messagebus/reply.h
+++ b/messagebus/src/vespa/messagebus/reply.h
@@ -1,13 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include "error.h"
#include "routable.h"
#include <vector>
namespace mbus {
class Message;
+class Error;
/**
* A reply is a response to a message that has been sent throught the
@@ -43,7 +43,7 @@ public:
~Reply() override;
void swapState(Routable &rhs) override;
- bool isReply() const override;
+ bool isReply() const override { return true; }
/**
* Add an Error to this Reply
@@ -72,14 +72,14 @@ public:
* @param i The index of the error to return.
* @return The error at the given index.
*/
- const Error &getError(uint32_t i) const { return _errors[i]; }
+ const Error &getError(uint32_t i) const;
/**
* Returns the number of errors that this reply contains.
*
* @return The number of replies.
*/
- uint32_t getNumErrors() const { return _errors.size(); }
+ uint32_t getNumErrors() const;
/**
* Attach a Message to this Reply. If a Reply contains errors, messagebus
diff --git a/messagebus/src/vespa/messagebus/result.cpp b/messagebus/src/vespa/messagebus/result.cpp
index e99120d8c2d..970bb72e638 100644
--- a/messagebus/src/vespa/messagebus/result.cpp
+++ b/messagebus/src/vespa/messagebus/result.cpp
@@ -1,80 +1,22 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "result.h"
+#include "message.h"
namespace mbus {
-Result::Handover::Handover(bool a, const Error &e, Message *m)
- : _accepted(a),
- _error(e),
- _msg(m)
-{ }
-
Result::Result()
: _accepted(true),
_error(),
_msg()
{ }
-Result::Result(const Error &err, Message::UP msg)
+Result::Result(const Error &err, std::unique_ptr<Message> msg)
: _accepted(false),
_error(err),
_msg(std::move(msg))
{ }
-Result::Result(Result &&rhs)
- : _accepted(rhs._accepted),
- _error(rhs._error),
- _msg(std::move(rhs._msg))
-{ }
-
-Result::Result(const Handover &rhs)
- : _accepted(rhs._accepted),
- _error(rhs._error),
- _msg(rhs._msg)
-{ }
-
-Result::~Result() {}
-
-bool
-Result::isAccepted() const
-{
- return _accepted;
-}
-
-const Error &
-Result::getError() const
-{
- return _error;
-}
-
-Message::UP
-Result::getMessage()
-{
- return std::move(_msg);
-}
-
-Result::operator Handover()
-{
- return Handover(_accepted, _error, _msg.release());
-}
-
-Result &
-Result::operator=(Result &&rhs)
-{
- _accepted = rhs._accepted;
- _error = rhs._error;
- _msg = std::move(rhs._msg);
- return *this;
-}
-
-Result &
-Result::operator=(const Handover &rhs)
-{
- _accepted = rhs._accepted;
- _error = rhs._error;
- _msg.reset(rhs._msg);
- return *this;
-}
+Result::~Result() = default;
} // namespace mbus
diff --git a/messagebus/src/vespa/messagebus/result.h b/messagebus/src/vespa/messagebus/result.h
index adb9c1ea81a..dcf6e4964e5 100644
--- a/messagebus/src/vespa/messagebus/result.h
+++ b/messagebus/src/vespa/messagebus/result.h
@@ -3,10 +3,11 @@
#pragma once
#include "error.h"
-#include "message.h"
namespace mbus {
+class Message;
+
/**
* A Result object is used as return value when trying to send a
* Message on a SourceSession. It says whether messagebus has accepted
@@ -24,24 +25,10 @@ class Result
private:
bool _accepted;
Error _error;
- Message::UP _msg;
+ std::unique_ptr<Message> _msg;
public:
/**
- * This inner class is used to implement destructive copy for
- * return values.
- **/
- class Handover {
- friend class Result;
- bool _accepted;
- Error _error;
- Message *_msg;
- Handover(bool a, const Error &e, Message *m);
- Handover(const Handover &); // not implemented
- Handover &operator=(const Handover &); // not implemented
- };
-
- /**
* Create a Result indicating that messagebus has accepted the
* Message.
**/
@@ -54,23 +41,11 @@ public:
* @param err the reason for not accepting the Message
* @param msg the message that did not get accepted
**/
- Result(const Error &err, Message::UP msg);
+ Result(const Error &err, std::unique_ptr<Message> msg);
- /**
- * Move constructor
- *
- * @param rhs the original object
- **/
- Result(Result &&rhs);
-
- /**
- * Construct a new Result from an internal Handover object that
- * has destructed the original Result.
- *
- * @param rhs handover object
- **/
- Result(const Handover &rhs);
+ Result(Result &&rhs) = default;
+ Result &operator=(Result &&rhs) = default;
~Result();
/**
@@ -78,14 +53,14 @@ public:
*
* @return true if the Message was accepted
**/
- bool isAccepted() const;
+ bool isAccepted() const { return _accepted; }
/**
* Obtain the error causing the message not to be accepted.
*
* @return error
**/
- const Error &getError() const;
+ const Error &getError() const { return _error; }
/**
* If the message was not accepted, this method may be used to get
@@ -95,28 +70,7 @@ public:
*
* @return the Message that was not accepted
**/
- Message::UP getMessage();
-
- /**
- * Perform an implicit typecast to support destructive copy of
- * return values.
- **/
- operator Handover();
-
- /**
- * Moving assignment operator
- *
- * @param rhs the original object
- **/
- Result &operator=(Result &&rhs);
-
- /**
- * Assign a Result from an internal Handover object that has
- * destructed the original Result.
- *
- * @param rhs handover object
- **/
- Result &operator=(const Handover &rhs);
+ std::unique_ptr<Message> getMessage() { return std::move(_msg); }
};
} // namespace mbus
diff --git a/messagebus/src/vespa/messagebus/routable.h b/messagebus/src/vespa/messagebus/routable.h
index 50cb4e090ce..2c83daded1e 100644
--- a/messagebus/src/vespa/messagebus/routable.h
+++ b/messagebus/src/vespa/messagebus/routable.h
@@ -24,9 +24,9 @@ namespace mbus {
*/
class Routable {
private:
- Context _context;
- CallStack _stack;
- Trace _trace;
+ Context _context;
+ CallStack _stack;
+ Trace _trace;
public:
/**
@@ -90,6 +90,7 @@ public:
* @return Trace object
*/
Trace &getTrace() { return _trace; }
+ Trace && steal_trace() { return std::move(_trace); }
/**
* Access the Trace object for this Routable. The Trace is part of the
@@ -106,7 +107,7 @@ public:
*
* @param trace The trace to set.
*/
- void setTrace(const Trace &trace) { _trace = trace; }
+ void setTrace(Trace &&trace) { _trace = std::move(trace); }
/**
* Swaps the state that makes this routable unique to another routable. The
diff --git a/messagebus/src/vespa/messagebus/routing/routingnode.cpp b/messagebus/src/vespa/messagebus/routing/routingnode.cpp
index f24afbc07ca..5a70f510dcc 100644
--- a/messagebus/src/vespa/messagebus/routing/routingnode.cpp
+++ b/messagebus/src/vespa/messagebus/routing/routingnode.cpp
@@ -184,10 +184,7 @@ RoutingNode::setReply(Reply::UP reply)
{
if (reply) {
_shouldRetry = _resender != nullptr && _resender->shouldRetry(*reply);
- if ( ! reply->getTrace().getRoot().isEmpty()) {
- _trace.getRoot().addChild(std::move(reply->getTrace().getRoot()));
- reply->getTrace().clear();
- }
+ _trace.addChild(reply->steal_trace());
}
_reply = std::move(reply);
}
@@ -268,14 +265,12 @@ RoutingNode::notifyMerge()
// Merges the trace information from all children into this. This method takes care not to spend cycles
// manipulating the trace in case tracing is disabled.
if (_trace.getLevel() > 0) {
- TraceNode tail;
+ Trace tail;
for (auto * child : _children) {
- TraceNode &root = child->_trace.getRoot();
- tail.addChild(root);
- root.clear();
+ tail.addChild(std::move(child->_trace));
}
tail.setStrict(false);
- _trace.getRoot().addChild(tail);
+ _trace.addChild(std::move(tail));
}
// Execute the {@link RoutingPolicy#merge(RoutingContext)} method of the current routing policy. If a
diff --git a/messagebus/src/vespa/messagebus/sendproxy.cpp b/messagebus/src/vespa/messagebus/sendproxy.cpp
index c884932664b..ff514f788cd 100644
--- a/messagebus/src/vespa/messagebus/sendproxy.cpp
+++ b/messagebus/src/vespa/messagebus/sendproxy.cpp
@@ -53,11 +53,10 @@ SendProxy::handleReply(Reply::UP reply)
} else if (logger.wants(ns_log::Logger::spam)) {
LOG(spam, "Trace for reply:\n%s", reply->getTrace().toString().c_str());
}
- Trace empty;
- trace.swap(empty);
+ trace.clear();
} else if (trace.getLevel() > 0) {
- trace.getRoot().addChild(reply->getTrace().getRoot());
- trace.getRoot().normalize();
+ trace.addChild(reply->steal_trace());
+ trace.normalize();
}
reply->swapState(*_msg);
reply->setMessage(std::move(_msg));
diff --git a/messagebus_test/src/tests/trace/trace.cpp b/messagebus_test/src/tests/trace/trace.cpp
index 334f00745da..1ab30303d2c 100644
--- a/messagebus_test/src/tests/trace/trace.cpp
+++ b/messagebus_test/src/tests/trace/trace.cpp
@@ -100,14 +100,14 @@ Test::Main()
Reply::UP reply;
SourceSession::UP ss = mb.getMessageBus().createSourceSession(src, SourceSessionParams());
for (int i = 0; i < 50; ++i) {
- Message::UP msg(new SimpleMessage("test"));
+ auto msg = std::make_unique<SimpleMessage>("test");
msg->getTrace().setLevel(1);
ss->send(std::move(msg), "test");
reply = src.getReply(10s);
if (reply) {
- reply->getTrace().getRoot().normalize();
+ reply->getTrace().normalize();
// resending breaks the trace, so retry until it has expected form
- if (!reply->hasErrors() && reply->getTrace().getRoot().encode() == expect.encode()) {
+ if (!reply->hasErrors() && reply->getTrace().encode() == expect.encode()) {
break;
}
}
@@ -116,7 +116,7 @@ Test::Main()
}
EXPECT_TRUE(!reply->hasErrors());
- EXPECT_EQUAL(reply->getTrace().getRoot().encode(), expect.encode());
+ EXPECT_EQUAL(reply->getTrace().encode(), expect.encode());
EXPECT_TRUE(system((ctl_script + " stop all").c_str()) == 0);
TEST_DONE();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
index d0ee6229428..00327dc0002 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
@@ -94,7 +94,7 @@ public final class Node implements Nodelike {
requireNonEmpty(ipConfig.primary(), "Active node " + hostname + " must have at least one valid IP address");
if (parentHostname.isPresent()) {
- if (!ipConfig.pool().isEmpty()) throw new IllegalArgumentException("A child node cannot have an IP address pool");
+ if (!ipConfig.pool().getIpSet().isEmpty()) throw new IllegalArgumentException("A child node cannot have an IP address pool");
if (modelName.isPresent()) throw new IllegalArgumentException("A child node cannot have model name set");
if (switchHostname.isPresent()) throw new IllegalArgumentException("A child node cannot have switch hostname set");
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
index 05bdfd25b76..86795767710 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
@@ -165,7 +165,7 @@ public class NodeRepository extends AbstractComponent {
this.osVersions = new OsVersions(this);
this.infrastructureVersions = new InfrastructureVersions(db);
this.firmwareChecks = new FirmwareChecks(db, clock);
- this.containerImages = new ContainerImages(db, containerImage, flagSource);
+ this.containerImages = new ContainerImages(db, containerImage);
this.jobControl = new JobControl(new JobControlFlags(db, flagSource));
this.applications = new Applications(db);
this.spareCount = spareCount;
@@ -460,7 +460,7 @@ public class NodeRepository extends AbstractComponent {
.map(node -> {
if (node.state() != State.provisioned && node.state() != State.dirty)
illegal("Can not set " + node + " ready. It is not provisioned or dirty.");
- if (node.type() == NodeType.host && node.ipConfig().pool().isEmpty())
+ if (node.type() == NodeType.host && node.ipConfig().pool().getIpSet().isEmpty())
illegal("Can not set host " + node + " ready. Its IP address pool is empty.");
return node.withWantToRetire(false, false, Agent.system, clock.instant());
})
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java
index fd92b5b0ca0..847b825a7a4 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java
@@ -57,7 +57,7 @@ public class Application {
public Application withCluster(ClusterSpec.Id id, boolean exclusive, ClusterResources min, ClusterResources max) {
Cluster cluster = clusters.get(id);
if (cluster == null)
- cluster = new Cluster(id, exclusive, min, max, Optional.empty(), Optional.empty(), List.of());
+ cluster = new Cluster(id, exclusive, min, max, Optional.empty(), Optional.empty(), List.of(), "");
else
cluster = cluster.withConfiguration(exclusive, min, max);
return with(cluster);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
index a17ee081447..90133f7499e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
@@ -25,6 +25,7 @@ public class Cluster {
private final Optional<ClusterResources> suggested;
private final Optional<ClusterResources> target;
private final List<ScalingEvent> scalingEvents;
+ private final String autoscalingStatus;
public Cluster(ClusterSpec.Id id,
boolean exclusive,
@@ -32,7 +33,8 @@ public class Cluster {
ClusterResources maxResources,
Optional<ClusterResources> suggestedResources,
Optional<ClusterResources> targetResources,
- List<ScalingEvent> scalingEvents) {
+ List<ScalingEvent> scalingEvents,
+ String autoscalingStatus) {
this.id = Objects.requireNonNull(id);
this.exclusive = exclusive;
this.min = Objects.requireNonNull(minResources);
@@ -44,6 +46,7 @@ public class Cluster {
else
this.target = targetResources;
this.scalingEvents = scalingEvents;
+ this.autoscalingStatus = autoscalingStatus;
}
public ClusterSpec.Id id() { return id; }
@@ -73,21 +76,33 @@ public class Cluster {
/** Returns the recent scaling events in this cluster */
public List<ScalingEvent> scalingEvents() { return scalingEvents; }
+ public Optional<ScalingEvent> lastScalingEvent() {
+ if (scalingEvents.isEmpty()) return Optional.empty();
+ return Optional.of(scalingEvents.get(scalingEvents.size() - 1));
+ }
+
+ /** The latest autoscaling status of this cluster, or empty (never null) if none */
+ public String autoscalingStatus() { return autoscalingStatus; }
+
public Cluster withConfiguration(boolean exclusive, ClusterResources min, ClusterResources max) {
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents);
+ return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
}
public Cluster withSuggested(Optional<ClusterResources> suggested) {
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents);
+ return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
}
public Cluster withTarget(Optional<ClusterResources> target) {
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents);
+ return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
}
public Cluster with(ScalingEvent scalingEvent) {
// NOTE: We're just storing the latest scaling event so far
- return new Cluster(id, exclusive, min, max, suggested, target, List.of(scalingEvent));
+ return new Cluster(id, exclusive, min, max, suggested, target, List.of(scalingEvent), autoscalingStatus);
+ }
+
+ public Cluster withAutoscalingStatus(String autoscalingStatus) {
+ return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
}
@Override
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 1a8c4c8a6c2..d2c943794fe 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
@@ -3,14 +3,16 @@ package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.NodeResources;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.applications.Cluster;
import java.time.Duration;
+import java.time.Instant;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
-import java.util.logging.Logger;
/**
* The autoscaler makes decisions about the flavor and node count that should be allocated to a cluster
@@ -20,8 +22,6 @@ import java.util.logging.Logger;
*/
public class Autoscaler {
- private static final Logger log = Logger.getLogger(Autoscaler.class.getName());
-
/** What cost difference factor is worth a reallocation? */
private static final double costDifferenceWorthReallocation = 0.1;
/** What difference factor for a resource is worth a reallocation? */
@@ -55,39 +55,46 @@ public class Autoscaler {
* @return scaling advice for this cluster
*/
public Advice autoscale(Cluster cluster, List<Node> clusterNodes) {
- if (cluster.minResources().equals(cluster.maxResources())) return Advice.none(); // Shortcut
+ if (cluster.minResources().equals(cluster.maxResources())) return Advice.none("Autoscaling is disabled"); // Shortcut
return autoscale(cluster, clusterNodes, Limits.of(cluster), cluster.exclusive());
}
private Advice autoscale(Cluster cluster, List<Node> clusterNodes, Limits limits, boolean exclusive) {
- log.fine(() -> "Autoscale " + cluster.toString());
-
- if (unstable(clusterNodes, nodeRepository)) {
- log.fine(() -> "Unstable - Advice.none " + cluster.toString());
- return Advice.none();
- }
+ ClusterSpec.Type clusterType = clusterNodes.get(0).allocation().get().membership().cluster().type();
+ if (unstable(clusterNodes, nodeRepository))
+ return Advice.none("Cluster change in progress");
- AllocatableClusterResources currentAllocation = new AllocatableClusterResources(clusterNodes, nodeRepository, cluster.exclusive());
+ AllocatableClusterResources currentAllocation =
+ new AllocatableClusterResources(clusterNodes, nodeRepository, cluster.exclusive());
ClusterTimeseries clusterTimeseries = new ClusterTimeseries(cluster, clusterNodes, metricsDb, nodeRepository);
- Optional<Double> cpuLoad = clusterTimeseries.averageLoad(Resource.cpu, cluster);
- Optional<Double> memoryLoad = clusterTimeseries.averageLoad(Resource.memory, cluster);
- Optional<Double> diskLoad = clusterTimeseries.averageLoad(Resource.disk, cluster);
- if (cpuLoad.isEmpty() || memoryLoad.isEmpty() || diskLoad.isEmpty()) return Advice.none();
+ int measurementsPerNode = clusterTimeseries.measurementsPerNode();
+ if (measurementsPerNode < minimumMeasurementsPerNode(clusterType))
+ return Advice.none("Collecting more data before making new scaling decisions" +
+ ": Has " + measurementsPerNode + " data points per node");
- var target = ResourceTarget.idealLoad(cpuLoad.get(), memoryLoad.get(), diskLoad.get(), currentAllocation);
+ int nodesMeasured = clusterTimeseries.nodesMeasured();
+ if (nodesMeasured != clusterNodes.size())
+ return Advice.none("Collecting more data before making new scaling decisions" +
+ ": Has measurements from " + nodesMeasured + " but need from " + clusterNodes.size());
+
+ double cpuLoad = clusterTimeseries.averageLoad(Resource.cpu);
+ double memoryLoad = clusterTimeseries.averageLoad(Resource.memory);
+ double diskLoad = clusterTimeseries.averageLoad(Resource.disk);
+
+ var target = ResourceTarget.idealLoad(cpuLoad, memoryLoad, diskLoad, currentAllocation);
Optional<AllocatableClusterResources> bestAllocation =
allocationOptimizer.findBestAllocation(target, currentAllocation, limits, exclusive);
- if (bestAllocation.isEmpty()) {
- log.fine(() -> "bestAllocation.isEmpty: Advice.dontScale for " + cluster.toString());
- return Advice.dontScale();
- }
- if (similar(bestAllocation.get(), currentAllocation)) {
- log.fine(() -> "Current allocation similar: Advice.dontScale for " + cluster.toString());
- return Advice.dontScale();
- }
+ if (bestAllocation.isEmpty())
+ return Advice.dontScale("No allocation changes are possible within configured limits");
+
+ if (similar(bestAllocation.get(), currentAllocation))
+ return Advice.dontScale("Cluster is ideally scaled (within configured limits)");
+ if (isDownscaling(bestAllocation.get(), currentAllocation) && recentlyScaled(cluster, clusterNodes))
+ return Advice.dontScale("Waiting a while before scaling down");
+
return Advice.scaleTo(bestAllocation.get().toAdvertisedClusterResources());
}
@@ -106,10 +113,23 @@ public class Autoscaler {
return Math.abs(r1 - r2) / (( r1 + r2) / 2) < threshold;
}
+ /** Returns true if this reduces total resources in any dimension */
+ private boolean isDownscaling(AllocatableClusterResources target, AllocatableClusterResources current) {
+ NodeResources targetTotal = target.toAdvertisedClusterResources().totalResources();
+ NodeResources currentTotal = current.toAdvertisedClusterResources().totalResources();
+ return ! targetTotal.justNumbers().satisfies(currentTotal.justNumbers());
+ }
+
+ private boolean recentlyScaled(Cluster cluster, List<Node> clusterNodes) {
+ Duration downscalingDelay = downscalingDelay(clusterNodes.get(0).allocation().get().membership().cluster().type());
+ return cluster.lastScalingEvent().map(event -> event.at()).orElse(Instant.MIN)
+ .isAfter(nodeRepository.clock().instant().minus(downscalingDelay));
+ }
+
/** The duration of the window we need to consider to make a scaling decision. See also minimumMeasurementsPerNode */
static Duration scalingWindow(ClusterSpec.Type clusterType) {
if (clusterType.isContent()) return Duration.ofHours(12);
- return Duration.ofHours(1);
+ return Duration.ofMinutes(30);
}
static Duration maxScalingWindow() {
@@ -119,7 +139,16 @@ public class Autoscaler {
/** Measurements are currently taken once a minute. See also scalingWindow */
static int minimumMeasurementsPerNode(ClusterSpec.Type clusterType) {
if (clusterType.isContent()) return 60;
- return 20;
+ return 7;
+ }
+
+ /**
+ * We should wait a while before scaling down after a scaling event as a peak in usage
+ * indicates more peaks may arrive in the near future.
+ */
+ static Duration downscalingDelay(ClusterSpec.Type clusterType) {
+ if (clusterType.isContent()) return Duration.ofHours(12);
+ return Duration.ofHours(1);
}
public static boolean unstable(List<Node> nodes, NodeRepository nodeRepository) {
@@ -140,10 +169,12 @@ public class Autoscaler {
private final boolean present;
private final Optional<ClusterResources> target;
+ private final String reason;
- private Advice(Optional<ClusterResources> target, boolean present) {
+ private Advice(Optional<ClusterResources> target, boolean present, String reason) {
this.target = target;
this.present = present;
+ this.reason = Objects.requireNonNull(reason);
}
/**
@@ -158,10 +189,14 @@ public class Autoscaler {
/** True if this provides advice (which may be to keep the current allocation) */
public boolean isPresent() { return present; }
- private static Advice none() { return new Advice(Optional.empty(), false); }
- private static Advice dontScale() { return new Advice(Optional.empty(), true); }
- private static Advice scaleTo(ClusterResources target) { return new Advice(Optional.of(target), true); }
+ /** The reason for this advice */
+ public String reason() { return reason; }
+ private static Advice none(String reason) { return new Advice(Optional.empty(), false, reason); }
+ private static Advice dontScale(String reason) { return new Advice(Optional.empty(), true, reason); }
+ private static Advice scaleTo(ClusterResources target) {
+ return new Advice(Optional.of(target), true, "Scaling due to load changes");
+ }
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java
index bb91b77dce5..e325e797ca5 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java
@@ -10,8 +10,6 @@ import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
-import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
@@ -21,10 +19,7 @@ import java.util.stream.Collectors;
*/
public class ClusterTimeseries {
- private static final Logger log = Logger.getLogger(ClusterTimeseries.class.getName());
-
private final List<Node> clusterNodes;
- private final Map<String, Instant> startTimePerNode;
/** The measurements for all hosts in this snapshot */
private final List<NodeTimeseries> nodeTimeseries;
@@ -32,9 +27,10 @@ public class ClusterTimeseries {
public ClusterTimeseries(Cluster cluster, List<Node> clusterNodes, MetricsDb db, NodeRepository nodeRepository) {
this.clusterNodes = clusterNodes;
ClusterSpec.Type clusterType = clusterNodes.get(0).allocation().get().membership().cluster().type();
- this.nodeTimeseries = db.getNodeTimeseries(nodeRepository.clock().instant().minus(Autoscaler.scalingWindow(clusterType)),
- clusterNodes.stream().map(Node::hostname).collect(Collectors.toSet()));
- this.startTimePerNode = metricStartTimes(cluster, clusterNodes, nodeRepository);
+ var allTimeseries = db.getNodeTimeseries(nodeRepository.clock().instant().minus(Autoscaler.scalingWindow(clusterType)),
+ clusterNodes.stream().map(Node::hostname).collect(Collectors.toSet()));
+ Map<String, Instant> startTimePerNode = metricStartTimes(cluster, clusterNodes, allTimeseries, nodeRepository);
+ nodeTimeseries = filterStale(allTimeseries, startTimePerNode);
}
/**
@@ -43,6 +39,7 @@ public class ClusterTimeseries {
*/
private Map<String, Instant> metricStartTimes(Cluster cluster,
List<Node> clusterNodes,
+ List<NodeTimeseries> nodeTimeseries,
NodeRepository nodeRepository) {
Map<String, Instant> startTimePerHost = new HashMap<>();
if ( ! cluster.scalingEvents().isEmpty()) {
@@ -65,31 +62,22 @@ public class ClusterTimeseries {
return startTimePerHost;
}
- /**
- * Returns the average load of this resource in the measurement window,
- * or empty if we do not have a reliable measurement across the cluster nodes.
- */
- public Optional<Double> averageLoad(Resource resource, Cluster cluster) {
- ClusterSpec.Type clusterType = clusterNodes.get(0).allocation().get().membership().cluster().type();
-
- List<NodeTimeseries> currentMeasurements = filterStale(nodeTimeseries, startTimePerNode);
+ /** Returns the average number of measurements per node */
+ public int measurementsPerNode() {
+ int measurementCount = nodeTimeseries.stream().mapToInt(m -> m.size()).sum();
+ return measurementCount / clusterNodes.size();
+ }
- // Require a total number of measurements scaling with the number of nodes,
- // but don't require that we have at least that many from every node
- int measurementCount = currentMeasurements.stream().mapToInt(m -> m.size()).sum();
- if (measurementCount / clusterNodes.size() < Autoscaler.minimumMeasurementsPerNode(clusterType)) {
- log.fine(() -> "Too few measurements per node for " + cluster.toString() + ": measurementCount " + measurementCount +
- " (" + nodeTimeseries.stream().mapToInt(m -> m.size()).sum() + " before filtering");
- return Optional.empty();
- }
- if (currentMeasurements.size() != clusterNodes.size()) {
- log.fine(() -> "Mssing measurements from some nodes for " + cluster.toString() + ": Has from " + currentMeasurements.size() +
- "but need " + clusterNodes.size() + "(before filtering: " + nodeTimeseries.size() + ")");
- return Optional.empty();
- }
+ /** Returns the number of nodes measured in this */
+ public int nodesMeasured() {
+ return nodeTimeseries.size();
+ }
- double measurementSum = currentMeasurements.stream().flatMap(m -> m.asList().stream()).mapToDouble(m -> value(resource, m)).sum();
- return Optional.of(measurementSum / measurementCount);
+ /** Returns the average load of this resource in this */
+ public double averageLoad(Resource resource) {
+ int measurementCount = nodeTimeseries.stream().mapToInt(m -> m.size()).sum();
+ double measurementSum = nodeTimeseries.stream().flatMap(m -> m.asList().stream()).mapToDouble(m -> value(resource, m)).sum();
+ return measurementSum / measurementCount;
}
private double value(Resource resource, MetricSnapshot snapshot) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java
index b53f56e4743..809c54146d0 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java
@@ -17,7 +17,6 @@ import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb;
import java.time.Duration;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -70,13 +69,13 @@ public class AutoscalingMaintainer extends NodeRepositoryMaintainer {
Optional<Cluster> cluster = application.cluster(clusterId);
if (cluster.isEmpty()) return;
- log.fine(() -> "Autoscale " + application.toString());
-
var advice = autoscaler.autoscale(cluster.get(), clusterNodes);
- if (advice.isEmpty()) return;
-
- if ( ! cluster.get().targetResources().equals(advice.target())) {
+ application = application.with(cluster.get().withAutoscalingStatus(advice.reason()));
+ if (advice.isEmpty()) {
+ applications().put(application, deployment.applicationLock().get());
+ }
+ else if ( ! cluster.get().targetResources().equals(advice.target())) {
applications().put(application.with(cluster.get().withTarget(advice.target())), deployment.applicationLock().get());
if (advice.target().isPresent()) {
logAutoscaling(advice.target().get(), applicationId, cluster.get(), clusterNodes);
@@ -100,11 +99,7 @@ public class AutoscalingMaintainer extends NodeRepositoryMaintainer {
}
static String toString(ClusterResources r) {
- return String.format(Locale.US, "%d%s * [vcpu: %.1f, memory: %.1f Gb, disk %.1f Gb]" +
- " (total: [vcpu: %.1f, memory: %.1f Gb, disk: %.1f Gb])",
- r.nodes(), r.groups() > 1 ? " (in " + r.groups() + " groups)" : "",
- r.nodeResources().vcpu(), r.nodeResources().memoryGb(), r.nodeResources().diskGb(),
- r.nodes() * r.nodeResources().vcpu(), r.nodes() * r.nodeResources().memoryGb(), r.nodes() * r.nodeResources().diskGb());
+ return r + " (total: " + r.totalResources() + ")";
}
private Map<ClusterSpec.Id, List<Node>> nodesByCluster(List<Node> applicationNodes) {
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 3bf287a3e80..d2dcaaeae5b 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
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.maintenance;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.jdisc.Metric;
@@ -13,6 +14,7 @@ import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.curator.stats.LatencyMetrics;
import com.yahoo.vespa.curator.stats.LockStats;
import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.Node.State;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
@@ -26,12 +28,13 @@ import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
+import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static com.yahoo.config.provision.NodeResources.DiskSpeed.any;
-import static com.yahoo.vespa.hosted.provision.Node.State.active;
/**
* @author oyving
@@ -70,9 +73,39 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
updateLockMetrics();
updateDockerMetrics(nodes);
updateTenantUsageMetrics(nodes);
+ updateRepairTicketMetrics(nodes);
+ updateAllocationMetrics(nodes);
return true;
}
+ private void updateAllocationMetrics(NodeList nodes) {
+ Map<ClusterKey, List<Node>> byCluster = nodes.stream()
+ .filter(node -> node.allocation().isPresent())
+ .collect(Collectors.groupingBy(node -> new ClusterKey(node.allocation().get().owner(), node.allocation().get().membership().cluster().id())));
+ byCluster.forEach((clusterKey, allocatedNodes) -> {
+ int activeNodes = 0;
+ int nonActiveNodes = 0;
+ for (var node : allocatedNodes) {
+ if (node.state() == State.active) {
+ activeNodes++;
+ } else {
+ nonActiveNodes++;
+ }
+ }
+ double nonActiveFraction;
+ if (activeNodes == 0) { // Cluster has been removed
+ nonActiveFraction = 1;
+ } else {
+ nonActiveFraction = (double) nonActiveNodes / (double) activeNodes;
+ }
+ Map<String, String> dimensions = new HashMap<>(dimensions(clusterKey.application));
+ dimensions.put("clusterId", clusterKey.cluster.value());
+ metric.set("nodes.active", activeNodes, getContext(dimensions));
+ metric.set("nodes.nonActive", nonActiveNodes, getContext(dimensions));
+ metric.set("nodes.nonActiveFraction", nonActiveFraction, getContext(dimensions));
+ });
+ }
+
private void updateZoneMetrics() {
metric.set("zone.working", nodeRepository().isWorking() ? 1 : 0, null);
}
@@ -99,14 +132,12 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
Optional<Allocation> allocation = node.allocation();
if (allocation.isPresent()) {
ApplicationId applicationId = allocation.get().owner();
- context = getContextAt(
- "state", node.state().name(),
- "host", node.hostname(),
- "tenantName", applicationId.tenant().value(),
- "applicationId", applicationId.serializedForm().replace(':', '.'),
- "app", toApp(applicationId),
- "clustertype", allocation.get().membership().cluster().type().name(),
- "clusterid", allocation.get().membership().cluster().id().value());
+ Map<String, String> dimensions = new HashMap<>(dimensions(applicationId));
+ dimensions.put("state", node.state().name());
+ dimensions.put("host", node.hostname());
+ dimensions.put("clustertype", allocation.get().membership().cluster().type().name());
+ dimensions.put("clusterid", allocation.get().membership().cluster().id().value());
+ context = getContext(dimensions);
long wantedRestartGeneration = allocation.get().restartGeneration().wanted();
metric.set("wantedRestartGeneration", wantedRestartGeneration, context);
@@ -126,9 +157,8 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
currentVersion.get().equals(wantedVersion);
metric.set("wantToChangeVespaVersion", converged ? 0 : 1, context);
} else {
- context = getContextAt(
- "state", node.state().name(),
- "host", node.hostname());
+ context = getContext(Map.of("state", node.state().name(),
+ "host", node.hostname()));
}
Optional<Version> currentVersion = node.status().vespaVersion();
@@ -211,24 +241,16 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
return version.getMinor() + version.getMicro() / 1000.0;
}
- private Metric.Context getContextAt(String... point) {
- if (point.length % 2 != 0)
- throw new IllegalArgumentException("Dimension specification comes in pairs");
-
- Map<String, String> dimensions = new HashMap<>();
- for (int i = 0; i < point.length; i += 2) {
- dimensions.put(point[i], point[i + 1]);
- }
-
+ private Metric.Context getContext(Map<String, String> dimensions) {
return contextMap.computeIfAbsent(dimensions, metric::createContext);
}
private void updateNodeCountMetrics(NodeList nodes) {
- Map<Node.State, List<Node>> nodesByState = nodes.nodeType(NodeType.tenant).asList().stream()
- .collect(Collectors.groupingBy(Node::state));
+ Map<State, List<Node>> nodesByState = nodes.nodeType(NodeType.tenant).asList().stream()
+ .collect(Collectors.groupingBy(Node::state));
// Count per state
- for (Node.State state : Node.State.values()) {
+ for (State state : State.values()) {
List<Node> nodesInState = nodesByState.getOrDefault(state, List.of());
metric.set("hostedVespa." + state.name() + "Hosts", nodesInState.size(), null);
}
@@ -237,7 +259,7 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
private void updateLockMetrics() {
LockStats.getGlobal().getLockMetricsByPath()
.forEach((lockPath, lockMetrics) -> {
- Metric.Context context = getContextAt("lockPath", lockPath);
+ Metric.Context context = getContext(Map.of("lockPath", lockPath));
metric.set("lockAttempt.acquire", lockMetrics.getAndResetAcquireCount(), context);
metric.set("lockAttempt.acquireFailed", lockMetrics.getAndResetAcquireFailedCount(), context);
@@ -285,10 +307,7 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
.map(node -> node.allocation().get().requestedResources().justNumbers())
.reduce(new NodeResources(0, 0, 0, 0, any), NodeResources::add);
- var context = getContextAt(
- "tenantName", applicationId.tenant().value(),
- "applicationId", applicationId.serializedForm().replace(':', '.'),
- "app", toApp(applicationId));
+ var context = getContext(dimensions(applicationId));
metric.set("hostedVespa.docker.allocatedCapacityCpu", allocatedCapacity.vcpu(), context);
metric.set("hostedVespa.docker.allocatedCapacityMem", allocatedCapacity.memoryGb(), context);
@@ -297,24 +316,65 @@ public class MetricsReporter extends NodeRepositoryMaintainer {
);
}
+ private void updateRepairTicketMetrics(NodeList nodes) {
+ nodes.nodeType(NodeType.host).stream()
+ .map(node -> node.reports().getReport("repairTicket"))
+ .flatMap(Optional::stream)
+ .map(report -> report.getInspector().field("status").asString())
+ .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
+ .forEach((status, number) -> metric.set("hostedVespa.breakfixedHosts", number, getContext(Map.of("status", status))));
+ }
+
+ private static Map<String, String> dimensions(ApplicationId application) {
+ return Map.of("tenantName", application.tenant().value(),
+ "applicationId", application.serializedForm().replace(':', '.'),
+ "app", toApp(application));
+ }
+
private static NodeResources getCapacityTotal(NodeList nodes) {
- return nodes.hosts().state(active).asList().stream()
- .map(host -> host.flavor().resources())
- .map(NodeResources::justNumbers)
- .reduce(new NodeResources(0, 0, 0, 0, any), NodeResources::add);
+ return nodes.hosts().state(State.active).asList().stream()
+ .map(host -> host.flavor().resources())
+ .map(NodeResources::justNumbers)
+ .reduce(new NodeResources(0, 0, 0, 0, any), NodeResources::add);
}
private static NodeResources getFreeCapacityTotal(NodeList nodes) {
- return nodes.hosts().state(active).asList().stream()
- .map(n -> freeCapacityOf(nodes, n))
- .map(NodeResources::justNumbers)
- .reduce(new NodeResources(0, 0, 0, 0, any), NodeResources::add);
+ return nodes.hosts().state(State.active).asList().stream()
+ .map(n -> freeCapacityOf(nodes, n))
+ .map(NodeResources::justNumbers)
+ .reduce(new NodeResources(0, 0, 0, 0, any), NodeResources::add);
}
private static NodeResources freeCapacityOf(NodeList nodes, Node dockerHost) {
return nodes.childrenOf(dockerHost).asList().stream()
- .map(node -> node.flavor().resources().justNumbers())
- .reduce(dockerHost.flavor().resources().justNumbers(), NodeResources::subtract);
+ .map(node -> node.flavor().resources().justNumbers())
+ .reduce(dockerHost.flavor().resources().justNumbers(), NodeResources::subtract);
+ }
+
+ private static class ClusterKey {
+
+ private final ApplicationId application;
+ private final ClusterSpec.Id cluster;
+
+ public ClusterKey(ApplicationId application, ClusterSpec.Id cluster) {
+ this.application = application;
+ this.cluster = cluster;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClusterKey that = (ClusterKey) o;
+ return application.equals(that.application) &&
+ cluster.equals(that.cluster);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(application, cluster);
+ }
+
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java
index 41d6c1e5425..bac31c40418 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java
@@ -20,6 +20,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static com.yahoo.config.provision.NodeType.confighost;
import static com.yahoo.config.provision.NodeType.controllerhost;
@@ -254,18 +255,25 @@ public class IP {
* @return an allocation from the pool, if any can be made
*/
public Optional<Allocation> findAllocation(LockedNodeList nodes, NameResolver resolver) {
+ if (ipAddresses.asSet().isEmpty()) {
+ // IP addresses have not yet been resolved and should be done later.
+ return findUnusedAddressStream(nodes)
+ .map(Allocation::ofAddress)
+ .findFirst();
+ }
+
if (ipAddresses.protocol == IpAddresses.Protocol.ipv4) {
- return findUnused(nodes).stream()
+ return findUnusedIpAddresses(nodes).stream()
.findFirst()
.map(addr -> Allocation.ofIpv4(addr, resolver));
}
- var unusedAddresses = findUnused(nodes);
+ var unusedAddresses = findUnusedIpAddresses(nodes);
var allocation = unusedAddresses.stream()
.filter(IP::isV6)
.findFirst()
.map(addr -> Allocation.ofIpv6(addr, resolver));
- allocation.flatMap(Allocation::secondary).ifPresent(ipv4Address -> {
+ allocation.flatMap(Allocation::ipv4Address).ifPresent(ipv4Address -> {
if (!unusedAddresses.contains(ipv4Address)) {
throw new IllegalArgumentException("Allocation resolved " + ipv4Address + " from hostname " +
allocation.get().hostname +
@@ -276,17 +284,43 @@ public class IP {
}
/**
- * Finds all unused addresses in this pool
+ * Finds all unused IP addresses in this pool
*
* @param nodes a list of all nodes in the repository
*/
- public Set<String> findUnused(NodeList nodes) {
+ public Set<String> findUnusedIpAddresses(NodeList nodes) {
var unusedAddresses = new LinkedHashSet<>(getIpSet());
nodes.matching(node -> node.ipConfig().primary().stream().anyMatch(ip -> getIpSet().contains(ip)))
.forEach(node -> unusedAddresses.removeAll(node.ipConfig().primary()));
return Collections.unmodifiableSet(unusedAddresses);
}
+ /**
+ * Returns the number of unused IP addresses in the pool, assuming any and all unaccounted for hostnames
+ * in the pool are resolved to exactly 1 IP address (or 2 with {@link IpAddresses.Protocol#dualStack}).
+ */
+ public int eventuallyUnusedAddressCount(NodeList nodes) {
+ // The address pool is filled immediately upon provisioning in dynamically provisioned zones,
+ // and within short time the IP address pool is filled. For all other cases, the IP address
+ // pool is already filled.
+ //
+ // The count in this method relies on the size of the IP address pool if that's non-empty,
+ // otherwise fall back to the address/hostname pool.
+
+
+ Set<String> currentIpAddresses = this.ipAddresses.asSet();
+ if (!currentIpAddresses.isEmpty()) {
+ return findUnusedIpAddresses(nodes).size();
+ }
+
+ return (int) findUnusedAddressStream(nodes).count();
+ }
+
+ private Stream<Address> findUnusedAddressStream(NodeList nodes) {
+ Set<String> hostnames = nodes.stream().map(Node::hostname).collect(Collectors.toSet());
+ return addresses.stream().filter(address -> !hostnames.contains(address.hostname()));
+ }
+
public IpAddresses.Protocol getProtocol() {
return ipAddresses.protocol;
}
@@ -299,10 +333,6 @@ public class IP {
return addresses;
}
- public boolean isEmpty() {
- return getIpSet().isEmpty();
- }
-
public Pool withIpAddresses(Set<String> ipAddresses) {
return Pool.of(ipAddresses, addresses);
}
@@ -326,22 +356,17 @@ public class IP {
}
- /** An IP address allocation from a pool */
+ /** An address allocation from a pool */
public static class Allocation {
private final String hostname;
- private final String primary;
- private final Optional<String> secondary;
-
- private Allocation(String hostname, String primary, Optional<String> secondary) {
- Objects.requireNonNull(primary, "primary must be non-null");
- Objects.requireNonNull(secondary, "ipv4Address must be non-null");
- if (secondary.isPresent() && !isV4(secondary.get())) { // Secondary must be IPv4, if present
- throw new IllegalArgumentException("Invalid IPv4 address '" + secondary + "'");
- }
+ private final Optional<String> ipv4Address;
+ private final Optional<String> ipv6Address;
+
+ private Allocation(String hostname, Optional<String> ipv4Address, Optional<String> ipv6Address) {
this.hostname = Objects.requireNonNull(hostname, "hostname must be non-null");
- this.primary = primary;
- this.secondary = secondary;
+ this.ipv4Address = Objects.requireNonNull(ipv4Address, "ipv4Address must be non-null");
+ this.ipv6Address = Objects.requireNonNull(ipv6Address, "ipv6Address must be non-null");
}
/**
@@ -350,13 +375,17 @@ public class IP {
* A successful allocation is guaranteed to have an IPv6 address, but may also have an IPv4 address if the
* hostname of the IPv6 address has an A record.
*
- * @param ipAddress Unassigned IPv6 address
+ * @param ipv6Address Unassigned IPv6 address
* @param resolver DNS name resolver to use
* @throws IllegalArgumentException if DNS is misconfigured
* @return An allocation containing 1 IPv6 address and 1 IPv4 address (if hostname is dual-stack)
*/
- private static Allocation ofIpv6(String ipAddress, NameResolver resolver) {
- String hostname6 = resolver.resolveHostname(ipAddress).orElseThrow(() -> new IllegalArgumentException("Could not resolve IP address: " + ipAddress));
+ private static Allocation ofIpv6(String ipv6Address, NameResolver resolver) {
+ if (!isV6(ipv6Address)) {
+ throw new IllegalArgumentException("Invalid IPv6 address '" + ipv6Address + "'");
+ }
+
+ String hostname6 = resolver.resolveHostname(ipv6Address).orElseThrow(() -> new IllegalArgumentException("Could not resolve IP address: " + ipv6Address));
List<String> ipv4Addresses = resolver.resolveAll(hostname6).stream()
.filter(IP::isV4)
.collect(Collectors.toList());
@@ -369,10 +398,10 @@ public class IP {
if (!hostname6.equals(hostname4)) {
throw new IllegalArgumentException(String.format("Hostnames resolved from each IP address do not " +
"point to the same hostname [%s -> %s, %s -> %s]",
- ipAddress, hostname6, addr, hostname4));
+ ipv6Address, hostname6, addr, hostname4));
}
});
- return new Allocation(hostname6, ipAddress, ipv4Address);
+ return new Allocation(hostname6, ipv4Address, Optional.of(ipv6Address));
}
/**
@@ -391,7 +420,11 @@ public class IP {
throw new IllegalArgumentException("Hostname " + hostname4 + " did not resolve to exactly 1 address. " +
"Resolved: " + addresses);
}
- return new Allocation(hostname4, addresses.get(0), Optional.empty());
+ return new Allocation(hostname4, Optional.of(addresses.get(0)), Optional.empty());
+ }
+
+ private static Allocation ofAddress(Address address) {
+ return new Allocation(address.hostname(), Optional.empty(), Optional.empty());
}
/** Hostname pointing to the IP addresses in this */
@@ -399,27 +432,28 @@ public class IP {
return hostname;
}
- /** Primary address of this allocation */
- public String primary() {
- return primary;
+ /** IPv4 address of this allocation */
+ public Optional<String> ipv4Address() {
+ return ipv4Address;
}
- /** Secondary address of this allocation */
- public Optional<String> secondary() {
- return secondary;
+ /** IPv6 address of this allocation */
+ public Optional<String> ipv6Address() {
+ return ipv6Address;
}
/** All IP addresses in this */
public Set<String> addresses() {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
- secondary.ifPresent(builder::add);
- builder.add(primary);
+ ipv4Address.ifPresent(builder::add);
+ ipv6Address.ifPresent(builder::add);
return builder.build();
}
@Override
public String toString() {
- return String.format("IP allocation [primary=%s, secondary=%s]", primary, secondary.orElse("<none>"));
+ return String.format("Address allocation [hostname=%s, IPv4=%s, IPv6=%s]",
+ hostname, ipv4Address.orElse("<none>"), ipv6Address.orElse("<none>"));
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
index 2ddbd6def6f..3979b898145 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
@@ -47,6 +47,7 @@ public class ApplicationSerializer {
private static final String groupsKey = "groups";
private static final String nodeResourcesKey = "resources";
private static final String scalingEventsKey = "scalingEvents";
+ private static final String autoscalingStatusKey = "autoscalingStatus";
private static final String fromKey = "from";
private static final String toKey = "to";
private static final String generationKey = "generation";
@@ -95,6 +96,7 @@ public class ApplicationSerializer {
cluster.suggestedResources().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject(suggestedResourcesKey)));
cluster.targetResources().ifPresent(target -> toSlime(target, clusterObject.setObject(targetResourcesKey)));
scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray(scalingEventsKey));
+ clusterObject.setString(autoscalingStatusKey, cluster.autoscalingStatus());
}
private static Cluster clusterFromSlime(String id, Inspector clusterObject) {
@@ -104,7 +106,8 @@ public class ApplicationSerializer {
clusterResourcesFromSlime(clusterObject.field(maxResourcesKey)),
optionalClusterResourcesFromSlime(clusterObject.field(suggestedResourcesKey)),
optionalClusterResourcesFromSlime(clusterObject.field(targetResourcesKey)),
- scalingEventsFromSlime(clusterObject.field(scalingEventsKey)));
+ scalingEventsFromSlime(clusterObject.field(scalingEventsKey)),
+ clusterObject.field(autoscalingStatusKey).asString());
}
private static void toSlime(ClusterResources resources, Cursor clusterResourcesObject) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java
index 45156c57481..b4cb9158a5c 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java
@@ -6,9 +6,6 @@ import com.google.common.base.Suppliers;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.curator.Lock;
-import com.yahoo.vespa.flags.BooleanFlag;
-import com.yahoo.vespa.flags.FlagSource;
-import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.hosted.provision.persistence.CuratorDatabaseClient;
import java.time.Duration;
@@ -31,7 +28,6 @@ public class ContainerImages {
private final CuratorDatabaseClient db;
private final DockerImage defaultImage;
- private final BooleanFlag replaceImage;
/**
* The container image is read on every request to /nodes/v2/node/[fqdn]. Cache current images to avoid
@@ -40,10 +36,9 @@ public class ContainerImages {
*/
private volatile Supplier<Map<NodeType, DockerImage>> images;
- public ContainerImages(CuratorDatabaseClient db, DockerImage defaultImage, FlagSource flagSource) {
+ public ContainerImages(CuratorDatabaseClient db, DockerImage defaultImage) {
this.db = db;
this.defaultImage = defaultImage;
- this.replaceImage = Flags.REGIONAL_CONTAINER_REGISTRY.bindTo(flagSource);
createCache();
}
@@ -85,7 +80,7 @@ public class ContainerImages {
/** Rewrite the registry part of given image, using this zone's default image */
private DockerImage rewriteRegistry(DockerImage image) {
DockerImage zoneImage = defaultImage;
- if (zoneImage.replacedBy().isPresent() && replaceImage.value()) {
+ if (zoneImage.replacedBy().isPresent()) {
zoneImage = zoneImage.replacedBy().get();
}
return image.withRegistry(zoneImage.registry());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
index b0baae650e4..6462fb6f19d 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
@@ -71,47 +71,47 @@ public class GroupPreparer {
}
// There were some changes, so re-do the allocation with locks
- try (Mutex lock = nodeRepository.lock(application)) {
- try (Mutex allocationLock = nodeRepository.lockUnallocated()) {
- NodeAllocation allocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes,
- highestIndex, wantedGroups, allocationLock);
-
- if (nodeRepository.zone().getCloud().dynamicProvisioning()) {
- Version osVersion = nodeRepository.osVersions().targetFor(NodeType.host).orElse(Version.emptyVersion);
- List<ProvisionedHost> provisionedHosts = allocation.getFulfilledDockerDeficit()
- .map(deficit -> hostProvisioner.get().provisionHosts(nodeRepository.database().getProvisionIndexes(deficit.getCount()),
- deficit.getFlavor(),
- application,
- osVersion,
- requestedNodes.isExclusive() ? HostSharing.exclusive : HostSharing.any))
- .orElseGet(List::of);
-
- // At this point we have started provisioning of the hosts, the first priority is to make sure that
- // the returned hosts are added to the node-repo so that they are tracked by the provision maintainers
- List<Node> hosts = provisionedHosts.stream()
- .map(ProvisionedHost::generateHost)
- .collect(Collectors.toList());
- nodeRepository.addNodes(hosts, Agent.application);
-
- // Offer the nodes on the newly provisioned hosts, this should be enough to cover the deficit
- List<NodeCandidate> candidates = provisionedHosts.stream()
- .map(host -> NodeCandidate.createNewExclusiveChild(host.generateNode(),
- host.generateHost()))
- .collect(Collectors.toList());
- allocation.offer(candidates);
- }
-
- if (! allocation.fulfilled() && requestedNodes.canFail())
- throw new OutOfCapacityException((cluster.group().isPresent() ? "Out of capacity on " + cluster.group().get() :"") +
- allocation.outOfCapacityDetails());
-
- // Carry out and return allocation
- nodeRepository.reserve(allocation.reservableNodes());
- nodeRepository.addDockerNodes(new LockedNodeList(allocation.newNodes(), allocationLock));
- List<Node> acceptedNodes = allocation.finalNodes();
- surplusActiveNodes.removeAll(acceptedNodes);
- return acceptedNodes;
+ try (Mutex lock = nodeRepository.lock(application);
+ Mutex allocationLock = nodeRepository.lockUnallocated()) {
+
+ NodeAllocation allocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes,
+ highestIndex, wantedGroups, allocationLock);
+
+ if (nodeRepository.zone().getCloud().dynamicProvisioning()) {
+ Version osVersion = nodeRepository.osVersions().targetFor(NodeType.host).orElse(Version.emptyVersion);
+ List<ProvisionedHost> provisionedHosts = allocation.getFulfilledDockerDeficit()
+ .map(deficit -> hostProvisioner.get().provisionHosts(nodeRepository.database().getProvisionIndexes(deficit.getCount()),
+ deficit.getFlavor(),
+ application,
+ osVersion,
+ requestedNodes.isExclusive() ? HostSharing.exclusive : HostSharing.any))
+ .orElseGet(List::of);
+
+ // At this point we have started provisioning of the hosts, the first priority is to make sure that
+ // the returned hosts are added to the node-repo so that they are tracked by the provision maintainers
+ List<Node> hosts = provisionedHosts.stream()
+ .map(ProvisionedHost::generateHost)
+ .collect(Collectors.toList());
+ nodeRepository.addNodes(hosts, Agent.application);
+
+ // Offer the nodes on the newly provisioned hosts, this should be enough to cover the deficit
+ List<NodeCandidate> candidates = provisionedHosts.stream()
+ .map(host -> NodeCandidate.createNewExclusiveChild(host.generateNode(),
+ host.generateHost()))
+ .collect(Collectors.toList());
+ allocation.offer(candidates);
}
+
+ if (! allocation.fulfilled() && requestedNodes.canFail())
+ throw new OutOfCapacityException((cluster.group().isPresent() ? "Out of capacity on " + cluster.group().get() :"") +
+ allocation.outOfCapacityDetails());
+
+ // Carry out and return allocation
+ nodeRepository.reserve(allocation.reservableNodes());
+ nodeRepository.addDockerNodes(new LockedNodeList(allocation.newNodes(), allocationLock));
+ List<Node> acceptedNodes = allocation.finalNodes();
+ surplusActiveNodes.removeAll(acceptedNodes);
+ return acceptedNodes;
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java
index 96053fdaa91..af3bde02421 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java
@@ -82,7 +82,11 @@ public class HostCapacity {
* Number of free (not allocated) IP addresses assigned to the dockerhost.
*/
int freeIPs(Node dockerHost) {
- return dockerHost.ipConfig().pool().findUnused(allNodes).size();
+ if (dockerHost.type() == NodeType.host) {
+ return dockerHost.ipConfig().pool().eventuallyUnusedAddressCount(allNodes);
+ } else {
+ return dockerHost.ipConfig().pool().findUnusedIpAddresses(allNodes).size();
+ }
}
/**
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
index f8231072a28..14937e6afeb 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
@@ -363,11 +363,11 @@ abstract class NodeCandidate implements Nodelike, Comparable<NodeCandidate> {
try {
allocation = parent.get().ipConfig().pool().findAllocation(allNodes, nodeRepository.nameResolver());
if (allocation.isEmpty()) return new InvalidNodeCandidate(resources, freeParentCapacity, parent.get(),
- "No IP addresses available on parent host");
+ "No addresses available on parent host");
} catch (Exception e) {
- log.warning("Failed allocating IP address on " + parent.get() +": " + Exceptions.toMessageString(e));
+ log.warning("Failed allocating address on " + parent.get() +": " + Exceptions.toMessageString(e));
return new InvalidNodeCandidate(resources, freeParentCapacity, parent.get(),
- "Failed when allocating IP address on host");
+ "Failed when allocating address on host");
}
Node node = Node.createDockerNode(allocation.get().addresses(),
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java
index 61cedbb9373..02621c79019 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java
@@ -7,10 +7,12 @@ import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.OsVersion;
import com.yahoo.vespa.hosted.provision.node.Status;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -26,25 +28,33 @@ public class ProvisionedHost {
private final String hostHostname;
private final Flavor hostFlavor;
private final Optional<ApplicationId> exclusiveTo;
- private final String nodeHostname;
+ private final List<Address> nodeAddresses;
private final NodeResources nodeResources;
private final Version osVersion;
public ProvisionedHost(String id, String hostHostname, Flavor hostFlavor, Optional<ApplicationId> exclusiveTo,
- String nodeHostname, NodeResources nodeResources, Version osVersion) {
+ List<Address> nodeAddresses, NodeResources nodeResources, Version osVersion) {
this.id = Objects.requireNonNull(id, "Host id must be set");
this.hostHostname = Objects.requireNonNull(hostHostname, "Host hostname must be set");
this.hostFlavor = Objects.requireNonNull(hostFlavor, "Host flavor must be set");
this.exclusiveTo = Objects.requireNonNull(exclusiveTo, "exclusiveTo must be set");
- this.nodeHostname = Objects.requireNonNull(nodeHostname, "Node hostname must be set");
+ this.nodeAddresses = validateNodeAddresses(nodeAddresses);
this.nodeResources = Objects.requireNonNull(nodeResources, "Node resources must be set");
this.osVersion = Objects.requireNonNull(osVersion, "OS version must be set");
}
+ private static List<Address> validateNodeAddresses(List<Address> nodeAddresses) {
+ Objects.requireNonNull(nodeAddresses, "Node addresses must be set");
+ if (nodeAddresses.isEmpty()) {
+ throw new IllegalArgumentException("There must be at least one node address");
+ }
+ return nodeAddresses;
+ }
+
/** Generate {@link Node} instance representing the provisioned physical host */
public Node generateHost() {
Node.Builder builder = Node
- .create(id, IP.Config.EMPTY, hostHostname, hostFlavor, NodeType.host)
+ .create(id, IP.Config.of(Set.of(), Set.of(), nodeAddresses), hostHostname, hostFlavor, NodeType.host)
.status(Status.initial().withOsVersion(OsVersion.EMPTY.withCurrent(Optional.of(osVersion))));
exclusiveTo.ifPresent(builder::exclusiveTo);
return builder.build();
@@ -52,7 +62,7 @@ public class ProvisionedHost {
/** Generate {@link Node} instance representing the node running on this physical host */
public Node generateNode() {
- return Node.createDockerNode(Set.of(), nodeHostname, hostHostname, nodeResources, NodeType.tenant).build();
+ return Node.createDockerNode(Set.of(), nodeHostname(), hostHostname, nodeResources, NodeType.tenant).build();
}
public String getId() {
@@ -68,7 +78,11 @@ public class ProvisionedHost {
}
public String nodeHostname() {
- return nodeHostname;
+ return nodeAddresses.get(0).hostname();
+ }
+
+ public List<Address> nodeAddresses() {
+ return nodeAddresses;
}
public NodeResources nodeResources() { return nodeResources; }
@@ -81,14 +95,14 @@ public class ProvisionedHost {
return id.equals(that.id) &&
hostHostname.equals(that.hostHostname) &&
hostFlavor.equals(that.hostFlavor) &&
- nodeHostname.equals(that.nodeHostname) &&
+ nodeAddresses.equals(that.nodeAddresses) &&
nodeResources.equals(that.nodeResources) &&
osVersion.equals(that.osVersion);
}
@Override
public int hashCode() {
- return Objects.hash(id, hostHostname, hostFlavor, nodeHostname, nodeResources, osVersion);
+ return Objects.hash(id, hostHostname, hostFlavor, nodeAddresses, nodeResources, osVersion);
}
@Override
@@ -97,7 +111,7 @@ public class ProvisionedHost {
"id='" + id + '\'' +
", hostHostname='" + hostHostname + '\'' +
", hostFlavor=" + hostFlavor +
- ", nodeHostname='" + nodeHostname + '\'' +
+ ", nodeAddresses='" + nodeAddresses + '\'' +
", nodeResources=" + nodeResources +
", osVersion=" + osVersion +
'}';
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java
index 9433b89ddc4..91b54fa37e9 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java
@@ -8,6 +8,7 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.applications.Application;
import com.yahoo.vespa.hosted.provision.applications.Cluster;
+import com.yahoo.vespa.hosted.provision.applications.ScalingEvent;
import com.yahoo.vespa.hosted.provision.autoscale.AllocatableClusterResources;
import java.net.URI;
@@ -51,6 +52,8 @@ public class ApplicationSerializer {
toSlime(currentResources, clusterObject.setObject("current"));
cluster.suggestedResources().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject("suggested")));
cluster.targetResources().ifPresent(target -> toSlime(target, clusterObject.setObject("target")));
+ scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray("scalingEvents"));
+ clusterObject.setString("autoscalingStatus", cluster.autoscalingStatus());
}
private static void toSlime(ClusterResources resources, Cursor clusterResourcesObject) {
@@ -59,4 +62,13 @@ public class ApplicationSerializer {
NodeResourcesSerializer.toSlime(resources.nodeResources(), clusterResourcesObject.setObject("resources"));
}
+ private static void scalingEventsToSlime(List<ScalingEvent> scalingEvents, Cursor scalingEventsArray) {
+ for (ScalingEvent scalingEvent : scalingEvents) {
+ Cursor scalingEventObject = scalingEventsArray.addObject();
+ toSlime(scalingEvent.from(), scalingEventObject.setObject("from"));
+ toSlime(scalingEvent.to(), scalingEventObject.setObject("to"));
+ scalingEventObject.setLong("at", scalingEvent.at().toEpochMilli());
+ }
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
index 304cebb3c01..c43629aeb09 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
@@ -29,6 +29,7 @@ import com.yahoo.vespa.hosted.provision.NoSuchNodeException;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.applications.Application;
+import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter;
@@ -256,8 +257,12 @@ public class NodesV2ApiHandler extends LoggingRequestHandler {
Set<String> ipAddressPool = new HashSet<>();
inspector.field("additionalIpAddresses").traverse((ArrayTraverser) (i, item) -> ipAddressPool.add(item.asString()));
+ List<Address> addressPool = new ArrayList<>();
+ inspector.field("additionalHostnames").traverse((ArrayTraverser) (i, item) ->
+ addressPool.add(new Address(item.asString())));
+
Node.Builder builder = Node.create(inspector.field("openStackId").asString(),
- IP.Config.of(ipAddresses, ipAddressPool, List.of()),
+ IP.Config.of(ipAddresses, ipAddressPool, addressPool),
inspector.field("hostname").asString(),
flavorFromSlime(inspector),
nodeTypeFromSlime(inspector.field("type")));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java
index 5813a7067cd..5393aa7cfb8 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java
@@ -17,6 +17,7 @@ import com.yahoo.vespa.hosted.provision.Nodelike;
import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
import org.junit.Test;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -44,11 +45,13 @@ public class AutoscalingTest {
// deploy
tester.deploy(application1, cluster1, 5, 1, hostResources);
+ tester.clock().advance(Duration.ofDays(1));
assertTrue("No measurements -> No change", tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
tester.addCpuMeasurements(0.25f, 1f, 59, application1);
assertTrue("Too few measurements -> No change", tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ tester.clock().advance(Duration.ofDays(1));
tester.addCpuMeasurements(0.25f, 1f, 60, application1);
ClusterResources scaledResources = tester.assertResources("Scaling up since resource usage is too high",
15, 1, 1.3, 28.6, 28.6,
@@ -58,6 +61,8 @@ public class AutoscalingTest {
assertTrue("Cluster in flux -> No further change", tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
tester.deactivateRetired(application1, cluster1, scaledResources);
+
+ tester.clock().advance(Duration.ofDays(1));
tester.addCpuMeasurements(0.8f, 1f, 3, application1);
assertTrue("Load change is large, but insufficient measurements for new config -> No change",
tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
@@ -112,6 +117,7 @@ public class AutoscalingTest {
tester.nodeRepository().getNodes(application1).stream()
.allMatch(n -> n.allocation().get().requestedResources().diskSpeed() == NodeResources.DiskSpeed.slow);
+ tester.clock().advance(Duration.ofDays(1));
tester.addCpuMeasurements(0.25f, 1f, 120, application1);
// Changing min and max from slow to any
ClusterResources min = new ClusterResources( 2, 1,
@@ -184,7 +190,7 @@ public class AutoscalingTest {
}
@Test
- public void test_autoscaling_limits_when_min_equals_xax() {
+ public void test_autoscaling_limits_when_min_equals_max() {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = min;
@@ -195,6 +201,7 @@ public class AutoscalingTest {
// deploy
tester.deploy(application1, cluster1, 5, 1, resources);
+ tester.clock().advance(Duration.ofDays(1));
tester.addCpuMeasurements(0.25f, 1f, 120, application1);
assertTrue(tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
}
@@ -283,6 +290,31 @@ public class AutoscalingTest {
// deploy
tester.deploy(application1, cluster1, 6, 1, hostResources.withVcpu(hostResources.vcpu() / 2));
+ tester.clock().advance(Duration.ofDays(1));
+ tester.addMemMeasurements(0.02f, 0.95f, 120, application1);
+ tester.assertResources("Scaling down",
+ 6, 1, 2.8, 4.0, 95.0,
+ tester.autoscale(application1, cluster1.id(), min, max).target());
+ }
+
+ @Test
+ public void scaling_down_only_after_delay() {
+ NodeResources hostResources = new NodeResources(6, 100, 100, 1);
+ ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
+ ClusterResources max = new ClusterResources(20, 1, new NodeResources(100, 1000, 1000, 1));
+ AutoscalingTester tester = new AutoscalingTester(hostResources);
+
+ ApplicationId application1 = tester.applicationId("application1");
+ ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
+
+ tester.deploy(application1, cluster1, 6, 1, hostResources.withVcpu(hostResources.vcpu() / 2));
+
+ // No autoscaling as it is too soon to scale down after initial deploy (counting as a scaling event)
+ tester.addMemMeasurements(0.02f, 0.95f, 120, application1);
+ assertTrue(tester.autoscale(application1, cluster1.id(), min, max).target().isEmpty());
+
+ // Trying the same a day later causes autoscaling
+ tester.clock().advance(Duration.ofDays(1));
tester.addMemMeasurements(0.02f, 0.95f, 120, application1);
tester.assertResources("Scaling down",
6, 1, 2.8, 4.0, 95.0,
@@ -344,6 +376,7 @@ public class AutoscalingTest {
// deploy (Why 103 Gb memory? See AutoscalingTester.MockHostResourcesCalculator
tester.deploy(application1, cluster1, 5, 1, new NodeResources(3, 103, 100, 1));
+ tester.clock().advance(Duration.ofDays(1));
tester.addMemMeasurements(0.9f, 0.6f, 120, application1);
ClusterResources scaledResources = tester.assertResources("Scaling up since resource usage is too high.",
8, 1, 3, 83, 34.3,
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 4d8b6d13a86..3faa4c244ee 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
@@ -20,6 +20,7 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.Nodelike;
import com.yahoo.vespa.hosted.provision.applications.Application;
+import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.provisioning.FatalProvisioningException;
@@ -294,7 +295,7 @@ class AutoscalingTester {
"hostname" + index,
hostFlavor,
Optional.empty(),
- "nodename" + index,
+ List.of(new Address("nodename" + index)),
resources,
osVersion));
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
index 5e318e00288..4b14174488e 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
@@ -110,9 +110,8 @@ public class AutoscalingMaintainerTest {
assertEquals(firstMaintenanceTime.toEpochMilli(), tester.deployer().lastDeployTime(app1).get().toEpochMilli());
// Add measurement of the expected generation, leading to rescaling
- tester.clock().advance(Duration.ofSeconds(1));
+ tester.clock().advance(Duration.ofHours(2));
tester.addMeasurements(0.1f, 0.1f, 0.1f, 1, 500, app1);
- //tester.clock().advance(Duration.ofSeconds(1));
Instant lastMaintenanceTime = tester.clock().instant();
tester.maintainer().maintain();
assertEquals(lastMaintenanceTime.toEpochMilli(), tester.deployer().lastDeployTime(app1).get().toEpochMilli());
@@ -122,10 +121,10 @@ public class AutoscalingMaintainerTest {
@Test
public void test_toString() {
- assertEquals("4 * [vcpu: 1.0, memory: 2.0 Gb, disk 4.0 Gb] (total: [vcpu: 4.0, memory: 8.0 Gb, disk: 16.0 Gb])",
+ assertEquals("4 nodes with [vcpu: 1.0, memory: 2.0 Gb, disk 4.0 Gb, bandwidth: 1.0 Gbps] (total: [vcpu: 4.0, memory: 8.0 Gb, disk 16.0 Gb, bandwidth: 4.0 Gbps])",
AutoscalingMaintainer.toString(new ClusterResources(4, 1, new NodeResources(1, 2, 4, 1))));
- assertEquals("4 (in 2 groups) * [vcpu: 1.0, memory: 2.0 Gb, disk 4.0 Gb] (total: [vcpu: 4.0, memory: 8.0 Gb, disk: 16.0 Gb])",
+ assertEquals("4 nodes (in 2 groups) with [vcpu: 1.0, memory: 2.0 Gb, disk 4.0 Gb, bandwidth: 1.0 Gbps] (total: [vcpu: 4.0, memory: 8.0 Gb, disk 16.0 Gb, bandwidth: 4.0 Gbps])",
AutoscalingMaintainer.toString(new ClusterResources(4, 2, new NodeResources(1, 2, 4, 1))));
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java
index 478376bc0cd..2833c4e11ba 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java
@@ -20,6 +20,7 @@ import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.custom.HostCapacity;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.Generation;
@@ -208,12 +209,12 @@ public class DynamicProvisioningMaintainerTest {
tester.maintainer.maintain();
assertTrue("No IP addresses written as DNS updates are failing",
- provisioning.get().stream().allMatch(host -> host.ipConfig().pool().isEmpty()));
+ provisioning.get().stream().allMatch(host -> host.ipConfig().pool().getIpSet().isEmpty()));
tester.hostProvisioner.without(Behaviour.failDnsUpdate);
tester.maintainer.maintain();
assertTrue("IP addresses written as DNS updates are succeeding",
- provisioning.get().stream().noneMatch(host -> host.ipConfig().pool().isEmpty()));
+ provisioning.get().stream().noneMatch(host -> host.ipConfig().pool().getIpSet().isEmpty()));
}
private static class DynamicProvisioningTester {
@@ -338,7 +339,7 @@ public class DynamicProvisioningMaintainerTest {
"hostname" + index,
hostFlavor,
Optional.empty(),
- "nodename" + index,
+ List.of(new Address("nodename" + index)),
resources,
osVersion));
}
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 dbc0a98d879..a25858c034f 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
@@ -3,14 +3,15 @@ package com.yahoo.vespa.hosted.provision.maintenance;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
+import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.Zone;
import com.yahoo.jdisc.Metric;
-import com.yahoo.test.ManualClock;
import com.yahoo.transaction.Mutex;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.applicationmodel.ApplicationInstance;
@@ -46,6 +47,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
+import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -59,6 +61,8 @@ import static org.mockito.Mockito.when;
*/
public class MetricsReporterTest {
+ private static final Duration LONG_INTERVAL = Duration.ofDays(1);
+
private final ServiceMonitor serviceMonitor = mock(ServiceMonitor.class);
private final ApplicationInstanceReference reference = mock(ApplicationInstanceReference.class);
@@ -138,7 +142,7 @@ public class MetricsReporterTest {
orchestrator,
serviceMonitor,
() -> 42,
- Duration.ofMinutes(1));
+ LONG_INTERVAL);
metricsReporter.maintain();
// Verify sum of values across dimensions, and remove these metrics to avoid checking against
@@ -222,7 +226,7 @@ public class MetricsReporterTest {
orchestrator,
serviceMonitor,
() -> 42,
- Duration.ofMinutes(1));
+ LONG_INTERVAL);
metricsReporter.maintain();
assertEquals(0, metric.values.get("hostedVespa.readyHosts")); // Only tenants counts
@@ -247,6 +251,53 @@ public class MetricsReporterTest {
assertEquals(2.0, metric.sumDoubleValues("hostedVespa.docker.allocatedCapacityCpu", app2context), 0.01d);
}
+ @Test
+ public void non_active_metric() {
+ ProvisioningTester tester = new ProvisioningTester.Builder().build();
+ tester.makeReadyHosts(5, new NodeResources(64, 256, 2000, 10));
+ tester.activateTenantHosts();
+ TestMetric metric = new TestMetric();
+ MetricsReporter metricsReporter = new MetricsReporter(tester.nodeRepository(),
+ metric,
+ tester.orchestrator(),
+ serviceMonitor,
+ () -> 42,
+ LONG_INTERVAL);
+
+
+ // Application is deployed
+ ApplicationId application = ApplicationId.from("t1", "a1", "default");
+ Map<String, String> dimensions = Map.of("applicationId", application.toFullString());
+ NodeResources resources = new NodeResources(2, 8, 100, 1);
+ List<Node> activeNodes = tester.deploy(application, Capacity.from(new ClusterResources(4, 1, resources)));
+ metricsReporter.maintain();
+ assertEquals(0D, getMetric("nodes.nonActiveFraction", metric, dimensions));
+ assertEquals(4, getMetric("nodes.active", metric, dimensions));
+ assertEquals(0, getMetric("nodes.nonActive", metric, dimensions));
+
+ // One node fails
+ tester.fail(activeNodes.get(0).hostname());
+ metricsReporter.maintain();
+ assertEquals(0.33D, getMetric("nodes.nonActiveFraction", metric, dimensions).doubleValue(), 0.005);
+ assertEquals(3, getMetric("nodes.active", metric, dimensions));
+ assertEquals(1, getMetric("nodes.nonActive", metric, dimensions));
+
+ // Cluster is removed
+ tester.deactivate(application);
+ metricsReporter.maintain();
+ assertEquals(1D, getMetric("nodes.nonActiveFraction", metric, dimensions).doubleValue(), Double.MIN_VALUE);
+ assertEquals(0, getMetric("nodes.active", metric, dimensions));
+ assertEquals(3, getMetric("nodes.nonActive", metric, dimensions));
+ }
+
+ private Number getMetric(String name, TestMetric metric, Map<String, String> dimensions) {
+ List<TestMetric.TestContext> metrics = metric.context.get(name).stream()
+ .filter(ctx -> ctx.properties.entrySet().containsAll(dimensions.entrySet()))
+ .collect(Collectors.toList());
+ if (metrics.isEmpty()) throw new IllegalArgumentException("No value found for metric " + name + " with dimensions " + dimensions);
+ return metrics.get(metrics.size() - 1).value;
+ }
+
private ApplicationId app(String tenant) {
return new ApplicationId.Builder()
.tenant(tenant)
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/TestMetric.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/TestMetric.java
index 09fb4d59443..b20524f678c 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/TestMetric.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/TestMetric.java
@@ -10,8 +10,8 @@ import java.util.Map;
public class TestMetric implements Metric {
- public Map<String, Number> values = new LinkedHashMap<>();
- public Map<String, List<Context>> context = new LinkedHashMap<>();
+ public final Map<String, Number> values = new LinkedHashMap<>();
+ public final Map<String, List<TestContext>> context = new LinkedHashMap<>();
@Override
public void set(String key, Number val, Context ctx) {
@@ -74,9 +74,9 @@ public class TestMetric implements Metric {
/**
* Context where the propertymap is not shared - but unique to each value.
*/
- private static class TestContext implements Context{
+ static class TestContext implements Context{
Number value;
- Map<String, ?> properties;
+ final Map<String, ?> properties;
public TestContext(Map<String, ?> properties) {
this.properties = properties;
@@ -86,4 +86,5 @@ public class TestMetric implements Metric {
this.value = value;
}
}
+
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java
index fb9c1ad0e5a..8101405ad7f 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java
@@ -86,8 +86,8 @@ public class IPTest {
resolver.addReverseRecord("::2", "host1");
Optional<IP.Allocation> allocation = pool.findAllocation(emptyList, resolver);
- assertEquals("::1", allocation.get().primary());
- assertFalse(allocation.get().secondary().isPresent());
+ assertEquals(Optional.of("::1"), allocation.get().ipv6Address());
+ assertFalse(allocation.get().ipv4Address().isPresent());
assertEquals("host3", allocation.get().hostname());
// Allocation fails if DNS record is missing
@@ -105,16 +105,16 @@ public class IPTest {
var pool = testPool(false);
var allocation = pool.findAllocation(emptyList, resolver);
assertFalse("Found allocation", allocation.isEmpty());
- assertEquals("127.0.0.1", allocation.get().primary());
- assertTrue("No secondary address", allocation.get().secondary().isEmpty());
+ assertEquals(Optional.of("127.0.0.1"), allocation.get().ipv4Address());
+ assertTrue("No IPv6 address", allocation.get().ipv6Address().isEmpty());
}
@Test
public void test_find_allocation_dual_stack() {
IP.Pool pool = testPool(true);
Optional<IP.Allocation> allocation = pool.findAllocation(emptyList, resolver);
- assertEquals("::1", allocation.get().primary());
- assertEquals("127.0.0.2", allocation.get().secondary().get());
+ assertEquals(Optional.of("::1"), allocation.get().ipv6Address());
+ assertEquals("127.0.0.2", allocation.get().ipv4Address().get());
assertEquals("host3", allocation.get().hostname());
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
index 72f9e9597de..e63f31cf304 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
@@ -33,7 +33,8 @@ public class ApplicationSerializerTest {
new ClusterResources(12, 6, new NodeResources(3, 6, 21, 24)),
Optional.empty(),
Optional.empty(),
- List.of()));
+ List.of(),
+ ""));
var minResources = new NodeResources(1, 2, 3, 4);
clusters.add(new Cluster(ClusterSpec.Id.from("c2"),
true,
@@ -44,7 +45,8 @@ public class ApplicationSerializerTest {
List.of(new ScalingEvent(new ClusterResources(10, 5, minResources),
new ClusterResources(12, 6, minResources),
7L,
- Instant.ofEpochMilli(12345L)))));
+ Instant.ofEpochMilli(12345L))),
+ "Autoscaling status"));
Application original = new Application(ApplicationId.from("myTenant", "myApplication", "myInstance"),
clusters);
@@ -65,6 +67,7 @@ public class ApplicationSerializerTest {
assertEquals(originalCluster.suggestedResources(), serializedCluster.suggestedResources());
assertEquals(originalCluster.targetResources(), serializedCluster.targetResources());
assertEquals(originalCluster.scalingEvents(), serializedCluster.scalingEvents());
+ assertEquals(originalCluster.autoscalingStatus(), serializedCluster.autoscalingStatus());
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java
index d02244b7e11..9d390697df5 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java
@@ -4,7 +4,6 @@ package com.yahoo.vespa.hosted.provision.provisioning;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import org.junit.Test;
@@ -52,22 +51,22 @@ public class ContainerImagesTest {
@Test
public void image_replacement() {
var flagSource = new InMemoryFlagSource();
- var defaultImage = DockerImage.fromString("foo.example.com/vespa/vespa")
- .withReplacedBy(DockerImage.fromString("bar.example.com/vespa/vespa"));
+ var defaultImage = DockerImage.fromString("foo.example.com/vespa/vespa");
var tester = new ProvisioningTester.Builder().defaultImage(defaultImage).flagSource(flagSource).build();
var hosts = tester.makeReadyNodes(2, "default", NodeType.host);
tester.activateTenantHosts();
- // Default image is used with flag disabled
- flagSource.withBooleanFlag(Flags.REGIONAL_CONTAINER_REGISTRY.id(), false);
+ // Default image is used when there is no replacement
for (var host : hosts) {
assertEquals(defaultImage, tester.nodeRepository().containerImages().imageFor(host.type()));
}
- // Enabling flag switches to replacement
- flagSource.withBooleanFlag(Flags.REGIONAL_CONTAINER_REGISTRY.id(), true);
+ // Replacement image is preferred
+ DockerImage imageWithReplacement = defaultImage.withReplacedBy(DockerImage.fromString("bar.example.com/vespa/vespa"));
+ tester = new ProvisioningTester.Builder().defaultImage(imageWithReplacement).flagSource(flagSource).build();
+ hosts = tester.makeReadyNodes(2, "default", NodeType.host);
for (var host : hosts) {
- assertEquals(defaultImage.replacedBy().get().asString(),
+ assertEquals(imageWithReplacement.replacedBy().get().asString(),
tester.nodeRepository().containerImages().imageFor(host.type()).asString());
}
}
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 4917a59879f..919d02c435c 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
@@ -20,6 +20,7 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
+import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner.HostSharing;
@@ -471,7 +472,7 @@ public class DynamicDockerProvisionTest {
throw new OutOfCapacityException("No host flavor matches " + resources);
return provisionIndexes.stream()
.map(i -> new ProvisionedHost("id-" + i, "host-" + i, hostFlavor.get(), Optional.empty(),
- "host-" + i + "-1", resources, osVersion))
+ List.of(new Address("host-" + i + "-1")), resources, osVersion))
.collect(Collectors.toList());
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java
index c6e89680e85..808770f42dc 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java
@@ -7,6 +7,7 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.IP;
import org.junit.Before;
import org.junit.Test;
@@ -15,6 +16,8 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -32,8 +35,8 @@ public class HostCapacityTest {
private HostCapacity capacity;
private List<Node> nodes;
private Node host1, host2, host3;
- private final NodeResources resources1 = new NodeResources(1, 30, 20, 1.5);
- private final NodeResources resources2 = new NodeResources(2, 40, 40, 0.5);
+ private final NodeResources dockerResources = new NodeResources(1, 30, 20, 1.5);
+ private final NodeResources docker2Resources = new NodeResources(2, 40, 40, 0.5);
@Before
public void setup() {
@@ -48,15 +51,15 @@ public class HostCapacityTest {
host3 = Node.create("host3", IP.Config.of(Set.of("::21"), generateIPs(22, 1), List.of()), "host3", nodeFlavors.getFlavorOrThrow("host"), NodeType.host).build();
// Add two containers to host1
- var nodeA = Node.createDockerNode(Set.of("::2"), "nodeA", "host1", resources1, NodeType.tenant).build();
- var nodeB = Node.createDockerNode(Set.of("::3"), "nodeB", "host1", resources1, NodeType.tenant).build();
+ var nodeA = Node.createDockerNode(Set.of("::2"), "nodeA", "host1", dockerResources, NodeType.tenant).build();
+ var nodeB = Node.createDockerNode(Set.of("::3"), "nodeB", "host1", dockerResources, NodeType.tenant).build();
// Add two containers to host 2 (same as host 1)
- var nodeC = Node.createDockerNode(Set.of("::12"), "nodeC", "host2", resources1, NodeType.tenant).build();
- var nodeD = Node.createDockerNode(Set.of("::13"), "nodeD", "host2", resources1, NodeType.tenant).build();
+ var nodeC = Node.createDockerNode(Set.of("::12"), "nodeC", "host2", dockerResources, NodeType.tenant).build();
+ var nodeD = Node.createDockerNode(Set.of("::13"), "nodeD", "host2", dockerResources, NodeType.tenant).build();
// Add a larger container to host3
- var nodeE = Node.createDockerNode(Set.of("::22"), "nodeE", "host3", resources2, NodeType.tenant).build();
+ var nodeE = Node.createDockerNode(Set.of("::22"), "nodeE", "host3", docker2Resources, NodeType.tenant).build();
// init docker host capacity
nodes = new ArrayList<>(List.of(host1, host2, host3, nodeA, nodeB, nodeC, nodeD, nodeE));
@@ -65,19 +68,19 @@ public class HostCapacityTest {
@Test
public void hasCapacity() {
- assertTrue(capacity.hasCapacity(host1, resources1));
- assertTrue(capacity.hasCapacity(host1, resources2));
- assertTrue(capacity.hasCapacity(host2, resources1));
- assertTrue(capacity.hasCapacity(host2, resources2));
- assertFalse(capacity.hasCapacity(host3, resources1)); // No ip available
- assertFalse(capacity.hasCapacity(host3, resources2)); // No ip available
+ assertTrue(capacity.hasCapacity(host1, dockerResources));
+ assertTrue(capacity.hasCapacity(host1, docker2Resources));
+ assertTrue(capacity.hasCapacity(host2, dockerResources));
+ assertTrue(capacity.hasCapacity(host2, docker2Resources));
+ assertFalse(capacity.hasCapacity(host3, dockerResources)); // No ip available
+ assertFalse(capacity.hasCapacity(host3, docker2Resources)); // No ip available
// Add a new node to host1 to deplete the memory resource
- Node nodeF = Node.createDockerNode(Set.of("::6"), "nodeF", "host1", resources1, NodeType.tenant).build();
+ Node nodeF = Node.createDockerNode(Set.of("::6"), "nodeF", "host1", dockerResources, NodeType.tenant).build();
nodes.add(nodeF);
capacity = new HostCapacity(new LockedNodeList(nodes, () -> {}), hostResourcesCalculator);
- assertFalse(capacity.hasCapacity(host1, resources1));
- assertFalse(capacity.hasCapacity(host1, resources2));
+ assertFalse(capacity.hasCapacity(host1, dockerResources));
+ assertFalse(capacity.hasCapacity(host1, docker2Resources));
}
@Test
@@ -112,19 +115,78 @@ public class HostCapacityTest {
var nodeFlavors = FlavorConfigBuilder.createDummies("devhost", "container");
var devHost = Node.create("devhost", new IP.Config(Set.of("::1"), generateIPs(2, 10)), "devhost", nodeFlavors.getFlavorOrThrow("devhost"), NodeType.devhost).build();
- var cfg = Node.createDockerNode(Set.of("::2"), "cfg", "devhost", resources1, NodeType.config).build();
+ var cfg = Node.createDockerNode(Set.of("::2"), "cfg", "devhost", dockerResources, NodeType.config).build();
var nodes = new ArrayList<>(List.of(cfg));
var capacity = new HostCapacity(new LockedNodeList(nodes, () -> {}), hostResourcesCalculator);
- assertTrue(capacity.hasCapacity(devHost, resources1));
+ assertTrue(capacity.hasCapacity(devHost, dockerResources));
- var container1 = Node.createDockerNode(Set.of("::3"), "container1", "devhost", resources1, NodeType.tenant).build();
+ var container1 = Node.createDockerNode(Set.of("::3"), "container1", "devhost", dockerResources, NodeType.tenant).build();
nodes = new ArrayList<>(List.of(cfg, container1));
capacity = new HostCapacity(new LockedNodeList(nodes, () -> {}), hostResourcesCalculator);
- assertFalse(capacity.hasCapacity(devHost, resources1));
+ assertFalse(capacity.hasCapacity(devHost, dockerResources));
}
+ @Test
+ public void verifyCapacityFromAddresses() {
+ Node nodeA = Node.createDockerNode(Set.of("::2"), "nodeA", "host1", dockerResources, NodeType.tenant).build();
+ Node nodeB = Node.createDockerNode(Set.of("::3"), "nodeB", "host1", dockerResources, NodeType.tenant).build();
+ Node nodeC = Node.createDockerNode(Set.of("::4"), "nodeC", "host1", dockerResources, NodeType.tenant).build();
+
+ // host1 is a host with resources = 7-100-120-5 (7 vcpus, 100G memory, 120G disk, and 5Gbps),
+ // while nodeA-C have resources = dockerResources = 1-30-20-1.5
+
+ Node host1 = setupHostWithAdditionalHostnames("host1", "nodeA");
+ // Allocating nodeA should be OK
+ assertTrue(hasCapacity(dockerResources, host1));
+ // then, the second node lacks hostname address
+ assertFalse(hasCapacity(dockerResources, host1, nodeA));
+
+ host1 = setupHostWithAdditionalHostnames("host1", "nodeA", "nodeB");
+ // Allocating nodeA and nodeB should be OK
+ assertTrue(hasCapacity(dockerResources, host1));
+ assertTrue(hasCapacity(dockerResources, host1, nodeA));
+ // but the third node lacks hostname address
+ assertFalse(hasCapacity(dockerResources, host1, nodeA, nodeB));
+
+ host1 = setupHostWithAdditionalHostnames("host1", "nodeA", "nodeB", "nodeC");
+ // Allocating nodeA, nodeB, and nodeC should be OK
+ assertTrue(hasCapacity(dockerResources, host1));
+ assertTrue(hasCapacity(dockerResources, host1, nodeA));
+ assertTrue(hasCapacity(dockerResources, host1, nodeA, nodeB));
+ // but the fourth node lacks hostname address
+ assertFalse(hasCapacity(dockerResources, host1, nodeA, nodeB, nodeC));
+
+ host1 = setupHostWithAdditionalHostnames("host1", "nodeA", "nodeB", "nodeC", "nodeD");
+ // Allocating nodeA, nodeB, and nodeC should be OK
+ assertTrue(hasCapacity(dockerResources, host1));
+ assertTrue(hasCapacity(dockerResources, host1, nodeA));
+ assertTrue(hasCapacity(dockerResources, host1, nodeA, nodeB));
+ // but the fourth lacks memory (host has 100G, while 4x30G = 120G
+ assertFalse(hasCapacity(dockerResources, host1, nodeA, nodeB, nodeC));
+ }
+
+ private Node setupHostWithAdditionalHostnames(String hostHostname, String... additionalHostnames) {
+ List<Address> addresses = Stream.of(additionalHostnames).map(Address::new).collect(Collectors.toList());
+
+ doAnswer(invocation -> ((Flavor)invocation.getArguments()[0]).resources())
+ .when(hostResourcesCalculator).advertisedResourcesOf(any());
+
+ NodeFlavors nodeFlavors = FlavorConfigBuilder.createDummies(
+ "host", // 7-100-120-5
+ "docker"); // 2- 40- 40-0.5 = docker2Resources
+
+ return Node.create(hostHostname, IP.Config.of(Set.of("::1"), Set.of(), addresses), hostHostname,
+ nodeFlavors.getFlavorOrThrow("host"), NodeType.host).build();
+ }
+
+ private boolean hasCapacity(NodeResources requestedCapacity, Node host, Node... remainingNodes) {
+ List<Node> nodes = Stream.concat(Stream.of(host), Stream.of(remainingNodes)).collect(Collectors.toList());
+ var capacity = new HostCapacity(new LockedNodeList(nodes, () -> {}), hostResourcesCalculator);
+ return capacity.hasCapacity(host, requestedCapacity);
+ }
+
private Set<String> generateIPs(int start, int count) {
// Allow 4 containers
Set<String> ipAddressPool = new LinkedHashSet<>();
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 f012f0a428f..b2529963a9f 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
@@ -314,8 +314,12 @@ public class ProvisioningTester {
}
public void fail(HostSpec host) {
- int beforeFailCount = nodeRepository.getNode(host.hostname(), Node.State.active).get().status().failCount();
- Node failedNode = nodeRepository.fail(host.hostname(), Agent.system, "Failing to unit test");
+ fail(host.hostname());
+ }
+
+ public void fail(String hostname) {
+ int beforeFailCount = nodeRepository.getNode(hostname, Node.State.active).get().status().failCount();
+ Node failedNode = nodeRepository.fail(hostname, Agent.system, "Failing to unit test");
assertTrue(nodeRepository.getNodes(NodeType.tenant, Node.State.failed).contains(failedNode));
assertEquals(beforeFailCount + 1, failedNode.status().failCount());
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
index a98d383e219..86427fe30ae 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java
@@ -91,8 +91,9 @@ public class NodesV2ApiTest {
// POST new nodes
assertResponse(new Request("http://localhost:8080/nodes/v2/node",
("[" + asNodeJson("host8.yahoo.com", "default", "127.0.8.1") + "," + // test with only 1 ip address
- asHostJson("host9.yahoo.com", "large-variant", "127.0.9.1", "::9:1") + "," +
- asNodeJson("parent2.yahoo.com", NodeType.host, "large-variant", Optional.of(TenantName.from("myTenant")), Optional.of(ApplicationId.from("tenant1", "app1", "instance1")), Optional.empty(), "127.0.127.1", "::127:1") + "," +
+ asHostJson("host9.yahoo.com", "large-variant", List.of("node9-1.yahoo.com"), "127.0.9.1", "::9:1") + "," +
+ asNodeJson("parent2.yahoo.com", NodeType.host, "large-variant", Optional.of(TenantName.from("myTenant")),
+ Optional.of(ApplicationId.from("tenant1", "app1", "instance1")), Optional.empty(), List.of(), "127.0.127.1", "::127:1") + "," +
asDockerNodeJson("host11.yahoo.com", "parent.host.yahoo.com", "::11") + "]").
getBytes(StandardCharsets.UTF_8),
Request.Method.POST),
@@ -322,7 +323,7 @@ public class NodesV2ApiTest {
// Attempt to POST host node with already assigned IP
tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node",
- "[" + asHostJson("host200.yahoo.com", "default", "127.0.2.1") + "]",
+ "[" + asHostJson("host200.yahoo.com", "default", List.of(), "127.0.2.1") + "]",
Request.Method.POST), 400,
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Cannot assign [127.0.2.1] to host200.yahoo.com: [127.0.2.1] already assigned to host2.yahoo.com\"}");
@@ -334,7 +335,7 @@ public class NodesV2ApiTest {
// Node types running a single container can share their IP address with child node
tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node",
- "[" + asNodeJson("cfghost42.yahoo.com", NodeType.confighost, "default", Optional.empty(), Optional.empty(), Optional.empty(), "127.0.42.1") + "]",
+ "[" + asNodeJson("cfghost42.yahoo.com", NodeType.confighost, "default", Optional.empty(), Optional.empty(), Optional.empty(), List.of(), "127.0.42.1") + "]",
Request.Method.POST), 200,
"{\"message\":\"Added 1 nodes to the provisioned state\"}");
tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node",
@@ -350,7 +351,7 @@ public class NodesV2ApiTest {
// ... nor with child node on different host
tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node",
- "[" + asNodeJson("cfghost43.yahoo.com", NodeType.confighost, "default", Optional.empty(), Optional.empty(), Optional.empty(), "127.0.43.1") + "]",
+ "[" + asNodeJson("cfghost43.yahoo.com", NodeType.confighost, "default", Optional.empty(), Optional.empty(), Optional.empty(), List.of(), "127.0.43.1") + "]",
Request.Method.POST), 200,
"{\"message\":\"Added 1 nodes to the provisioned state\"}");
tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/cfg42.yahoo.com",
@@ -392,7 +393,7 @@ public class NodesV2ApiTest {
@Test
public void fails_to_ready_node_with_hard_fail() throws Exception {
assertResponse(new Request("http://localhost:8080/nodes/v2/node",
- ("[" + asHostJson("host12.yahoo.com", "default") + "]").
+ ("[" + asHostJson("host12.yahoo.com", "default", List.of()) + "]").
getBytes(StandardCharsets.UTF_8),
Request.Method.POST),
"{\"message\":\"Added 1 nodes to the provisioned state\"}");
@@ -961,7 +962,8 @@ public class NodesV2ApiTest {
public void test_node_switch_hostname() throws Exception {
String hostname = "host42.yahoo.com";
// Add host with switch hostname
- String json = asNodeJson(hostname, NodeType.host, "default", Optional.empty(), Optional.empty(), Optional.of("switch0"), "127.0.42.1", "::42:1");
+ String json = asNodeJson(hostname, NodeType.host, "default", Optional.empty(), Optional.empty(),
+ Optional.of("switch0"), List.of(), "127.0.42.1", "::42:1");
assertResponse(new Request("http://localhost:8080/nodes/v2/node",
("[" + json + "]").getBytes(StandardCharsets.UTF_8),
Request.Method.POST),
@@ -1013,17 +1015,22 @@ public class NodesV2ApiTest {
"\"flavor\":\"" + flavor + "\"}";
}
- private static String asHostJson(String hostname, String flavor, String... ipAddress) {
- return asNodeJson(hostname, NodeType.host, flavor, Optional.empty(), Optional.empty(), Optional.empty(), ipAddress);
+ private static String asHostJson(String hostname, String flavor, List<String> additionalHostnames, String... ipAddress) {
+ return asNodeJson(hostname, NodeType.host, flavor, Optional.empty(), Optional.empty(), Optional.empty(),
+ additionalHostnames, ipAddress);
}
- private static String asNodeJson(String hostname, NodeType nodeType, String flavor, Optional<TenantName> reservedTo, Optional<ApplicationId> exclusiveTo, Optional<String> switchHostname, String... ipAddress) {
+ private static String asNodeJson(String hostname, NodeType nodeType, String flavor, Optional<TenantName> reservedTo,
+ Optional<ApplicationId> exclusiveTo, Optional<String> switchHostname,
+ List<String> additionalHostnames, String... ipAddress) {
return "{\"hostname\":\"" + hostname + "\", \"openStackId\":\"" + hostname + "\"," +
createIpAddresses(ipAddress) +
"\"flavor\":\"" + flavor + "\"" +
(reservedTo.map(tenantName -> ", \"reservedTo\":\"" + tenantName.value() + "\"").orElse("")) +
(exclusiveTo.map(appId -> ", \"exclusiveTo\":\"" + appId.serializedForm() + "\"").orElse("")) +
(switchHostname.map(s -> ", \"switchHostname\":\"" + s + "\"").orElse("")) +
+ (additionalHostnames.isEmpty() ? "" : ", \"additionalHostnames\":[\"" +
+ String.join("\",\"", additionalHostnames) + "\"]") +
", \"type\":\"" + nodeType + "\"}";
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json
index 456ed18334e..82f7e04f92b 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json
@@ -62,7 +62,37 @@
"diskSpeed" : "fast",
"storageType" : "any"
}
- }
+ },
+ "scalingEvents" : [
+ {
+ "from": {
+ "nodes": 0,
+ "groups": 0,
+ "resources": {
+ "vcpu" : 0.0,
+ "memoryGb": 0.0,
+ "diskGb": 0.0,
+ "bandwidthGbps": 0.0,
+ "diskSpeed": "fast",
+ "storageType": "any"
+ }
+ },
+ "to": {
+ "nodes": 2,
+ "groups": 1,
+ "resources" : {
+ "vcpu": 2.0,
+ "memoryGb": 8.0,
+ "diskGb": 50.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "fast",
+ "storageType": "local"
+ }
+ },
+ "at" : 123
+ }
+ ],
+ "autoscalingStatus" : ""
}
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json
index bd22087ecfa..0ee590f60e0 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json
@@ -38,7 +38,37 @@
"diskSpeed": "fast",
"storageType": "local"
}
- }
+ },
+ "scalingEvents" : [
+ {
+ "from": {
+ "nodes": 0,
+ "groups": 0,
+ "resources": {
+ "vcpu" : 0.0,
+ "memoryGb": 0.0,
+ "diskGb": 0.0,
+ "bandwidthGbps": 0.0,
+ "diskSpeed": "fast",
+ "storageType": "any"
+ }
+ },
+ "to": {
+ "nodes": 2,
+ "groups": 1,
+ "resources" : {
+ "vcpu": 2.0,
+ "memoryGb": 8.0,
+ "diskGb": 50.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "fast",
+ "storageType": "local"
+ }
+ },
+ "at" : 123
+ }
+ ],
+ "autoscalingStatus" : ""
}
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node9.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node9.json
index dac9fd30267..809e58bd7b6 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node9.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node9.json
@@ -25,5 +25,6 @@
"127.0.9.1",
"::9:1"
],
- "additionalIpAddresses": []
+ "additionalIpAddresses": [],
+ "additionalHostnames": ["node9-1.yahoo.com"]
}
diff --git a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java
index 4b82f278f23..e2e769f8556 100644
--- a/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java
+++ b/orchestrator/src/main/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactory.java
@@ -14,6 +14,7 @@ import java.util.List;
/**
* @author bakksjo
*/
+@SuppressWarnings("removal") // VespaJerseyJaxRsClientFactory
public class RetryingClusterControllerClientFactory extends AbstractComponent implements ClusterControllerClientFactory {
// TODO: Figure this port out dynamically.
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java
index 95fdd61563b..309d6a756f6 100644
--- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java
+++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/controller/RetryingClusterControllerClientFactoryTest.java
@@ -27,6 +27,7 @@ public class RetryingClusterControllerClientFactoryTest {
private final Clock clock = new ManualClock();
@Test
+ @SuppressWarnings("removal") // VespaJerseyJaxRsClientFactory
public void verifyJerseyCallForSetNodeState() throws IOException {
VespaJerseyJaxRsClientFactory clientFactory = mock(VespaJerseyJaxRsClientFactory.class);
ClusterControllerJaxRsApi api = mock(ClusterControllerJaxRsApi.class);
diff --git a/parent/pom.xml b/parent/pom.xml
index b3648ea52df..486f659e529 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -575,6 +575,11 @@
<version>${apache.httpcore.version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ <version>${apache.httpclient5.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.6</version>
@@ -749,6 +754,7 @@
<antlr4.version>4.5</antlr4.version>
<apache.httpclient.version>4.5.12</apache.httpclient.version>
<apache.httpcore.version>4.4.13</apache.httpcore.version>
+ <apache.httpclient5.version>5.0.3</apache.httpclient5.version>
<asm.version>7.0</asm.version>
<!-- Athenz dependencies. Make sure these dependencies match those in Vespa's internal repositories -->
<athenz.version>1.8.49</athenz.version>
diff --git a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
index 6ad74b660c1..96c55d0d9f2 100644
--- a/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
+++ b/persistence/src/vespa/persistence/conformancetest/conformancetest.cpp
@@ -31,8 +31,6 @@ using PersistenceProviderUP = std::unique_ptr<PersistenceProvider>;
namespace {
-LoadType defaultLoadType(0, "default");
-
std::unique_ptr<PersistenceProvider> getSpi(ConformanceTest::PersistenceFactory &factory,
const document::TestDocMan &testDocMan) {
PersistenceProviderUP result(factory.getPersistenceImplementation(
@@ -61,7 +59,7 @@ createIterator(PersistenceProvider& spi,
fieldSet = std::make_shared<document::DocIdOnly>();
}
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
return spi.createIterator(b, std::move(fieldSet), sel, versions, context);
}
@@ -152,7 +150,7 @@ doIterate(PersistenceProvider& spi,
std::vector<Chunk> chunks;
while (true) {
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
IterateResult result(spi.iterate(id, maxByteSize, context));
EXPECT_EQ(Result::ErrorType::NONE, result.getErrorCode());
@@ -204,7 +202,7 @@ iterateBucket(PersistenceProvider& spi,
DocumentSelection docSel("");
Selection sel(docSel);
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
CreateIteratorResult iter = spi.createIterator(
bucket,
std::make_shared<document::AllFields>(),
@@ -286,7 +284,7 @@ feedDocs(PersistenceProvider& spi,
uint32_t maxSize = 110)
{
std::vector<DocAndTimestamp> docs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
for (uint32_t i = 0; i < numDocs; ++i) {
Document::SP doc(
testDocMan.createRandomDocumentAtLocation(
@@ -333,7 +331,7 @@ TEST_F(ConformanceTest, testBasics)
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
Document::SP doc2 = testDocMan.createRandomDocumentAtLocation(0x01, 2);
@@ -428,7 +426,7 @@ TEST_F(ConformanceTest, testListBuckets)
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
Document::SP doc2 = testDocMan.createRandomDocumentAtLocation(0x02, 2);
Document::SP doc3 = testDocMan.createRandomDocumentAtLocation(0x03, 3);
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
spi->createBucket(bucket1, context);
spi->createBucket(bucket2, context);
spi->createBucket(bucket3, context);
@@ -461,7 +459,7 @@ TEST_F(ConformanceTest, testBucketInfo)
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
Document::SP doc2 = testDocMan.createRandomDocumentAtLocation(0x01, 2);
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
spi->createBucket(bucket, context);
spi->put(bucket, Timestamp(2), doc2, context);
@@ -518,7 +516,7 @@ TEST_F(ConformanceTest, testOrderIndependentBucketInfo)
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
Document::SP doc2 = testDocMan.createRandomDocumentAtLocation(0x01, 2);
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
spi->createBucket(bucket, context);
BucketChecksum checksumOrdered(0);
@@ -557,7 +555,7 @@ TEST_F(ConformanceTest, testPut)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
@@ -582,7 +580,7 @@ TEST_F(ConformanceTest, testPutNewDocumentVersion)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
@@ -633,7 +631,7 @@ TEST_F(ConformanceTest, testPutOlderDocumentVersion)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
@@ -677,7 +675,7 @@ TEST_F(ConformanceTest, testPutDuplicate)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
@@ -709,7 +707,7 @@ TEST_F(ConformanceTest, testRemove)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
@@ -802,7 +800,7 @@ TEST_F(ConformanceTest, testRemoveMerge)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
@@ -897,7 +895,7 @@ TEST_F(ConformanceTest, testUpdate)
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
spi->createBucket(bucket, context);
@@ -996,7 +994,7 @@ TEST_F(ConformanceTest, testGet)
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
spi->createBucket(bucket, context);
@@ -1038,7 +1036,7 @@ TEST_F(ConformanceTest, testIterateCreateIterator)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1057,7 +1055,7 @@ TEST_F(ConformanceTest, testIterateWithUnknownId)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1071,7 +1069,7 @@ TEST_F(ConformanceTest, testIterateDestroyIterator)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1103,7 +1101,7 @@ TEST_F(ConformanceTest, testIterateAllDocs)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1121,7 +1119,7 @@ TEST_F(ConformanceTest, testIterateAllDocsNewestVersionOnly)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1149,7 +1147,7 @@ TEST_F(ConformanceTest, testIterateChunked)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1169,7 +1167,7 @@ TEST_F(ConformanceTest, testMaxByteSize)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1195,7 +1193,7 @@ TEST_F(ConformanceTest, testIterateMatchTimestampRange)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1233,7 +1231,7 @@ TEST_F(ConformanceTest, testIterateExplicitTimestampSubset)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1282,7 +1280,7 @@ TEST_F(ConformanceTest, testIterateRemoves)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1332,7 +1330,7 @@ TEST_F(ConformanceTest, testIterateMatchSelection)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1362,7 +1360,7 @@ TEST_F(ConformanceTest, testIterationRequiringDocumentIdOnlyMatching)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1392,7 +1390,7 @@ TEST_F(ConformanceTest, testIterateBadDocumentSelection)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
{
@@ -1426,7 +1424,7 @@ TEST_F(ConformanceTest, testIterateAlreadyCompleted)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
@@ -1450,7 +1448,7 @@ TEST_F(ConformanceTest, testIterateEmptyBucket)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket b(makeSpiBucket(BucketId(8, 0x1)));
spi->createBucket(b, context);
Selection sel(createSelection(""));
@@ -1470,7 +1468,7 @@ TEST_F(ConformanceTest, testDeleteBucket)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
@@ -1495,7 +1493,7 @@ testDeleteBucketPostCondition(const PersistenceProvider &spi,
const Bucket &bucket,
const Document &doc1)
{
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
{
GetResult result = spi.get(bucket,
document::AllFields(),
@@ -1513,7 +1511,7 @@ TEST_F(ConformanceTest, testSplitNormalCase)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucketA(makeSpiBucket(BucketId(3, 0x02)));
Bucket bucketB(makeSpiBucket(BucketId(3, 0x06)));
@@ -1555,7 +1553,7 @@ testSplitNormalCasePostCondition(const PersistenceProvider &spi,
EXPECT_EQ(10, (int)spi.getBucketInfo(bucketB).getBucketInfo().getDocumentCount());
document::AllFields fs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
for (uint32_t i = 0; i < 10; ++i) {
Document::UP doc1 = testDocMan.createRandomDocumentAtLocation(0x02, i);
EXPECT_TRUE(spi.get(bucketA, fs, doc1->getId(), context).hasDocument());
@@ -1576,7 +1574,7 @@ TEST_F(ConformanceTest, testSplitTargetExists)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucketA(makeSpiBucket(BucketId(3, 0x02)));
Bucket bucketB(makeSpiBucket(BucketId(3, 0x06)));
@@ -1633,7 +1631,7 @@ testSplitTargetExistsPostCondition(const PersistenceProvider &spi,
getDocumentCount());
document::AllFields fs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
for (uint32_t i = 0; i < 10; ++i) {
Document::UP doc1 = testDocMan.createRandomDocumentAtLocation(0x02, i);
EXPECT_TRUE(
@@ -1660,7 +1658,7 @@ TEST_F(ConformanceTest, testSplitSingleDocumentInSource)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket target1(makeSpiBucket(BucketId(3, 0x02)));
Bucket target2(makeSpiBucket(BucketId(3, 0x06)));
@@ -1696,7 +1694,7 @@ ConformanceTest::testSplitSingleDocumentInSourcePostCondition(
EXPECT_EQ(uint32_t(1), spi.getBucketInfo(target2).getBucketInfo().getDocumentCount());
document::AllFields fs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Document::UP doc = testDocMan.createRandomDocumentAtLocation(0x06, 0);
EXPECT_TRUE(spi.get(target2, fs, doc->getId(), context).hasDocument());
EXPECT_TRUE(!spi.get(target1, fs, doc->getId(), context).hasDocument());
@@ -1710,7 +1708,7 @@ ConformanceTest::createAndPopulateJoinSourceBuckets(
const Bucket& source2,
document::TestDocMan& testDocMan)
{
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
spi.createBucket(source1, context);
spi.createBucket(source2, context);
@@ -1741,7 +1739,7 @@ ConformanceTest::doTestJoinNormalCase(const Bucket& source1,
createAndPopulateJoinSourceBuckets(*spi, source1, source2, testDocMan);
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
spi->join(source1, source2, target, context);
testJoinNormalCasePostCondition(*spi, source1, source2, target, testDocMan);
@@ -1780,7 +1778,7 @@ testJoinNormalCasePostCondition(const PersistenceProvider &spi,
EXPECT_EQ(20, (int)spi.getBucketInfo(bucketC).getBucketInfo().getDocumentCount());
document::AllFields fs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
for (uint32_t i = 0; i < 10; ++i) {
Document::UP doc(
testDocMan.createRandomDocumentAtLocation(
@@ -1802,7 +1800,7 @@ TEST_F(ConformanceTest, testJoinTargetExists)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucketA(makeSpiBucket(BucketId(3, 0x02)));
spi->createBucket(bucketA, context);
@@ -1851,7 +1849,7 @@ testJoinTargetExistsPostCondition(const PersistenceProvider &spi,
EXPECT_EQ(30, (int)spi.getBucketInfo(bucketC).getBucketInfo().getDocumentCount());
document::AllFields fs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
for (uint32_t i = 0; i < 10; ++i) {
Document::UP doc1 = testDocMan.createRandomDocumentAtLocation(0x02, i);
EXPECT_TRUE(spi.get(bucketC, fs, doc1->getId(), context).hasDocument());
@@ -1891,7 +1889,7 @@ TEST_F(ConformanceTest, testJoinOneBucket)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucketA(makeSpiBucket(BucketId(3, 0x02)));
spi->createBucket(bucketA, context);
@@ -1921,7 +1919,7 @@ testJoinOneBucketPostCondition(const PersistenceProvider &spi,
EXPECT_EQ(10, (int)spi.getBucketInfo(bucketC).getBucketInfo().getDocumentCount());
document::AllFields fs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
for (uint32_t i = 0; i < 10; ++i) {
Document::UP doc1 = testDocMan.createRandomDocumentAtLocation(0x02, i);
EXPECT_TRUE(spi.get(bucketC, fs, doc1->getId(), context).hasDocument());
@@ -1947,7 +1945,7 @@ ConformanceTest::doTestJoinSameSourceBuckets(const Bucket& source, const Bucket&
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
spi->createBucket(source, context);
populateBucket(source, *spi, context, 0, 10, testDocMan);
@@ -1986,7 +1984,7 @@ ConformanceTest::testJoinSameSourceBucketsTargetExistsPostCondition(
EXPECT_EQ(20, (int)spi.getBucketInfo(target).getBucketInfo().getDocumentCount());
document::AllFields fs;
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
for (uint32_t i = 0; i < 20; ++i) {
Document::UP doc1 = testDocMan.createRandomDocumentAtLocation(0x02, i);
EXPECT_TRUE(spi.get(target, fs, doc1->getId(), context).hasDocument());
@@ -1999,7 +1997,7 @@ TEST_F(ConformanceTest, testJoinSameSourceBucketsTargetExists)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket source(makeSpiBucket(BucketId(3, 0x02)));
spi->createBucket(source, context);
@@ -2038,7 +2036,7 @@ TEST_F(ConformanceTest, testBucketActivation)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
spi->setClusterState(makeBucketSpace(), createClusterState());
@@ -2081,7 +2079,7 @@ TEST_F(SingleDocTypeConformanceTest, testBucketActivationSplitAndJoin)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucketA(makeSpiBucket(BucketId(3, 0x02)));
Bucket bucketB(makeSpiBucket(BucketId(3, 0x06)));
@@ -2159,7 +2157,7 @@ TEST_F(ConformanceTest, testRemoveEntry)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
Bucket bucket(makeSpiBucket(BucketId(8, 0x01)));
Document::SP doc1 = testDocMan.createRandomDocumentAtLocation(0x01, 1);
@@ -2227,7 +2225,7 @@ TEST_F(ConformanceTest, testBucketSpaces)
document::TestDocMan testDocMan;
_factory->clear();
PersistenceProviderUP spi(getSpi(*_factory, testDocMan));
- Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0));
+ Context context(Priority(0), Trace::TraceLevel(0));
BucketSpace bucketSpace0(makeBucketSpace("testdoctype1"));
BucketSpace bucketSpace1(makeBucketSpace("testdoctype2"));
BucketSpace bucketSpace2(makeBucketSpace("no"));
diff --git a/persistence/src/vespa/persistence/spi/context.cpp b/persistence/src/vespa/persistence/spi/context.cpp
index 429e2fb9d4e..80f5f05c59b 100644
--- a/persistence/src/vespa/persistence/spi/context.cpp
+++ b/persistence/src/vespa/persistence/spi/context.cpp
@@ -4,9 +4,8 @@
namespace storage::spi {
-Context::Context(const LoadType& loadType, Priority pri, int maxTraceLevel)
- : _loadType(&loadType),
- _priority(pri),
+Context::Context(Priority pri, int maxTraceLevel)
+ : _priority(pri),
_trace(maxTraceLevel),
_readConsistency(ReadConsistency::STRONG)
{ }
diff --git a/persistence/src/vespa/persistence/spi/context.h b/persistence/src/vespa/persistence/spi/context.h
index 8c31439ee75..1fd7805b471 100644
--- a/persistence/src/vespa/persistence/spi/context.h
+++ b/persistence/src/vespa/persistence/spi/context.h
@@ -32,12 +32,8 @@
#include "read_consistency.h"
#include <vespa/vespalib/trace/trace.h>
-namespace metrics { class LoadType; }
-
namespace storage::spi {
-using LoadType = metrics::LoadType;
-
typedef uint16_t Priority; // 0 - max pri, 255 - min pri
// Define this type just because a ton of tests currently use it.
@@ -46,18 +42,15 @@ struct Trace {
};
class Context {
- const LoadType* _loadType;
Priority _priority;
vespalib::Trace _trace;
ReadConsistency _readConsistency;
-
public:
Context(Context &&) = default;
Context & operator = (Context &&) = default;
- Context(const LoadType& loadType, Priority pri, int maxTraceLevel);
+ Context(Priority pri, int maxTraceLevel);
~Context();
- const LoadType& getLoadType() const { return *_loadType; }
Priority getPriority() const { return _priority; }
/**
@@ -76,12 +69,14 @@ public:
return _readConsistency;
}
+ vespalib::Trace && steal_trace() { return std::move(_trace); }
vespalib::Trace& getTrace() { return _trace; }
const vespalib::Trace& getTrace() const { return _trace; }
bool shouldTrace(int level) { return _trace.shouldTrace(level); }
- void trace(int level, vespalib::stringref msg, bool addTime = true)
- { _trace.trace(level, msg, addTime); }
+ void trace(int level, vespalib::stringref msg, bool addTime = true) {
+ _trace.trace(level, msg, addTime);
+ }
};
}
diff --git a/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.cpp b/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.cpp
index e6502d14347..714add169fd 100644
--- a/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.cpp
+++ b/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.cpp
@@ -23,7 +23,8 @@ DocumentApiMessageBusBmFeedHandler::DocumentApiMessageBusBmFeedHandler(BmMessage
: IBmFeedHandler(),
_name(vespalib::string("DocumentApiMessageBusBmFeedHandler(distributor)")),
_storage_address(std::make_unique<StorageMessageAddress>("storage", NodeType::DISTRIBUTOR, 0)),
- _message_bus(message_bus)
+ _message_bus(message_bus),
+ _route(_storage_address->to_mbus_route())
{
}
@@ -32,7 +33,7 @@ DocumentApiMessageBusBmFeedHandler::~DocumentApiMessageBusBmFeedHandler() = defa
void
DocumentApiMessageBusBmFeedHandler::send_msg(std::unique_ptr<documentapi::DocumentMessage> msg, PendingTracker& pending_tracker)
{
- _message_bus.send_msg(std::move(msg), _storage_address->getRoute(), pending_tracker);
+ _message_bus.send_msg(std::move(msg), _route, pending_tracker);
}
void
diff --git a/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.h b/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.h
index 42bc61e587e..52e0b89007f 100644
--- a/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.h
+++ b/searchcore/src/apps/vespa-feed-bm/document_api_message_bus_bm_feed_handler.h
@@ -3,6 +3,7 @@
#pragma once
#include "i_bm_feed_handler.h"
+#include <vespa/messagebus/routing/route.h>
namespace document { class DocumentTypeRepo; }
namespace documentapi { class DocumentMessage; };
@@ -21,6 +22,7 @@ class DocumentApiMessageBusBmFeedHandler : public IBmFeedHandler
vespalib::string _name;
std::unique_ptr<storage::api::StorageMessageAddress> _storage_address;
BmMessageBus& _message_bus;
+ mbus::Route _route;
void send_msg(std::unique_ptr<documentapi::DocumentMessage> msg, PendingTracker& tracker);
public:
DocumentApiMessageBusBmFeedHandler(BmMessageBus &message_bus);
diff --git a/searchcore/src/apps/vespa-feed-bm/spi_bm_feed_handler.cpp b/searchcore/src/apps/vespa-feed-bm/spi_bm_feed_handler.cpp
index 24439a10925..daebc8a7a47 100644
--- a/searchcore/src/apps/vespa-feed-bm/spi_bm_feed_handler.cpp
+++ b/searchcore/src/apps/vespa-feed-bm/spi_bm_feed_handler.cpp
@@ -6,7 +6,6 @@
#include <vespa/document/fieldset/fieldsetrepo.h>
#include <vespa/document/fieldvalue/document.h>
#include <vespa/document/update/documentupdate.h>
-#include <vespa/metrics/loadtype.h>
#include <vespa/persistence/spi/persistenceprovider.h>
using document::Document;
@@ -20,8 +19,7 @@ namespace feedbm {
namespace {
-storage::spi::LoadType default_load_type(0, "default");
-storage::spi::Context context(default_load_type, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+storage::spi::Context context(storage::spi::Priority(0), 0);
void get_bucket_info_loop(PendingTracker &tracker)
{
@@ -38,7 +36,7 @@ class MyOperationComplete : public storage::spi::OperationComplete
PendingTracker& _tracker;
public:
MyOperationComplete(std::atomic<uint32_t> &errors, const Bucket& bucket, PendingTracker& tracker);
- ~MyOperationComplete();
+ ~MyOperationComplete() override;
void onComplete(std::unique_ptr<storage::spi::Result> result) override;
void addResultHandler(const storage::spi::ResultHandler* resultHandler) override;
};
diff --git a/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.cpp b/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.cpp
index 731b90888ea..18c1b979895 100644
--- a/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.cpp
+++ b/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.cpp
@@ -22,7 +22,8 @@ StorageApiMessageBusBmFeedHandler::StorageApiMessageBusBmFeedHandler(BmMessageBu
_name(vespalib::string("StorageApiMessageBusBmFeedHandler(") + (distributor ? "distributor" : "service-layer") + ")"),
_distributor(distributor),
_storage_address(std::make_unique<StorageMessageAddress>("storage", distributor ? NodeType::DISTRIBUTOR : NodeType::STORAGE, 0)),
- _message_bus(message_bus)
+ _message_bus(message_bus),
+ _route(_storage_address->to_mbus_route())
{
}
@@ -33,7 +34,7 @@ StorageApiMessageBusBmFeedHandler::send_msg(std::shared_ptr<storage::api::Storag
{
cmd->setSourceIndex(0);
auto msg = std::make_unique<storage::mbusprot::StorageCommand>(cmd);
- _message_bus.send_msg(std::move(msg), _storage_address->getRoute(), pending_tracker);
+ _message_bus.send_msg(std::move(msg), _route, pending_tracker);
}
void
diff --git a/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.h b/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.h
index 82447e1e873..6925053ad43 100644
--- a/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.h
+++ b/searchcore/src/apps/vespa-feed-bm/storage_api_message_bus_bm_feed_handler.h
@@ -3,6 +3,7 @@
#pragma once
#include "i_bm_feed_handler.h"
+#include <vespa/messagebus/routing/route.h>
namespace document { class DocumentTypeRepo; }
namespace documentapi { class DocumentMessage; };
@@ -25,6 +26,7 @@ class StorageApiMessageBusBmFeedHandler : public IBmFeedHandler
bool _distributor;
std::unique_ptr<storage::api::StorageMessageAddress> _storage_address;
BmMessageBus& _message_bus;
+ mbus::Route _route;
void send_msg(std::shared_ptr<storage::api::StorageCommand> cmd, PendingTracker& tracker);
public:
StorageApiMessageBusBmFeedHandler(BmMessageBus &message_bus, bool distributor);
diff --git a/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp b/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
index b875ab8e058..2b2b8acbc50 100644
--- a/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/feedview/feedview_test.cpp
@@ -179,23 +179,20 @@ public:
{
}
- void notifyPutDone(IDestructorCallbackSP, document::GlobalId gid, uint32_t lid, SerialNum) override {
+ void notifyPut(IDestructorCallbackSP, document::GlobalId gid, uint32_t lid, SerialNum) override {
_changeGid = gid;
_changeLid = lid;
_gidToLid[gid] = lid;
++_changes;
}
- void notifyRemove(IDestructorCallbackSP, document::GlobalId gid, SerialNum) override {
+ void notifyRemove(IDestructorCallbackSP, document::GlobalId gid, SerialNum) override {
_changeGid = gid;
_changeLid = 0;
_gidToLid[gid] = 0;
++_changes;
}
- void notifyRemoveDone(document::GlobalId, SerialNum) override {
- }
-
void assertChanges(document::GlobalId expGid, uint32_t expLid, uint32_t expChanges) {
EXPECT_EQUAL(expGid, _changeGid);
EXPECT_EQUAL(expLid, _changeLid);
diff --git a/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp b/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
index 2b7bacfaec0..95a41a255ce 100644
--- a/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
+++ b/searchcore/src/tests/proton/persistenceengine/persistenceengine_test.cpp
@@ -453,8 +453,7 @@ TEST_F("require that getPartitionStates() prepares all handlers", SimpleFixture)
TEST_F("require that puts are routed to handler", SimpleFixture)
{
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
f.engine.put(bucket1, tstamp1, doc1, context);
TEST_DO(assertHandler(bucket1, tstamp1, docId1, f.hset.handler1));
TEST_DO(assertHandler(bucket0, tstamp0, docId0, f.hset.handler2));
@@ -473,8 +472,7 @@ TEST_F("require that put is rejected if resource limit is reached", SimpleFixtur
f._writeFilter._acceptWriteOperation = false;
f._writeFilter._message = "Disk is full";
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
EXPECT_EQUAL(
Result(Result::ErrorType::RESOURCE_EXHAUSTED,
"Put operation rejected for document 'id:type3:type3::1': 'Disk is full'"),
@@ -484,8 +482,7 @@ TEST_F("require that put is rejected if resource limit is reached", SimpleFixtur
TEST_F("require that updates are routed to handler", SimpleFixture)
{
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
f.hset.handler1.setExistingTimestamp(tstamp2);
UpdateResult ur = f.engine.update(bucket1, tstamp1, upd1, context);
TEST_DO(assertHandler(bucket1, tstamp1, docId1, f.hset.handler1));
@@ -504,8 +501,7 @@ TEST_F("require that updates are routed to handler", SimpleFixture)
TEST_F("require that updates with bad ids are rejected", SimpleFixture)
{
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
EXPECT_EQUAL(UpdateResult(Result::ErrorType::PERMANENT_ERROR, "Update operation rejected due to bad id (id:type2:type2::1, type1)"),
f.engine.update(bucket1, tstamp1, bad_id_upd, context));
@@ -516,8 +512,7 @@ TEST_F("require that update is rejected if resource limit is reached", SimpleFix
f._writeFilter._acceptWriteOperation = false;
f._writeFilter._message = "Disk is full";
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
EXPECT_EQUAL(
Result(Result::ErrorType::RESOURCE_EXHAUSTED,
@@ -528,8 +523,7 @@ TEST_F("require that update is rejected if resource limit is reached", SimpleFix
TEST_F("require that removes are routed to handlers", SimpleFixture)
{
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
RemoveResult rr = f.engine.remove(bucket1, tstamp1, docId3, context);
TEST_DO(assertHandler(bucket0, tstamp0, docId0, f.hset.handler1));
TEST_DO(assertHandler(bucket0, tstamp0, docId0, f.hset.handler2));
@@ -565,8 +559,7 @@ TEST_F("require that remove is NOT rejected if resource limit is reached", Simpl
f._writeFilter._acceptWriteOperation = false;
f._writeFilter._message = "Disk is full";
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
EXPECT_EQUAL(RemoveResult(false), f.engine.remove(bucket1, tstamp1, docId1, context));
}
@@ -618,8 +611,7 @@ TEST_F("require that getBucketInfo() is routed to handlers and merged", SimpleFi
TEST_F("require that createBucket() is routed to handlers and merged", SimpleFixture)
{
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
f.hset.handler1._createBucketResult = Result(Result::ErrorType::TRANSIENT_ERROR, "err1a");
f.hset.handler2._createBucketResult = Result(Result::ErrorType::PERMANENT_ERROR, "err2a");
@@ -631,8 +623,7 @@ TEST_F("require that createBucket() is routed to handlers and merged", SimpleFix
TEST_F("require that deleteBucket() is routed to handlers and merged", SimpleFixture)
{
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
f.hset.handler1.deleteBucketResult = Result(Result::ErrorType::TRANSIENT_ERROR, "err1");
f.hset.handler2.deleteBucketResult = Result(Result::ErrorType::PERMANENT_ERROR, "err2");
@@ -650,8 +641,7 @@ TEST_F("require that getModifiedBuckets() is routed to handlers and merged", Sim
TEST_F("require that get is sent to all handlers", SimpleFixture) {
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
GetResult result = f.engine.get(bucket1, document::AllFields(), docId1, context);
EXPECT_EQUAL(docId1, f.hset.handler1.lastDocId);
@@ -661,8 +651,7 @@ TEST_F("require that get is sent to all handlers", SimpleFixture) {
TEST_F("require that get freezes the bucket", SimpleFixture) {
EXPECT_FALSE(f.hset.handler1.wasFrozen(bucket1));
EXPECT_FALSE(f.hset.handler2.wasFrozen(bucket1));
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
f.engine.get(bucket1, document::AllFields(), docId1, context);
EXPECT_TRUE(f.hset.handler1.wasFrozen(bucket1));
EXPECT_TRUE(f.hset.handler2.wasFrozen(bucket1));
@@ -673,8 +662,7 @@ TEST_F("require that get freezes the bucket", SimpleFixture) {
TEST_F("require that get returns the first document found", SimpleFixture) {
f.hset.handler1.setDocument(*doc1, tstamp1);
f.hset.handler2.setDocument(*doc2, tstamp2);
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
GetResult result = f.engine.get(bucket1, document::AllFields(), docId1, context);
EXPECT_EQUAL(docId1, f.hset.handler1.lastDocId);
@@ -687,8 +675,7 @@ TEST_F("require that get returns the first document found", SimpleFixture) {
}
TEST_F("require that createIterator does", SimpleFixture) {
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
CreateIteratorResult result =
f.engine.createIterator(bucket1, std::make_shared<document::AllFields>(), selection,
storage::spi::NEWEST_DOCUMENT_ONLY, context);
@@ -701,8 +688,7 @@ TEST_F("require that createIterator does", SimpleFixture) {
}
TEST_F("require that iterator ids are unique", SimpleFixture) {
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
CreateIteratorResult result =
f.engine.createIterator(bucket1, std::make_shared<document::AllFields>(), selection,
storage::spi::NEWEST_DOCUMENT_ONLY, context);
@@ -716,8 +702,7 @@ TEST_F("require that iterator ids are unique", SimpleFixture) {
TEST_F("require that iterate requires valid iterator", SimpleFixture) {
uint64_t max_size = 1024;
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
IterateResult it_result = f.engine.iterate(IteratorId(1), max_size, context);
EXPECT_TRUE(it_result.hasError());
EXPECT_EQUAL(Result::ErrorType::PERMANENT_ERROR, it_result.getErrorCode());
@@ -736,8 +721,7 @@ TEST_F("require that iterate returns documents", SimpleFixture) {
f.hset.handler1.setDocument(*doc1, tstamp1);
f.hset.handler2.setDocument(*doc2, tstamp2);
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
uint64_t max_size = 1024;
CreateIteratorResult result =
f.engine.createIterator(bucket1, std::make_shared<document::AllFields>(), selection,
@@ -752,8 +736,7 @@ TEST_F("require that iterate returns documents", SimpleFixture) {
TEST_F("require that destroyIterator prevents iteration", SimpleFixture) {
f.hset.handler1.setDocument(*doc1, tstamp1);
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
CreateIteratorResult create_result =
f.engine.createIterator(bucket1, std::make_shared<document::AllFields>(), selection,
storage::spi::NEWEST_DOCUMENT_ONLY, context);
@@ -773,8 +756,7 @@ TEST_F("require that destroyIterator prevents iteration", SimpleFixture) {
TEST_F("require that buckets are frozen during iterator life", SimpleFixture) {
EXPECT_FALSE(f.hset.handler1.isFrozen(bucket1));
EXPECT_FALSE(f.hset.handler2.isFrozen(bucket1));
- storage::spi::LoadType loadType(0, "default");
- Context context(loadType, storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0), storage::spi::Trace::TraceLevel(0));
CreateIteratorResult create_result =
f.engine.createIterator(bucket1, std::make_shared<document::AllFields>(), selection,
storage::spi::NEWEST_DOCUMENT_ONLY, context);
diff --git a/searchcore/src/tests/proton/reference/gid_to_lid_change_handler/gid_to_lid_change_handler_test.cpp b/searchcore/src/tests/proton/reference/gid_to_lid_change_handler/gid_to_lid_change_handler_test.cpp
index a10d48ee7fe..9d72045c918 100644
--- a/searchcore/src/tests/proton/reference/gid_to_lid_change_handler/gid_to_lid_change_handler_test.cpp
+++ b/searchcore/src/tests/proton/reference/gid_to_lid_change_handler/gid_to_lid_change_handler_test.cpp
@@ -6,6 +6,7 @@
#include <vespa/searchcore/proton/server/executor_thread_service.h>
#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/searchcore/proton/reference/i_gid_to_lid_change_listener.h>
+#include <vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h>
#include <vespa/searchcore/proton/reference/gid_to_lid_change_handler.h>
#include <vespa/searchlib/common/gatecallback.h>
#include <map>
@@ -112,11 +113,13 @@ public:
struct Fixture
{
std::vector<std::shared_ptr<ListenerStats>> _statss;
- std::shared_ptr<GidToLidChangeHandler> _handler;
+ std::shared_ptr<GidToLidChangeHandler> _real_handler;
+ std::shared_ptr<IGidToLidChangeHandler> _handler;
Fixture()
: _statss(),
- _handler(std::make_shared<GidToLidChangeHandler>())
+ _real_handler(std::make_shared<GidToLidChangeHandler>()),
+ _handler(_real_handler)
{
}
@@ -127,7 +130,7 @@ struct Fixture
void close()
{
- _handler->close();
+ _real_handler->close();
}
ListenerStats &addStats() {
@@ -139,10 +142,15 @@ struct Fixture
_handler->addListener(std::move(listener));
}
- void notifyPutDone(GlobalId gid, uint32_t lid, SerialNum serialNum) {
- vespalib::Gate gate;
- _handler->notifyPutDone(std::make_shared<search::GateCallback>(gate), gid, lid, serialNum);
- gate.await();
+ void commit() {
+ auto pending = _handler->grab_pending_changes();
+ if (pending) {
+ pending->notify_done();
+ }
+ }
+
+ void notifyPut(GlobalId gid, uint32_t lid, SerialNum serial_num) {
+ _handler->notifyPut(std::shared_ptr<search::IDestructorCallback>(), gid, lid, serial_num);
}
void notifyRemove(GlobalId gid, SerialNum serialNum) {
@@ -151,10 +159,6 @@ struct Fixture
gate.await();
}
- void notifyRemoveDone(GlobalId gid, SerialNum serialNum) {
- _handler->notifyRemoveDone(gid, serialNum);
- }
-
void removeListeners(const vespalib::string &docTypeName,
const std::set<vespalib::string> &keepNames) {
_handler->removeListeners(docTypeName, keepNames);
@@ -169,7 +173,8 @@ TEST_F("Test that we can register a listener", Fixture)
TEST_DO(stats.assertListeners(1, 0, 0));
f.addListener(std::move(listener));
TEST_DO(stats.assertListeners(1, 1, 0));
- f.notifyPutDone(toGid(doc1), 10, 10);
+ f.notifyPut(toGid(doc1), 10, 10);
+ f.commit();
TEST_DO(stats.assertChanges(1, 0));
f.removeListeners("testdoc", {});
TEST_DO(stats.assertListeners(1, 1, 1));
@@ -192,7 +197,8 @@ TEST_F("Test that we can register multiple listeners", Fixture)
TEST_DO(stats1.assertListeners(1, 1, 0));
TEST_DO(stats2.assertListeners(1, 1, 0));
TEST_DO(stats3.assertListeners(1, 1, 0));
- f.notifyPutDone(toGid(doc1), 10, 10);
+ f.notifyPut(toGid(doc1), 10, 10);
+ f.commit();
TEST_DO(stats1.assertChanges(1, 0));
TEST_DO(stats2.assertChanges(1, 0));
TEST_DO(stats3.assertChanges(1, 0));
@@ -250,62 +256,39 @@ public:
}
};
-TEST_F("Test that put is ignored if we have a pending remove", StatsFixture)
+TEST_F("Test that multiple puts are processed", StatsFixture)
{
- f.notifyRemove(toGid(doc1), 20);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyPutDone(toGid(doc1), 10, 10);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyRemoveDone(toGid(doc1), 20);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyPutDone(toGid(doc1), 11, 30);
- TEST_DO(f.assertChanges(1, 1));
+ f.notifyPut(toGid(doc1), 10, 10);
+ TEST_DO(f.assertChanges(0, 0));
+ f.notifyPut(toGid(doc1), 11, 20);
+ TEST_DO(f.assertChanges(0, 0));
+ f.commit();
+ TEST_DO(f.assertChanges(2, 0));
}
-TEST_F("Test that pending removes are merged", StatsFixture)
+TEST_F("Test that put is ignored if we have a pending remove", StatsFixture)
{
+ f.notifyPut(toGid(doc1), 10, 10);
+ TEST_DO(f.assertChanges(0, 0));
f.notifyRemove(toGid(doc1), 20);
TEST_DO(f.assertChanges(0, 1));
- f.notifyRemove(toGid(doc1), 40);
+ f.commit();
TEST_DO(f.assertChanges(0, 1));
- f.notifyPutDone(toGid(doc1), 10, 10);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyRemoveDone(toGid(doc1), 20);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyPutDone(toGid(doc1), 11, 30);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyRemoveDone(toGid(doc1), 40);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyPutDone(toGid(doc1), 12, 50);
+ f.notifyPut(toGid(doc1), 11, 30);
+ f.commit();
TEST_DO(f.assertChanges(1, 1));
}
-TEST_F("Test that out of order notifyRemoveDone is handled", StatsFixture)
+TEST_F("Test that pending removes are merged", StatsFixture)
{
- f.notifyRemove(toGid(doc1), 20);
+ f.notifyPut(toGid(doc1), 10, 10);
+ TEST_DO(f.assertChanges(0, 0));
+ f.notifyRemove(toGid(doc1), 20);
TEST_DO(f.assertChanges(0, 1));
f.notifyRemove(toGid(doc1), 40);
TEST_DO(f.assertChanges(0, 1));
- f.notifyRemoveDone(toGid(doc1), 40);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyRemoveDone(toGid(doc1), 20);
+ f.commit();
TEST_DO(f.assertChanges(0, 1));
- f.notifyPutDone(toGid(doc1), 12, 50);
- TEST_DO(f.assertChanges(1, 1));
-}
-
-TEST_F("Test that out of order notifyPutDone is partially handled", StatsFixture)
-{
- f.notifyRemove(toGid(doc1), 20);
- TEST_DO(f.assertChanges(0, 1));
- f.notifyPutDone(toGid(doc1), 12, 50);
- TEST_DO(f.assertChanges(1, 1));
- f.notifyPutDone(toGid(doc1), 11, 40);
- TEST_DO(f.assertChanges(1, 1));
- f.notifyPutDone(toGid(doc1), 13, 55);
- TEST_DO(f.assertChanges(2, 1));
- f.notifyRemoveDone(toGid(doc1), 20);
- TEST_DO(f.assertChanges(2, 1));
}
}
diff --git a/searchcore/src/vespa/searchcore/config/proton.def b/searchcore/src/vespa/searchcore/config/proton.def
index 5ace4b66803..08bb68b1b40 100644
--- a/searchcore/src/vespa/searchcore/config/proton.def
+++ b/searchcore/src/vespa/searchcore/config/proton.def
@@ -499,4 +499,4 @@ bucketdb.checksumtype enum {LEGACY, XXHASH64} default = LEGACY restart
## TENSOR_ENGINE (default) uses DefaultTensorEngine, which has been the production implementation for years.
## FAST_VALUE uses the new and optimized FastValueBuilderFactory instead.
## TODO: Remove when default has been switched to FAST_VALUE.
-tensor_implementation enum {TENSOR_ENGINE, FAST_VALUE} default = TENSOR_ENGINE
+tensor_implementation enum {TENSOR_ENGINE, FAST_VALUE} default = FAST_VALUE
diff --git a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
index 0eec650cfdb..02efd1ef11f 100644
--- a/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
+++ b/searchcore/src/vespa/searchcore/proton/persistenceengine/persistenceengine.cpp
@@ -611,9 +611,7 @@ PersistenceEngine::join(const Bucket& source1, const Bucket& source2, const Buck
void
PersistenceEngine::destroyIterators()
{
- Context context(storage::spi::LoadType(0, "default"),
- storage::spi::Priority(0x80),
- storage::spi::Trace::TraceLevel(0));
+ Context context(storage::spi::Priority(0x80), 0);
for (;;) {
IteratorId id;
{
diff --git a/searchcore/src/vespa/searchcore/proton/reference/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/reference/CMakeLists.txt
index a98b095cc21..1ba96e9adf5 100644
--- a/searchcore/src/vespa/searchcore/proton/reference/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/reference/CMakeLists.txt
@@ -10,7 +10,7 @@ vespa_add_library(searchcore_reference STATIC
gid_to_lid_change_registrator.cpp
gid_to_lid_mapper.cpp
gid_to_lid_mapper_factory.cpp
- pending_notify_remove_done.cpp
+ pending_gid_to_lid_changes.cpp
DEPENDS
searchcore_attribute
searchcore_documentmetastore
diff --git a/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.cpp b/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.cpp
index 6c45096a53f..a579a11e61f 100644
--- a/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "dummy_gid_to_lid_change_handler.h"
+#include "i_pending_gid_to_lid_changes.h"
namespace proton {
@@ -9,7 +10,7 @@ DummyGidToLidChangeHandler::DummyGidToLidChangeHandler() = default;
DummyGidToLidChangeHandler::~DummyGidToLidChangeHandler() = default;
void
-DummyGidToLidChangeHandler::notifyPutDone(IDestructorCallbackSP , GlobalId, uint32_t, SerialNum)
+DummyGidToLidChangeHandler::notifyPut(IDestructorCallbackSP, GlobalId, uint32_t, SerialNum)
{
}
@@ -18,9 +19,10 @@ DummyGidToLidChangeHandler::notifyRemove(IDestructorCallbackSP , GlobalId, Seria
{
}
-void
-DummyGidToLidChangeHandler::notifyRemoveDone(GlobalId, SerialNum)
+std::unique_ptr<IPendingGidToLidChanges>
+DummyGidToLidChangeHandler::grab_pending_changes()
{
+ return {};
}
void
diff --git a/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.h b/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.h
index d5f6d788885..54cc0e2144a 100644
--- a/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.h
+++ b/searchcore/src/vespa/searchcore/proton/reference/dummy_gid_to_lid_change_handler.h
@@ -22,11 +22,11 @@ public:
DummyGidToLidChangeHandler();
~DummyGidToLidChangeHandler() override;
- void notifyPutDone(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serialNum) override;
+ void notifyPut(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serial_num) override;
void notifyRemove(IDestructorCallbackSP context, GlobalId gid, SerialNum serialNum) override;
- void notifyRemoveDone(GlobalId gid, SerialNum serialNum) override;
void addListener(std::unique_ptr<IGidToLidChangeListener> listener) override;
void removeListeners(const vespalib::string &docTypeName, const std::set<vespalib::string> &keepNames) override;
+ std::unique_ptr<IPendingGidToLidChanges> grab_pending_changes() override;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.cpp b/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.cpp
index 0c9087405b6..805f19b5bf0 100644
--- a/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.cpp
+++ b/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.cpp
@@ -2,6 +2,7 @@
#include "gid_to_lid_change_handler.h"
#include "i_gid_to_lid_change_listener.h"
+#include "pending_gid_to_lid_changes.h"
#include <vespa/vespalib/util/lambdatask.h>
#include <cassert>
#include <vespa/vespalib/stllike/hash_map.hpp>
@@ -14,8 +15,8 @@ GidToLidChangeHandler::GidToLidChangeHandler()
: _lock(),
_listeners(),
_closed(false),
- _pendingRemove()
-
+ _pendingRemove(),
+ _pending_changes()
{
}
@@ -43,6 +44,13 @@ GidToLidChangeHandler::notifyRemove(IDestructorCallbackSP context, GlobalId gid)
}
void
+GidToLidChangeHandler::notifyPut(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serial_num)
+{
+ lock_guard guard(_lock);
+ _pending_changes.emplace_back(std::move(context), gid, lid, serial_num, false);
+}
+
+void
GidToLidChangeHandler::notifyPutDone(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serialNum)
{
lock_guard guard(_lock);
@@ -79,6 +87,7 @@ GidToLidChangeHandler::notifyRemove(IDestructorCallbackSP context, GlobalId gid,
} else {
notifyRemove(std::move(context), gid);
}
+ _pending_changes.emplace_back(IDestructorCallbackSP(), gid, 0, serialNum, true);
}
void
@@ -96,6 +105,16 @@ GidToLidChangeHandler::notifyRemoveDone(GlobalId gid, SerialNum serialNum)
}
}
+std::unique_ptr<IPendingGidToLidChanges>
+GidToLidChangeHandler::grab_pending_changes()
+{
+ lock_guard guard(_lock);
+ if (_pending_changes.empty()) {
+ return {};
+ }
+ return std::make_unique<PendingGidToLidChanges>(*this, std::move(_pending_changes));
+}
+
void
GidToLidChangeHandler::close()
{
diff --git a/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.h b/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.h
index 13a51edd0b5..25645aebcc9 100644
--- a/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.h
+++ b/searchcore/src/vespa/searchcore/proton/reference/gid_to_lid_change_handler.h
@@ -3,8 +3,8 @@
#pragma once
#include "i_gid_to_lid_change_handler.h"
+#include "pending_gid_to_lid_change.h"
#include <vespa/vespalib/stllike/hash_map.h>
-#include <vespa/document/base/globalid.h>
#include <vector>
#include <mutex>
@@ -44,6 +44,7 @@ class GidToLidChangeHandler : public std::enable_shared_from_this<GidToLidChange
Listeners _listeners;
bool _closed;
vespalib::hash_map<GlobalId, PendingRemoveEntry, GlobalId::hash> _pendingRemove;
+ std::vector<PendingGidToLidChange> _pending_changes;
void notifyPutDone(IDestructorCallbackSP context, GlobalId gid, uint32_t lid);
void notifyRemove(IDestructorCallbackSP context, GlobalId gid);
@@ -51,9 +52,11 @@ public:
GidToLidChangeHandler();
~GidToLidChangeHandler() override;
- void notifyPutDone(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serialNum) override;
+ void notifyPut(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serial_num) override;
+ void notifyPutDone(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serialNum);
void notifyRemove(IDestructorCallbackSP context, GlobalId gid, SerialNum serialNum) override;
- void notifyRemoveDone(GlobalId gid, SerialNum serialNum) override;
+ void notifyRemoveDone(GlobalId gid, SerialNum serialNum);
+ std::unique_ptr<IPendingGidToLidChanges> grab_pending_changes() override;
/**
* Close handler, further notifications are blocked.
diff --git a/searchcore/src/vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h b/searchcore/src/vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h
index cbafef57e46..0dad58b31ae 100644
--- a/searchcore/src/vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h
+++ b/searchcore/src/vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h
@@ -10,6 +10,8 @@ namespace document { class GlobalId; }
namespace proton {
+class IPendingGidToLidChanges;
+
/*
* Interface class for registering listeners that get notification when
* gid to lid mapping changes.
@@ -36,11 +38,15 @@ public:
virtual void removeListeners(const vespalib::string &docTypeName,
const std::set<vespalib::string> &keepNames) = 0;
/**
- * Notify gid to lid mapping change.
+ * Notify pending gid to lid mapping change. Passed on to listeners later
+ * when force commit has made changes visible.
+ */
+ virtual void notifyPut(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serial_num) = 0;
+ /**
+ * Notify removal of gid. Passed on to listeners at once.
*/
- virtual void notifyPutDone(IDestructorCallbackSP context, GlobalId gid, uint32_t lid, SerialNum serialNum) = 0;
virtual void notifyRemove(IDestructorCallbackSP context, GlobalId gid, SerialNum serialNum) = 0;
- virtual void notifyRemoveDone(GlobalId gid, SerialNum serialNum) = 0;
+ virtual std::unique_ptr<IPendingGidToLidChanges> grab_pending_changes() = 0;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h b/searchcore/src/vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h
new file mode 100644
index 00000000000..6e4e9f240ff
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h
@@ -0,0 +1,18 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace proton {
+
+/*
+ * Interface class for a container of gid to lid changes awaiting a
+ * force commit.
+ */
+class IPendingGidToLidChanges
+{
+public:
+ virtual ~IPendingGidToLidChanges() = default;
+ virtual void notify_done() = 0;
+};
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_change.h b/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_change.h
new file mode 100644
index 00000000000..1577077a47b
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_change.h
@@ -0,0 +1,44 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/searchlib/common/idestructorcallback.h>
+#include <vespa/document/base/globalid.h>
+#include <vespa/searchlib/common/serialnum.h>
+
+namespace proton {
+
+/*
+ * Class for a gid to lid change awaiting a force commit.
+ */
+class PendingGidToLidChange
+{
+ using Context = std::shared_ptr<search::IDestructorCallback>;
+ using GlobalId = document::GlobalId;
+ using SerialNum = search::SerialNum;
+
+ Context _context;
+ SerialNum _serial_num;
+ GlobalId _gid;
+ uint32_t _lid;
+ bool _is_remove;
+public:
+ PendingGidToLidChange();
+ PendingGidToLidChange(Context context, const GlobalId& gid, uint32_t lid, SerialNum serial_num, bool is_remove_) noexcept
+ : _context(std::move(context)),
+ _serial_num(serial_num),
+ _gid(gid),
+ _lid(lid),
+ _is_remove(is_remove_)
+ {
+ }
+ ~PendingGidToLidChange() = default;
+
+ Context steal_context() && { return std::move(_context); }
+ const GlobalId &get_gid() const { return _gid; }
+ uint32_t get_lid() const { return _lid; }
+ SerialNum get_serial_num() const { return _serial_num; }
+ bool is_remove() const { return _is_remove; }
+};
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.cpp b/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.cpp
new file mode 100644
index 00000000000..bb9da2d9c7f
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.cpp
@@ -0,0 +1,29 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "pending_gid_to_lid_changes.h"
+#include "gid_to_lid_change_handler.h"
+
+namespace proton {
+
+PendingGidToLidChanges::PendingGidToLidChanges(GidToLidChangeHandler& handler, std::vector<PendingGidToLidChange> &&pending_changes)
+ : IPendingGidToLidChanges(),
+ _handler(handler),
+ _pending_changes(std::move(pending_changes))
+{
+}
+
+PendingGidToLidChanges::~PendingGidToLidChanges() = default;
+
+void
+PendingGidToLidChanges::notify_done()
+{
+ for (auto& change : _pending_changes) {
+ if (change.is_remove()) {
+ _handler.notifyRemoveDone(change.get_gid(), change.get_serial_num());
+ } else {
+ _handler.notifyPutDone(std::move(change).steal_context(), change.get_gid(), change.get_lid(), change.get_serial_num());
+ }
+ }
+}
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.h b/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.h
new file mode 100644
index 00000000000..a3e74e74b1f
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/reference/pending_gid_to_lid_changes.h
@@ -0,0 +1,26 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "i_pending_gid_to_lid_changes.h"
+#include "pending_gid_to_lid_change.h"
+#include <vector>
+
+namespace proton {
+
+class GidToLidChangeHandler;
+
+/*
+ * Class for a vector of gid to lid changes awaiting a force commit.
+ */
+class PendingGidToLidChanges : public IPendingGidToLidChanges
+{
+ GidToLidChangeHandler& _handler;
+ std::vector<PendingGidToLidChange> _pending_changes;
+public:
+ PendingGidToLidChanges(GidToLidChangeHandler& handler, std::vector<PendingGidToLidChange> &&pending_changes);
+ ~PendingGidToLidChanges() override;
+ void notify_done() override;
+};
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.cpp b/searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.cpp
deleted file mode 100644
index e806628bc02..00000000000
--- a/searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "pending_notify_remove_done.h"
-#include <vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h>
-#include <cassert>
-
-namespace proton
-{
-
-PendingNotifyRemoveDone::PendingNotifyRemoveDone()
- : _gidToLidChangeHandler(nullptr),
- _gid(),
- _serialNum(0),
- _pending(false)
-{
-}
-
-PendingNotifyRemoveDone::PendingNotifyRemoveDone(PendingNotifyRemoveDone &&rhs)
- : _gidToLidChangeHandler(rhs._gidToLidChangeHandler),
- _gid(rhs._gid),
- _serialNum(rhs._serialNum),
- _pending(rhs._pending)
-{
- rhs._pending = false;
-}
-
-PendingNotifyRemoveDone::~PendingNotifyRemoveDone()
-{
- assert(!_pending); // Fail if notifyRemoveDone is still pending
-}
-
-void
-PendingNotifyRemoveDone::setup(IGidToLidChangeHandler &gidToLidChangeHandler, document::GlobalId gid, search::SerialNum serialNum)
-{
- _gidToLidChangeHandler = &gidToLidChangeHandler;
- _gid = gid;
- _serialNum = serialNum;
- _pending = true;
-}
-
-void
-PendingNotifyRemoveDone::invoke()
-{
- if (_pending) {
- _gidToLidChangeHandler->notifyRemoveDone(_gid, _serialNum);
- _pending = false;
- }
-}
-
-} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.h b/searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.h
deleted file mode 100644
index 95aad182b10..00000000000
--- a/searchcore/src/vespa/searchcore/proton/reference/pending_notify_remove_done.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include <vespa/document/base/globalid.h>
-#include <vespa/searchlib/common/serialnum.h>
-
-namespace proton
-{
-
-class IGidToLidChangeHandler;
-
-/*
- * Class used to keep track of a pending notifyRemoveDone() call to
- * a gid to lid change handler.
- */
-class PendingNotifyRemoveDone
-{
- IGidToLidChangeHandler *_gidToLidChangeHandler;
- document::GlobalId _gid;
- search::SerialNum _serialNum;
- bool _pending;
-
-public:
- PendingNotifyRemoveDone();
- PendingNotifyRemoveDone(PendingNotifyRemoveDone &&rhs);
- PendingNotifyRemoveDone(const PendingNotifyRemoveDone &rhs) = delete;
- PendingNotifyRemoveDone &operator=(const PendingNotifyRemoveDone &rhs) = delete;
- PendingNotifyRemoveDone &operator=(PendingNotifyRemoveDone &&rhs) = delete;
- ~PendingNotifyRemoveDone();
- void setup(IGidToLidChangeHandler &gidToLidChangeHandler, document::GlobalId gid, search::SerialNum serialNum);
- void invoke();
-};
-
-} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
index 93432221e61..57445775df3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/server/CMakeLists.txt
@@ -80,7 +80,6 @@ vespa_add_library(searchcore_server STATIC
pruneremoveddocumentsjob.cpp
putdonecontext.cpp
reconfig_params.cpp
- remove_batch_done_context.cpp
remove_operations_rate_tracker.cpp
removedonecontext.cpp
removedonetask.cpp
diff --git a/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.cpp b/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.cpp
index 9c9c3fc7eca..32554555984 100644
--- a/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.cpp
@@ -3,6 +3,7 @@
#include "forcecommitcontext.h"
#include "forcecommitdonetask.h"
#include <vespa/searchcore/proton/common/docid_limit.h>
+#include <vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h>
#include <cassert>
namespace proton {
@@ -10,9 +11,10 @@ namespace proton {
ForceCommitContext::ForceCommitContext(vespalib::Executor &executor,
IDocumentMetaStore &documentMetaStore,
PendingLidTrackerBase::Snapshot lidsToCommit,
+ std::unique_ptr<IPendingGidToLidChanges> pending_gid_to_lid_changes,
std::shared_ptr<IDestructorCallback> onDone)
: _executor(executor),
- _task(std::make_unique<ForceCommitDoneTask>(documentMetaStore)),
+ _task(std::make_unique<ForceCommitDoneTask>(documentMetaStore, std::move(pending_gid_to_lid_changes))),
_committedDocIdLimit(0u),
_docIdLimit(nullptr),
_lidsToCommit(std::move(lidsToCommit)),
diff --git a/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.h b/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.h
index a9987a15da6..73c4ac97f42 100644
--- a/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.h
+++ b/searchcore/src/vespa/searchcore/proton/server/forcecommitcontext.h
@@ -12,6 +12,7 @@ namespace proton {
class ForceCommitDoneTask;
struct IDocumentMetaStore;
class DocIdLimit;
+class IPendingGidToLidChanges;
/**
* Context class for forced commits that schedules a task when
@@ -34,6 +35,7 @@ public:
ForceCommitContext(vespalib::Executor &executor,
IDocumentMetaStore &documentMetaStore,
PendingLidTrackerBase::Snapshot lidsToCommit,
+ std::unique_ptr<IPendingGidToLidChanges> pending_gid_to_lid_changes,
std::shared_ptr<IDestructorCallback> onDone);
~ForceCommitContext() override;
diff --git a/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.cpp b/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.cpp
index 81da2a4ec3e..733c15d55bb 100644
--- a/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.cpp
@@ -2,13 +2,15 @@
#include "forcecommitdonetask.h"
#include <vespa/searchcore/proton/documentmetastore/i_document_meta_store.h>
+#include <vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h>
namespace proton {
-ForceCommitDoneTask::ForceCommitDoneTask(IDocumentMetaStore &documentMetaStore)
+ForceCommitDoneTask::ForceCommitDoneTask(IDocumentMetaStore &documentMetaStore, std::unique_ptr<IPendingGidToLidChanges> pending_gid_to_lid_changes)
: _lidsToReuse(),
_holdUnblockShrinkLidSpace(false),
- _documentMetaStore(documentMetaStore)
+ _documentMetaStore(documentMetaStore),
+ _pending_gid_to_lid_changes(std::move(pending_gid_to_lid_changes))
{
}
@@ -24,6 +26,9 @@ ForceCommitDoneTask::reuseLids(std::vector<uint32_t> &&lids)
void
ForceCommitDoneTask::run()
{
+ if (_pending_gid_to_lid_changes) {
+ _pending_gid_to_lid_changes->notify_done();
+ }
if (!_lidsToReuse.empty()) {
if (_lidsToReuse.size() == 1) {
_documentMetaStore.removeComplete(_lidsToReuse[0]);
diff --git a/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.h b/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.h
index cffe4199e84..fe95d1575e9 100644
--- a/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.h
+++ b/searchcore/src/vespa/searchcore/proton/server/forcecommitdonetask.h
@@ -8,6 +8,7 @@
namespace proton {
struct IDocumentMetaStore;
+class IPendingGidToLidChanges;
/**
* Class for task to be executed when a forced commit has completed and
@@ -28,9 +29,10 @@ class ForceCommitDoneTask : public vespalib::Executor::Task
std::vector<uint32_t> _lidsToReuse;
bool _holdUnblockShrinkLidSpace;
IDocumentMetaStore &_documentMetaStore;
+ std::unique_ptr<IPendingGidToLidChanges> _pending_gid_to_lid_changes;
public:
- ForceCommitDoneTask(IDocumentMetaStore &documentMetaStore);
+ ForceCommitDoneTask(IDocumentMetaStore &documentMetaStore, std::unique_ptr<IPendingGidToLidChanges> pending_gid_to_lid_changes);
~ForceCommitDoneTask() override;
void reuseLids(std::vector<uint32_t> &&lids);
@@ -42,7 +44,7 @@ public:
void run() override;
bool empty() const {
- return _lidsToReuse.empty() && !_holdUnblockShrinkLidSpace;
+ return _lidsToReuse.empty() && !_holdUnblockShrinkLidSpace && !_pending_gid_to_lid_changes;
}
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/putdonecontext.cpp b/searchcore/src/vespa/searchcore/proton/server/putdonecontext.cpp
index 20ffe203235..23caaf1250b 100644
--- a/searchcore/src/vespa/searchcore/proton/server/putdonecontext.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/putdonecontext.cpp
@@ -10,18 +10,12 @@ using document::Document;
namespace proton {
PutDoneContext::PutDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted,
- IGidToLidChangeHandler &gidToLidChangeHandler,
std::shared_ptr<const Document> doc,
- const document::GlobalId &gid, uint32_t lid,
- search::SerialNum serialNum, bool enableNotifyPut)
+ uint32_t lid)
: OperationDoneContext(std::move(token)),
_uncommitted(std::move(uncommitted)),
_lid(lid),
_docIdLimit(nullptr),
- _gidToLidChangeHandler(gidToLidChangeHandler),
- _gid(gid),
- _serialNum(serialNum),
- _enableNotifyPut(enableNotifyPut),
_doc(std::move(doc))
{
}
@@ -31,9 +25,6 @@ PutDoneContext::~PutDoneContext()
if (_docIdLimit != nullptr) {
_docIdLimit->bumpUpLimit(_lid + 1);
}
- if (_enableNotifyPut) {
- _gidToLidChangeHandler.notifyPutDone(steal(), _gid, _lid, _serialNum);
- }
}
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/putdonecontext.h b/searchcore/src/vespa/searchcore/proton/server/putdonecontext.h
index c5e8c558c9e..e7271d8a1b3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/putdonecontext.h
+++ b/searchcore/src/vespa/searchcore/proton/server/putdonecontext.h
@@ -12,7 +12,6 @@ namespace document { class Document; }
namespace proton {
class DocIdLimit;
-class IGidToLidChangeHandler;
/**
* Context class for document put operations that acks operation when
@@ -26,16 +25,12 @@ class PutDoneContext : public OperationDoneContext
IPendingLidTracker::Token _uncommitted;
uint32_t _lid;
DocIdLimit *_docIdLimit;
- IGidToLidChangeHandler &_gidToLidChangeHandler;
- document::GlobalId _gid;
- search::SerialNum _serialNum;
- bool _enableNotifyPut;
std::shared_ptr<const document::Document> _doc;
public:
- PutDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted, IGidToLidChangeHandler &gidToLidChangeHandler,
+ PutDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted,
std::shared_ptr<const document::Document> doc,
- const document::GlobalId &gid, uint32_t lid, search::SerialNum serialNum, bool enableNotifyPut);
+ uint32_t lid);
~PutDoneContext() override;
void registerPutLid(DocIdLimit *docIdLimit) { _docIdLimit = docIdLimit; }
diff --git a/searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.cpp b/searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.cpp
deleted file mode 100644
index b0ece5f35a1..00000000000
--- a/searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "remove_batch_done_context.h"
-#include <vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h>
-
-namespace proton {
-
-RemoveBatchDoneContext::RemoveBatchDoneContext(vespalib::Executor &executor,
- vespalib::Executor::Task::UP task,
- IGidToLidChangeHandler &gidToLidChangeHandler,
- std::vector<document::GlobalId> gidsToRemove,
- search::SerialNum serialNum)
- : search::ScheduleTaskCallback(executor, std::move(task)),
- _gidToLidChangeHandler(gidToLidChangeHandler),
- _gidsToRemove(std::move(gidsToRemove)),
- _serialNum(serialNum)
-{
-}
-
-RemoveBatchDoneContext::~RemoveBatchDoneContext()
-{
- for (const auto &gid : _gidsToRemove) {
- _gidToLidChangeHandler.notifyRemoveDone(gid, _serialNum);
- }
-}
-
-} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.h b/searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.h
deleted file mode 100644
index 2a93239574a..00000000000
--- a/searchcore/src/vespa/searchcore/proton/server/remove_batch_done_context.h
+++ /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.
-
-#pragma once
-
-#include <vespa/searchlib/common/scheduletaskcallback.h>
-#include <vespa/document/base/globalid.h>
-#include <vespa/searchlib/common/serialnum.h>
-#include <vector>
-
-namespace proton
-{
-
-class IGidToLidChangeHandler;
-
-/**
- * Context class for document batch remove that notifies gid to lid
- * change handler about each remove done and schedules a
- * task when instance is destroyed. Typically a shared pointer to an
- * instance is passed around to multiple worker threads that performs
- * portions of a larger task before dropping the shared pointer.
- */
-class RemoveBatchDoneContext : public search::ScheduleTaskCallback
-{
- IGidToLidChangeHandler &_gidToLidChangeHandler;
- std::vector<document::GlobalId> _gidsToRemove;
- search::SerialNum _serialNum;
-
-public:
- RemoveBatchDoneContext(vespalib::Executor &executor,
- vespalib::Executor::Task::UP task,
- IGidToLidChangeHandler &gidToLidChangeHandler,
- std::vector<document::GlobalId> gidsToRemove,
- search::SerialNum serialNum);
-
- virtual ~RemoveBatchDoneContext();
-};
-
-} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/removedonecontext.cpp b/searchcore/src/vespa/searchcore/proton/server/removedonecontext.cpp
index d35e6bb7127..859d8693f6d 100644
--- a/searchcore/src/vespa/searchcore/proton/server/removedonecontext.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/removedonecontext.cpp
@@ -9,11 +9,10 @@ namespace proton {
RemoveDoneContext::RemoveDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted, vespalib::Executor &executor,
IDocumentMetaStore &documentMetaStore,
- PendingNotifyRemoveDone &&pendingNotifyRemoveDone, uint32_t lid)
+ uint32_t lid)
: OperationDoneContext(std::move(token)),
_executor(executor),
_task(),
- _pendingNotifyRemoveDone(std::move(pendingNotifyRemoveDone)),
_uncommitted(std::move(uncommitted))
{
if (lid != 0) {
@@ -23,7 +22,6 @@ RemoveDoneContext::RemoveDoneContext(FeedToken token, IPendingLidTracker::Token
RemoveDoneContext::~RemoveDoneContext()
{
- _pendingNotifyRemoveDone.invoke();
ack();
if (_task) {
vespalib::Executor::Task::UP res = _executor.execute(std::move(_task));
diff --git a/searchcore/src/vespa/searchcore/proton/server/removedonecontext.h b/searchcore/src/vespa/searchcore/proton/server/removedonecontext.h
index 7b6c6be1fe1..485b82dd141 100644
--- a/searchcore/src/vespa/searchcore/proton/server/removedonecontext.h
+++ b/searchcore/src/vespa/searchcore/proton/server/removedonecontext.h
@@ -3,7 +3,6 @@
#pragma once
#include "operationdonecontext.h"
-#include <vespa/searchcore/proton/reference/pending_notify_remove_done.h>
#include <vespa/searchcore/proton/common/ipendinglidtracker.h>
#include <vespa/vespalib/util/executor.h>
#include <vespa/document/base/globalid.h>
@@ -26,12 +25,11 @@ class RemoveDoneContext : public OperationDoneContext
{
vespalib::Executor &_executor;
std::unique_ptr<vespalib::Executor::Task> _task;
- PendingNotifyRemoveDone _pendingNotifyRemoveDone;
IPendingLidTracker::Token _uncommitted;
public:
RemoveDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted, vespalib::Executor &executor, IDocumentMetaStore &documentMetaStore,
- PendingNotifyRemoveDone &&pendingNotifyRemoveDone, uint32_t lid);
+ uint32_t lid);
~RemoveDoneContext() override;
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
index bf357188766..7c3c796bda3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.cpp
@@ -5,7 +5,6 @@
#include "ireplayconfig.h"
#include "operationdonecontext.h"
#include "putdonecontext.h"
-#include "remove_batch_done_context.h"
#include "removedonecontext.h"
#include "updatedonecontext.h"
#include <vespa/document/datatype/documenttype.h>
@@ -15,7 +14,9 @@
#include <vespa/searchcore/proton/common/feedtoken.h>
#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h>
+#include <vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h>
#include <vespa/searchlib/common/gatecallback.h>
+#include <vespa/searchlib/common/scheduletaskcallback.h>
#include <vespa/vespalib/util/isequencedtaskexecutor.h>
#include <vespa/vespalib/util/exceptions.h>
@@ -48,11 +49,10 @@ private:
public:
PutDoneContextForMove(FeedToken token, IPendingLidTracker::Token uncommitted,
- IGidToLidChangeHandler &gidToLidChangeHandler,
std::shared_ptr<const Document> doc,
- const document::GlobalId &gid, uint32_t lid, search::SerialNum serialNum,
- bool enableNotifyPut, IDestructorCallback::SP moveDoneCtx)
- : PutDoneContext(std::move(token), std::move(uncommitted), gidToLidChangeHandler, std::move(doc), gid, lid, serialNum, enableNotifyPut),
+ uint32_t lid,
+ IDestructorCallback::SP moveDoneCtx)
+ : PutDoneContext(std::move(token), std::move(uncommitted),std::move(doc), lid),
_moveDoneCtx(std::move(moveDoneCtx))
{}
~PutDoneContextForMove() override = default;
@@ -60,31 +60,28 @@ public:
std::shared_ptr<PutDoneContext>
createPutDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted,
- IGidToLidChangeHandler &gidToLidChangeHandler,
std::shared_ptr<const Document> doc,
- const document::GlobalId &gid, uint32_t lid,
- SerialNum serialNum, bool enableNotifyPut,
+ uint32_t lid,
IDestructorCallback::SP moveDoneCtx)
{
std::shared_ptr<PutDoneContext> result;
if (moveDoneCtx) {
- result = std::make_shared<PutDoneContextForMove>(std::move(token), std::move(uncommitted), gidToLidChangeHandler,
- std::move(doc), gid, lid, serialNum, enableNotifyPut, std::move(moveDoneCtx));
+ result = std::make_shared<PutDoneContextForMove>(std::move(token), std::move(uncommitted),
+ std::move(doc), lid, std::move(moveDoneCtx));
} else {
- result = std::make_shared<PutDoneContext>(std::move(token), std::move(uncommitted), gidToLidChangeHandler,
- std::move(doc), gid, lid, serialNum, enableNotifyPut);
+ result = std::make_shared<PutDoneContext>(std::move(token), std::move(uncommitted),
+ std::move(doc), lid);
}
return result;
}
std::shared_ptr<PutDoneContext>
createPutDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted,
- IGidToLidChangeHandler &gidToLidChangeHandler,
std::shared_ptr<const Document> doc,
- const document::GlobalId &gid, uint32_t lid, SerialNum serialNum, bool enableNotifyPut)
+ uint32_t lid)
{
- return createPutDoneContext(std::move(token), std::move(uncommitted), gidToLidChangeHandler, std::move(doc), gid,
- lid, serialNum, enableNotifyPut, IDestructorCallback::SP());
+ return createPutDoneContext(std::move(token), std::move(uncommitted), std::move(doc),
+ lid, IDestructorCallback::SP());
}
std::shared_ptr<UpdateDoneContext>
@@ -109,10 +106,10 @@ private:
public:
RemoveDoneContextForMove(FeedToken token, IPendingLidTracker::Token uncommitted, vespalib::Executor &executor,
- IDocumentMetaStore &documentMetaStore, PendingNotifyRemoveDone &&pendingNotifyRemoveDone,
+ IDocumentMetaStore &documentMetaStore,
uint32_t lid, IDestructorCallback::SP moveDoneCtx)
: RemoveDoneContext(std::move(token), std::move(uncommitted), executor,
- documentMetaStore, std::move(pendingNotifyRemoveDone) ,lid),
+ documentMetaStore, lid),
_moveDoneCtx(std::move(moveDoneCtx))
{}
~RemoveDoneContextForMove() override = default;
@@ -120,16 +117,16 @@ public:
std::shared_ptr<RemoveDoneContext>
createRemoveDoneContext(FeedToken token, IPendingLidTracker::Token uncommitted, vespalib::Executor &executor,
- IDocumentMetaStore &documentMetaStore,PendingNotifyRemoveDone &&pendingNotifyRemoveDone,
+ IDocumentMetaStore &documentMetaStore,
uint32_t lid, IDestructorCallback::SP moveDoneCtx)
{
if (moveDoneCtx) {
return std::make_shared<RemoveDoneContextForMove>
- (std::move(token), std::move(uncommitted), executor, documentMetaStore, std::move(pendingNotifyRemoveDone),
+ (std::move(token), std::move(uncommitted), executor, documentMetaStore,
lid, std::move(moveDoneCtx));
} else {
return std::make_shared<RemoveDoneContext>
- (std::move(token), std::move(uncommitted), executor, documentMetaStore, std::move(pendingNotifyRemoveDone), lid);
+ (std::move(token), std::move(uncommitted), executor, documentMetaStore, lid);
}
}
@@ -243,6 +240,7 @@ StoreOnlyFeedView::forceCommit(SerialNum serialNum, DoneCallback onDone)
{
internalForceCommit(serialNum, std::make_shared<ForceCommitContext>(_writeService.master(), _metaStore,
_pendingLidsForCommit->produceSnapshot(),
+ _gidToLidChangeHandler.grab_pending_changes(),
std::move(onDone)));
}
@@ -305,17 +303,18 @@ StoreOnlyFeedView::internalPut(FeedToken token, const PutOperation &putOp)
putOp.getSubDbId(), putOp.getLid(), putOp.getPrevSubDbId(), putOp.getPrevLid(),
_params._subDbId, doc->toString(true).size(), doc->toString(true).c_str());
- PendingNotifyRemoveDone pendingNotifyRemoveDone = adjustMetaStore(putOp, docId.getGlobalId(), docId);
+ adjustMetaStore(putOp, docId.getGlobalId(), docId);
auto uncommitted = get_pending_lid_token(putOp);
bool docAlreadyExists = putOp.getValidPrevDbdId(_params._subDbId);
if (putOp.getValidDbdId(_params._subDbId)) {
- const document::GlobalId &gid = docId.getGlobalId();
+ if (putOp.changedDbdId() && useDocumentMetaStore(serialNum)) {
+ _gidToLidChangeHandler.notifyPut(token, docId.getGlobalId(), putOp.getLid(), serialNum);
+ }
std::shared_ptr<PutDoneContext> onWriteDone =
createPutDoneContext(std::move(token), std::move(uncommitted),
- _gidToLidChangeHandler, doc, gid, putOp.getLid(), serialNum,
- putOp.changedDbdId() && useDocumentMetaStore(serialNum));
+ doc, putOp.getLid());
putSummary(serialNum, putOp.getLid(), doc, onWriteDone);
putAttributes(serialNum, putOp.getLid(), *doc, onWriteDone);
putIndexedFields(serialNum, putOp.getLid(), doc, onWriteDone);
@@ -323,7 +322,7 @@ StoreOnlyFeedView::internalPut(FeedToken token, const PutOperation &putOp)
if (docAlreadyExists && putOp.changedDbdId()) {
assert(!putOp.getValidDbdId(_params._subDbId));
internalRemove(std::move(token), _pendingLidsForCommit->produce(putOp.getPrevLid()), serialNum,
- std::move(pendingNotifyRemoveDone), putOp.getPrevLid(), IDestructorCallback::SP());
+ putOp.getPrevLid(), IDestructorCallback::SP());
}
}
@@ -574,7 +573,7 @@ StoreOnlyFeedView::internalRemove(FeedToken token, const RemoveOperationWithDocI
_params._docTypeName.toString().c_str(), serialNum, docId.toString().c_str(),
rmOp.getSubDbId(), rmOp.getLid(), rmOp.getPrevSubDbId(), rmOp.getPrevLid(), _params._subDbId);
- PendingNotifyRemoveDone pendingNotifyRemoveDone = adjustMetaStore(rmOp, docId.getGlobalId(), docId);
+ adjustMetaStore(rmOp, docId.getGlobalId(), docId);
auto uncommitted = get_pending_lid_token(rmOp);
if (rmOp.getValidDbdId(_params._subDbId)) {
@@ -587,7 +586,7 @@ StoreOnlyFeedView::internalRemove(FeedToken token, const RemoveOperationWithDocI
if (rmOp.changedDbdId()) {
assert(!rmOp.getValidDbdId(_params._subDbId));
internalRemove(std::move(token), _pendingLidsForCommit->produce(rmOp.getPrevLid()), serialNum,
- std::move(pendingNotifyRemoveDone), rmOp.getPrevLid(), IDestructorCallback::SP());
+ rmOp.getPrevLid(), IDestructorCallback::SP());
}
}
}
@@ -599,13 +598,13 @@ StoreOnlyFeedView::internalRemove(FeedToken token, const RemoveOperationWithGid
assert(rmOp.notMovingLidInSameSubDb());
const SerialNum serialNum = rmOp.getSerialNum();
DocumentId dummy;
- PendingNotifyRemoveDone pendingNotifyRemoveDone = adjustMetaStore(rmOp, rmOp.getGlobalId(), dummy);
+ adjustMetaStore(rmOp, rmOp.getGlobalId(), dummy);
auto uncommitted = _pendingLidsForCommit->produce(rmOp.getLid());
if (rmOp.getValidPrevDbdId(_params._subDbId)) {
if (rmOp.changedDbdId()) {
assert(!rmOp.getValidDbdId(_params._subDbId));
- internalRemove(std::move(token), _pendingLidsForCommit->produce(rmOp.getPrevLid()), serialNum, std::move(pendingNotifyRemoveDone),
+ internalRemove(std::move(token), _pendingLidsForCommit->produce(rmOp.getPrevLid()), serialNum,
rmOp.getPrevLid(), IDestructorCallback::SP());
}
}
@@ -613,23 +612,22 @@ StoreOnlyFeedView::internalRemove(FeedToken token, const RemoveOperationWithGid
void
StoreOnlyFeedView::internalRemove(FeedToken token, IPendingLidTracker::Token uncommitted, SerialNum serialNum,
- PendingNotifyRemoveDone &&pendingNotifyRemoveDone, Lid lid,
+ Lid lid,
IDestructorCallback::SP moveDoneCtx)
{
bool explicitReuseLid = _lidReuseDelayer.delayReuse(lid);
std::shared_ptr<RemoveDoneContext> onWriteDone;
onWriteDone = createRemoveDoneContext(std::move(token), std::move(uncommitted),_writeService.master(), _metaStore,
- std::move(pendingNotifyRemoveDone), (explicitReuseLid ? lid : 0u),
+ (explicitReuseLid ? lid : 0u),
std::move(moveDoneCtx));
removeSummary(serialNum, lid, onWriteDone);
removeAttributes(serialNum, lid, onWriteDone);
removeIndexedFields(serialNum, lid, onWriteDone);
}
-PendingNotifyRemoveDone
+void
StoreOnlyFeedView::adjustMetaStore(const DocumentOperation &op, const GlobalId & gid, const DocumentId &docId)
{
- PendingNotifyRemoveDone pendingNotifyRemoveDone;
const SerialNum serialNum = op.getSerialNum();
if (useDocumentMetaStore(serialNum)) {
if (op.getValidDbdId(_params._subDbId)) {
@@ -644,13 +642,11 @@ StoreOnlyFeedView::adjustMetaStore(const DocumentOperation &op, const GlobalId &
} else if (op.getValidPrevDbdId(_params._subDbId)) {
vespalib::Gate gate;
_gidToLidChangeHandler.notifyRemove(std::make_shared<search::GateCallback>(gate), gid, serialNum);
- pendingNotifyRemoveDone.setup(_gidToLidChangeHandler, gid, serialNum);
gate.await();
removeMetaData(_metaStore, gid, docId, op, _params._subDbType == SubDbType::REMOVED);
}
_metaStore.commit(serialNum, serialNum);
}
- return pendingNotifyRemoveDone;
}
void
@@ -695,8 +691,7 @@ StoreOnlyFeedView::removeDocuments(const RemoveDocumentsOperation &op, bool remo
} else {
removeBatchDoneTask = makeLambdaTask([]() {});
}
- onWriteDone = std::make_shared<RemoveBatchDoneContext>(_writeService.master(), std::move(removeBatchDoneTask),
- _gidToLidChangeHandler, std::move(gidsToRemove), serialNum);
+ onWriteDone = std::make_shared<search::ScheduleTaskCallback>(_writeService.master(), std::move(removeBatchDoneTask));
if (remove_index_and_attributes) {
removeIndexedFields(serialNum, lidsToRemove, onWriteDone);
removeAttributes(serialNum, lidsToRemove, onWriteDone);
@@ -767,20 +762,21 @@ StoreOnlyFeedView::handleMove(const MoveOperation &moveOp, IDestructorCallback::
moveOp.getSubDbId(), moveOp.getLid(), moveOp.getPrevSubDbId(), moveOp.getPrevLid(),
_params._subDbId, doc->toString(true).size(), doc->toString(true).c_str());
- PendingNotifyRemoveDone pendingNotifyRemoveDone = adjustMetaStore(moveOp, docId.getGlobalId(), docId);
+ adjustMetaStore(moveOp, docId.getGlobalId(), docId);
bool docAlreadyExists = moveOp.getValidPrevDbdId(_params._subDbId);
if (moveOp.getValidDbdId(_params._subDbId)) {
- const document::GlobalId &gid = docId.getGlobalId();
+ if (moveOp.changedDbdId() && useDocumentMetaStore(serialNum)) {
+ _gidToLidChangeHandler.notifyPut(FeedToken(), docId.getGlobalId(), moveOp.getLid(), serialNum);
+ }
std::shared_ptr<PutDoneContext> onWriteDone =
createPutDoneContext(FeedToken(), _pendingLidsForCommit->produce(moveOp.getLid()),
- _gidToLidChangeHandler, doc, gid, moveOp.getLid(), serialNum,
- moveOp.changedDbdId() && useDocumentMetaStore(serialNum), doneCtx);
+ doc, moveOp.getLid(), doneCtx);
putSummary(serialNum, moveOp.getLid(), doc, onWriteDone);
putAttributes(serialNum, moveOp.getLid(), *doc, onWriteDone);
putIndexedFields(serialNum, moveOp.getLid(), doc, onWriteDone);
}
if (docAlreadyExists && moveOp.changedDbdId()) {
- internalRemove(FeedToken(), _pendingLidsForCommit->produce(moveOp.getPrevLid()), serialNum, std::move(pendingNotifyRemoveDone), moveOp.getPrevLid(), doneCtx);
+ internalRemove(FeedToken(), _pendingLidsForCommit->produce(moveOp.getPrevLid()), serialNum, moveOp.getPrevLid(), doneCtx);
}
}
@@ -820,6 +816,7 @@ StoreOnlyFeedView::handleCompactLidSpace(const CompactLidSpaceOperation &op)
getDocumentMetaStore()->get().compactLidSpace(op.getLidLimit());
auto commitContext(std::make_shared<ForceCommitContext>(_writeService.master(), _metaStore,
_pendingLidsForCommit->produceSnapshot(),
+ _gidToLidChangeHandler.grab_pending_changes(),
DoneCallback()));
commitContext->holdUnblockShrinkLidSpace();
internalForceCommit(serialNum, commitContext);
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
index da7d5e53a88..9927c93add4 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlyfeedview.h
@@ -16,7 +16,6 @@
#include <vespa/searchcore/proton/documentmetastore/lidreusedelayer.h>
#include <vespa/searchcore/proton/feedoperation/lidvectorcontext.h>
#include <vespa/searchcore/proton/persistenceengine/resulthandler.h>
-#include <vespa/searchcore/proton/reference/pending_notify_remove_done.h>
#include <vespa/searchcorespi/index/ithreadingservice.h>
#include <vespa/searchlib/query/base.h>
#include <vespa/vespalib/util/threadstackexecutorbase.h>
@@ -169,7 +168,7 @@ private:
return replaySerialNum > _params._flushedDocumentMetaStoreSerialNum;
}
- PendingNotifyRemoveDone adjustMetaStore(const DocumentOperation &op, const document::GlobalId & gid, const document::DocumentId &docId);
+ void adjustMetaStore(const DocumentOperation &op, const document::GlobalId & gid, const document::DocumentId &docId);
void internalPut(FeedToken token, const PutOperation &putOp);
void internalUpdate(FeedToken token, const UpdateOperation &updOp);
@@ -182,7 +181,6 @@ private:
size_t removeDocuments(const RemoveDocumentsOperation &op, bool remove_index_and_attribute_fields);
void internalRemove(FeedToken token, IPendingLidTracker::Token uncommitted, SerialNum serialNum,
- PendingNotifyRemoveDone &&pendingNotifyRemoveDone,
Lid lid, std::shared_ptr<search::IDestructorCallback> moveDoneCtx);
IPendingLidTracker::Token get_pending_lid_token(const DocumentOperation &op);
diff --git a/searchcore/src/vespa/searchcore/proton/test/mock_gid_to_lid_change_handler.h b/searchcore/src/vespa/searchcore/proton/test/mock_gid_to_lid_change_handler.h
index f9531486e9b..976cdc70048 100644
--- a/searchcore/src/vespa/searchcore/proton/test/mock_gid_to_lid_change_handler.h
+++ b/searchcore/src/vespa/searchcore/proton/test/mock_gid_to_lid_change_handler.h
@@ -3,6 +3,7 @@
#include <vespa/searchcore/proton/reference/i_gid_to_lid_change_handler.h>
#include <vespa/searchcore/proton/reference/i_gid_to_lid_change_listener.h>
+#include <vespa/searchcore/proton/reference/i_pending_gid_to_lid_changes.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/test/insertion_operators.h>
@@ -42,9 +43,9 @@ public:
_removes.emplace_back(docTypeName, keepNames);
}
- void notifyPutDone(IDestructorCallbackSP, document::GlobalId, uint32_t, SerialNum) override { }
+ void notifyPut(IDestructorCallbackSP, document::GlobalId, uint32_t, SerialNum) override { }
void notifyRemove(IDestructorCallbackSP, document::GlobalId, SerialNum) override { }
- void notifyRemoveDone(document::GlobalId, SerialNum) override { }
+ std::unique_ptr<IPendingGidToLidChanges> grab_pending_changes() override { return {}; }
void assertAdds(const std::vector<AddEntry> &expAdds)
{
diff --git a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
index da77e29dbb0..daa85c91b2c 100644
--- a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
+++ b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp
@@ -49,6 +49,7 @@ using search::tensor::NearestNeighborIndexSaver;
using search::tensor::PrepareResult;
using search::tensor::TensorAttribute;
using vespalib::eval::TensorSpec;
+using vespalib::eval::CellType;
using vespalib::eval::ValueType;
using vespalib::eval::Value;
using vespalib::eval::EngineOrFactory;
@@ -228,11 +229,11 @@ class MockNearestNeighborIndexFactory : public NearestNeighborIndexFactory {
std::unique_ptr<NearestNeighborIndex> make(const DocVectorAccess& vectors,
size_t vector_size,
- ValueType::CellType cell_type,
+ CellType cell_type,
const search::attribute::HnswIndexParams& params) const override {
(void) vector_size;
(void) params;
- assert(cell_type == ValueType::CellType::DOUBLE);
+ assert(cell_type == CellType::DOUBLE);
return std::make_unique<MockNearestNeighborIndex>(vectors);
}
};
diff --git a/searchlib/src/tests/queryeval/nearest_neighbor/nearest_neighbor_test.cpp b/searchlib/src/tests/queryeval/nearest_neighbor/nearest_neighbor_test.cpp
index f6ca0bd1427..23cb3831b6d 100644
--- a/searchlib/src/tests/queryeval/nearest_neighbor/nearest_neighbor_test.cpp
+++ b/searchlib/src/tests/queryeval/nearest_neighbor/nearest_neighbor_test.cpp
@@ -25,7 +25,7 @@ using search::AttributeVector;
using search::BitVector;
using vespalib::eval::Value;
using vespalib::eval::ValueType;
-using CellType = vespalib::eval::ValueType::CellType;
+using vespalib::eval::CellType;
using vespalib::eval::TensorSpec;
using vespalib::eval::EngineOrFactory;
using search::tensor::DistanceFunction;
diff --git a/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp b/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp
index a9e24e056f2..06fb95089fd 100644
--- a/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp
+++ b/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp
@@ -33,7 +33,7 @@ void verify_geo_miles(const DistanceFunction *dist_fun,
TEST(DistanceFunctionsTest, euclidean_gives_expected_score)
{
- auto ct = vespalib::eval::ValueType::CellType::DOUBLE;
+ auto ct = vespalib::eval::CellType::DOUBLE;
auto euclid = make_distance_function(DistanceMetric::Euclidean, ct);
@@ -54,7 +54,7 @@ TEST(DistanceFunctionsTest, euclidean_gives_expected_score)
TEST(DistanceFunctionsTest, angular_gives_expected_score)
{
- auto ct = vespalib::eval::ValueType::CellType::DOUBLE;
+ auto ct = vespalib::eval::CellType::DOUBLE;
auto angular = make_distance_function(DistanceMetric::Angular, ct);
@@ -109,7 +109,7 @@ TEST(DistanceFunctionsTest, angular_gives_expected_score)
TEST(DistanceFunctionsTest, innerproduct_gives_expected_score)
{
- auto ct = vespalib::eval::ValueType::CellType::DOUBLE;
+ auto ct = vespalib::eval::CellType::DOUBLE;
auto innerproduct = make_distance_function(DistanceMetric::InnerProduct, ct);
@@ -144,7 +144,7 @@ TEST(DistanceFunctionsTest, innerproduct_gives_expected_score)
TEST(DistanceFunctionsTest, hamming_gives_expected_score)
{
- auto ct = vespalib::eval::ValueType::CellType::DOUBLE;
+ auto ct = vespalib::eval::CellType::DOUBLE;
auto hamming = make_distance_function(DistanceMetric::Hamming, ct);
@@ -184,7 +184,7 @@ TEST(DistanceFunctionsTest, hamming_gives_expected_score)
TEST(GeoDegreesTest, gives_expected_score)
{
- auto ct = vespalib::eval::ValueType::CellType::DOUBLE;
+ auto ct = vespalib::eval::CellType::DOUBLE;
auto geodeg = make_distance_function(DistanceMetric::GeoDegrees, ct);
std::vector<double> g1_sfo{37.61, -122.38};
diff --git a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_iterator.cpp b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_iterator.cpp
index dd685ce5c43..85b7e8f89e8 100644
--- a/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_iterator.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/nearest_neighbor_iterator.cpp
@@ -7,8 +7,7 @@ using search::tensor::DenseTensorAttribute;
using vespalib::ConstArrayRef;
using vespalib::tensor::MutableDenseTensorView;
using vespalib::eval::TypedCells;
-
-using CellType = vespalib::eval::ValueType::CellType;
+using vespalib::eval::CellType;
namespace search::queryeval {
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 0bb6f339455..aca14a1575e 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
@@ -28,7 +28,7 @@ make_random_level_generator(uint32_t m)
std::unique_ptr<NearestNeighborIndex>
DefaultNearestNeighborIndexFactory::make(const DocVectorAccess& vectors,
size_t vector_size,
- vespalib::eval::ValueType::CellType cell_type,
+ vespalib::eval::CellType cell_type,
const search::attribute::HnswIndexParams& params) const
{
(void) vector_size;
diff --git a/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.h b/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.h
index 6a9ded92b60..67a19a5431a 100644
--- a/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.h
+++ b/searchlib/src/vespa/searchlib/tensor/default_nearest_neighbor_index_factory.h
@@ -13,7 +13,7 @@ class DefaultNearestNeighborIndexFactory : public NearestNeighborIndexFactory {
public:
std::unique_ptr<NearestNeighborIndex> make(const DocVectorAccess& vectors,
size_t vector_size,
- vespalib::eval::ValueType::CellType cell_type,
+ vespalib::eval::CellType cell_type,
const search::attribute::HnswIndexParams& params) const override;
};
diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp
index 1abc3800d97..ddbb956838b 100644
--- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp
@@ -9,7 +9,7 @@ using vespalib::datastore::Handle;
using vespalib::tensor::MutableDenseTensorView;
using vespalib::eval::Value;
using vespalib::eval::ValueType;
-using CellType = vespalib::eval::ValueType::CellType;
+using CellType = vespalib::eval::CellType;
namespace search::tensor {
diff --git a/searchlib/src/vespa/searchlib/tensor/distance_function_factory.cpp b/searchlib/src/vespa/searchlib/tensor/distance_function_factory.cpp
index a868dfe191b..81b27b56258 100644
--- a/searchlib/src/vespa/searchlib/tensor/distance_function_factory.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/distance_function_factory.cpp
@@ -4,44 +4,45 @@
#include "distance_functions.h"
using search::attribute::DistanceMetric;
+using vespalib::eval::CellType;
using vespalib::eval::ValueType;
namespace search::tensor {
DistanceFunction::UP
-make_distance_function(DistanceMetric variant, ValueType::CellType cell_type)
+make_distance_function(DistanceMetric variant, CellType cell_type)
{
switch (variant) {
case DistanceMetric::Euclidean:
- if (cell_type == ValueType::CellType::FLOAT) {
+ if (cell_type == CellType::FLOAT) {
return std::make_unique<SquaredEuclideanDistance<float>>();
} else {
return std::make_unique<SquaredEuclideanDistance<double>>();
}
break;
case DistanceMetric::Angular:
- if (cell_type == ValueType::CellType::FLOAT) {
+ if (cell_type == CellType::FLOAT) {
return std::make_unique<AngularDistance<float>>();
} else {
return std::make_unique<AngularDistance<double>>();
}
break;
case DistanceMetric::GeoDegrees:
- if (cell_type == ValueType::CellType::FLOAT) {
+ if (cell_type == CellType::FLOAT) {
return std::make_unique<GeoDegreesDistance<float>>();
} else {
return std::make_unique<GeoDegreesDistance<double>>();
}
break;
case DistanceMetric::InnerProduct:
- if (cell_type == ValueType::CellType::FLOAT) {
+ if (cell_type == CellType::FLOAT) {
return std::make_unique<InnerProductDistance<float>>();
} else {
return std::make_unique<InnerProductDistance<double>>();
}
break;
case DistanceMetric::Hamming:
- if (cell_type == ValueType::CellType::FLOAT) {
+ if (cell_type == CellType::FLOAT) {
return std::make_unique<HammingDistance<float>>();
} else {
return std::make_unique<HammingDistance<double>>();
diff --git a/searchlib/src/vespa/searchlib/tensor/distance_function_factory.h b/searchlib/src/vespa/searchlib/tensor/distance_function_factory.h
index c86e40279bc..abb1f503694 100644
--- a/searchlib/src/vespa/searchlib/tensor/distance_function_factory.h
+++ b/searchlib/src/vespa/searchlib/tensor/distance_function_factory.h
@@ -14,6 +14,6 @@ namespace search::tensor {
**/
DistanceFunction::UP
make_distance_function(search::attribute::DistanceMetric variant,
- vespalib::eval::ValueType::CellType cell_type);
+ vespalib::eval::CellType cell_type);
}
diff --git a/searchlib/src/vespa/searchlib/tensor/nearest_neighbor_index_factory.h b/searchlib/src/vespa/searchlib/tensor/nearest_neighbor_index_factory.h
index 089119944a7..e5c15266ceb 100644
--- a/searchlib/src/vespa/searchlib/tensor/nearest_neighbor_index_factory.h
+++ b/searchlib/src/vespa/searchlib/tensor/nearest_neighbor_index_factory.h
@@ -20,7 +20,7 @@ public:
virtual ~NearestNeighborIndexFactory() {}
virtual std::unique_ptr<NearestNeighborIndex> make(const DocVectorAccess& vectors,
size_t vector_size,
- vespalib::eval::ValueType::CellType cell_type,
+ vespalib::eval::CellType cell_type,
const search::attribute::HnswIndexParams& params) const = 0;
};
diff --git a/simplemetrics/src/main/java/com/yahoo/metrics/simple/MetricReceiver.java b/simplemetrics/src/main/java/com/yahoo/metrics/simple/MetricReceiver.java
index a2b82978a26..e0e3469e257 100644
--- a/simplemetrics/src/main/java/com/yahoo/metrics/simple/MetricReceiver.java
+++ b/simplemetrics/src/main/java/com/yahoo/metrics/simple/MetricReceiver.java
@@ -29,6 +29,7 @@ public class MetricReceiver {
private volatile Map<String, MetricSettings> metricSettings;
private static final class NullCounter extends Counter {
+
NullCounter() {
super(null, null, null);
}
@@ -72,18 +73,23 @@ public class MetricReceiver {
public PointBuilder builder() {
return super.builder();
}
+
}
public static final class MockReceiver extends MetricReceiver {
+
private final ThreadLocalDirectory<Bucket, Sample> collection;
+
private MockReceiver(ThreadLocalDirectory<Bucket, Sample> collection) {
super(collection, null);
this.collection = collection;
}
+
public MockReceiver() {
this(new ThreadLocalDirectory<>(new MetricUpdater()));
}
- /** gathers all data since last snapshot */
+
+ /** Gathers all data since last snapshot */
public Bucket getSnapshot() {
final Bucket merged = new Bucket();
for (Bucket b : collection.fetch()) {
@@ -91,13 +97,16 @@ public class MetricReceiver {
}
return merged;
}
- /** utility method for testing */
+
+ /** Utility method for testing */
public Point point(String dim, String val) {
return pointBuilder().set(dim, val).build();
}
+
}
private static final class NullReceiver extends MetricReceiver {
+
NullReceiver() {
super(null, null);
}
@@ -164,21 +173,18 @@ public class MetricReceiver {
* {@link #declareGauge(String)}, or {@link #declareGauge(String, Point)}
* instead.
*
- * @param s
- * a single simple containing all meta data necessary to update a
- * metric
+ * @param sample a single simple containing all meta data necessary to update a metric
*/
- public void update(Sample s) {
+ public void update(Sample sample) {
// pass around the receiver instead of histogram settings to avoid reading any volatile if unnecessary
- s.setReceiver(this);
- metricsCollection.update(s);
+ sample.setReceiver(this);
+ metricsCollection.update(sample);
}
/**
* Declare a counter metric without setting any default position.
*
- * @param name
- * the name of the metric
+ * @param name the name of the metric
* @return a thread-safe counter
*/
public Counter declareCounter(String name) {
@@ -189,11 +195,8 @@ public class MetricReceiver {
* Declare a counter metric, with default dimension values as given. Create
* the point argument by using a builder from {@link #pointBuilder()}.
*
- * @param name
- * the name of the metric
- * @param boundDimensions
- * dimensions which have a fixed value in the life cycle of the
- * metric object or null
+ * @param name the name of the metric
+ * @param boundDimensions dimensions which have a fixed value in the life cycle of the metric object or null
* @return a thread-safe counter with given default values
*/
public Counter declareCounter(String name, Point boundDimensions) {
@@ -203,8 +206,7 @@ public class MetricReceiver {
/**
* Declare a gauge metric with any default position.
*
- * @param name
- * the name of the metric
+ * @param name the name of the metric
* @return a thread-safe gauge instance
*/
public Gauge declareGauge(String name) {
@@ -215,21 +217,12 @@ public class MetricReceiver {
* Declare a gauge metric, with default dimension values as given. Create
* the point argument by using a builder from {@link #pointBuilder()}.
*
- * @param name
- * the name of the metric
- * @param boundDimensions
- * dimensions which have a fixed value in the life cycle of the
- * metric object or null
+ * @param name the name of the metric
+ * @param boundDimensions dimensions which have a fixed value in the life cycle of the metric object or null
* @return a thread-safe gauge metric
*/
public Gauge declareGauge(String name, Point boundDimensions) {
- Optional<Point> optionalOfBoundDimensions;
- if (boundDimensions == null) {
- optionalOfBoundDimensions = Optional.empty();
- } else {
- optionalOfBoundDimensions = Optional.of(boundDimensions);
- }
- return declareGauge(name, optionalOfBoundDimensions, null);
+ return declareGauge(name, Optional.ofNullable(boundDimensions), null);
}
/**
@@ -238,13 +231,9 @@ public class MetricReceiver {
* MetricSettings instances are built using
* {@link MetricSettings.Builder}.
*
- * @param name
- * the name of the metric
- * @param boundDimensions
- * an optional of dimensions which have a fixed value in the life
- * cycle of the metric object
- * @param customSettings
- * any optional settings
+ * @param name the name of the metric
+ * @param boundDimensions an optional of dimensions which have a fixed value in the life cycle of the metric object
+ * @param customSettings any optional settings
* @return a thread-safe gauge metric
*/
public Gauge declareGauge(String name, Optional<Point> boundDimensions, MetricSettings customSettings) {
@@ -283,14 +272,8 @@ public class MetricReceiver {
/**
* Add how to build a histogram for a given metric.
*
- * <p>
- * Do note, this is not part of the public API.
- * </p>
- *
- * @param metricName
- * the metric where samples should be put in a histogram
- * @param definition
- * settings for a histogram
+ * @param metricName the metric where samples should be put in a histogram
+ * @param definition settings for a histogram
*/
void addMetricDefinition(String metricName, MetricSettings definition) {
synchronized (histogramDefinitionsLock) {
@@ -304,15 +287,9 @@ public class MetricReceiver {
}
/**
- * Get how to build a histogram for a given metric, or null if no histogram
- * should be created.
- *
- * <p>
- * Do note, this is not part of the public API.
- * </p>
+ * Get how to build a histogram for a given metric, or null if no histogram should be created.
*
- * @param metricName
- * the name of an arbitrary metric
+ * @param metricName the name of an arbitrary metric
* @return the corresponding histogram definition or null
*/
MetricSettings getMetricDefinition(String metricName) {
diff --git a/storage/src/tests/persistence/common/filestortestfixture.cpp b/storage/src/tests/persistence/common/filestortestfixture.cpp
index c6a0bdbb540..079abd6df06 100644
--- a/storage/src/tests/persistence/common/filestortestfixture.cpp
+++ b/storage/src/tests/persistence/common/filestortestfixture.cpp
@@ -2,6 +2,7 @@
#include <vespa/storage/persistence/messages.h>
#include <vespa/storage/persistence/filestorage/filestormanager.h>
+#include <vespa/storageapi/message/bucket.h>
#include <vespa/persistence/dummyimpl/dummypersistence.h>
#include <tests/persistence/common/filestortestfixture.h>
#include <vespa/document/repo/documenttyperepo.h>
@@ -15,7 +16,6 @@ using document::test::makeDocumentBucket;
namespace storage {
-spi::LoadType FileStorTestFixture::defaultLoadType = spi::LoadType(0, "default");
const uint32_t FileStorTestFixture::MSG_WAIT_TIME;
void
@@ -51,10 +51,8 @@ FileStorTestFixture::TearDown()
void
FileStorTestFixture::createBucket(const document::BucketId& bid)
{
- spi::Context context(defaultLoadType, spi::Priority(0),
- spi::Trace::TraceLevel(0));
- _node->getPersistenceProvider().createBucket(
- makeSpiBucket(bid), context);
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
+ _node->getPersistenceProvider().createBucket(makeSpiBucket(bid), context);
StorBucketDatabase::WrappedEntry entry(
_node->getStorageBucketDatabase().get(bid, "foo",
diff --git a/storage/src/tests/persistence/common/filestortestfixture.h b/storage/src/tests/persistence/common/filestortestfixture.h
index dcfeb42b4fd..69866f7bbf2 100644
--- a/storage/src/tests/persistence/common/filestortestfixture.h
+++ b/storage/src/tests/persistence/common/filestortestfixture.h
@@ -15,8 +15,6 @@ namespace storage {
class FileStorTestFixture : public ::testing::Test
{
public:
- static spi::LoadType defaultLoadType;
-
std::unique_ptr<TestServiceLayerApp> _node;
std::unique_ptr<vdstestlib::DirConfig> _config;
const document::DocumentType* _testdoctype1;
diff --git a/storage/src/tests/persistence/filestorage/filestormanagertest.cpp b/storage/src/tests/persistence/filestorage/filestormanagertest.cpp
index 7cfe7ea3761..3cac188cb17 100644
--- a/storage/src/tests/persistence/filestorage/filestormanagertest.cpp
+++ b/storage/src/tests/persistence/filestorage/filestormanagertest.cpp
@@ -56,7 +56,7 @@ namespace storage {
namespace {
-spi::LoadType defaultLoadType(0, "default");
+metrics::LoadType defaultLoadType(0, "default");
struct TestFileStorComponents;
@@ -86,7 +86,7 @@ struct FileStorTestBase : Test {
void TearDown() override;
void createBucket(document::BucketId bid) {
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
_node->getPersistenceProvider().createBucket(makeSpiBucket(bid), context);
StorBucketDatabase::WrappedEntry entry(
@@ -728,7 +728,7 @@ TEST_F(FileStorManagerTest, priority) {
for (uint32_t i=0; i<documents.size(); ++i) {
document::BucketId bucket(16, factory.getBucketId(documents[i]->getId()).getRawId());
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
_node->getPersistenceProvider().createBucket(makeSpiBucket(bucket), context);
}
@@ -790,8 +790,7 @@ TEST_F(FileStorManagerTest, split1) {
documents.push_back(doc);
}
document::BucketIdFactory factory;
- spi::Context context(defaultLoadType, spi::Priority(0),
- spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
{
// Populate bucket with the given data
for (uint32_t i=0; i<documents.size(); ++i) {
@@ -905,7 +904,7 @@ TEST_F(FileStorManagerTest, split_single_group) {
auto& top = c.top;
setClusterState("storage:2 distributor:1");
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
for (uint32_t j=0; j<1; ++j) {
// Test this twice, once where all the data ends up in file with
// splitbit set, and once where all the data ends up in file with
@@ -984,8 +983,7 @@ FileStorTestBase::putDoc(DummyStorageLink& top,
uint32_t docNum)
{
api::StorageMessageAddress address("storage", lib::NodeType::STORAGE, 3);
- spi::Context context(defaultLoadType, spi::Priority(0),
- spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
document::BucketIdFactory factory;
document::DocumentId docId(vespalib::make_string("id:ns:testdoctype1:n=%" PRIu64 ":%d", target.getId(), docNum));
document::BucketId bucket(16, factory.getBucketId(docId).getRawId());
diff --git a/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp b/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp
index 35aee60d30f..8b38083b33d 100644
--- a/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp
+++ b/storage/src/tests/persistence/filestorage/mergeblockingtest.cpp
@@ -2,7 +2,7 @@
#include <vespa/document/test/make_document_bucket.h>
#include <vespa/storage/persistence/messages.h>
-#include <tests/persistence/common/persistenceproviderwrapper.h>
+#include <vespa/storageapi/message/bucket.h>
#include <vespa/persistence/dummyimpl/dummypersistence.h>
#include <tests/persistence/common/filestortestfixture.h>
#include <vector>
diff --git a/storage/src/tests/persistence/filestorage/operationabortingtest.cpp b/storage/src/tests/persistence/filestorage/operationabortingtest.cpp
index eb7f2f883ab..95e2d8e2c43 100644
--- a/storage/src/tests/persistence/filestorage/operationabortingtest.cpp
+++ b/storage/src/tests/persistence/filestorage/operationabortingtest.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/storage/persistence/messages.h>
+#include <vespa/storageapi/message/bucket.h>
#include <tests/persistence/common/persistenceproviderwrapper.h>
#include <vespa/persistence/dummyimpl/dummypersistence.h>
#include <tests/persistence/common/filestortestfixture.h>
@@ -9,7 +10,6 @@
#include <vespa/vespalib/util/thread.h>
#include <vespa/vespalib/stllike/hash_set_insert.hpp>
#include <vespa/vespalib/gtest/gtest.h>
-#include <vespa/vespalib/util/time.h>
#include <thread>
#include <vespa/log/log.h>
@@ -72,7 +72,7 @@ public:
}
};
-spi::LoadType defaultLoadType(0, "default");
+metrics::LoadType defaultLoadType(0, "default");
}
diff --git a/storage/src/tests/persistence/mergehandlertest.cpp b/storage/src/tests/persistence/mergehandlertest.cpp
index 81f98136575..c1da8e9ec06 100644
--- a/storage/src/tests/persistence/mergehandlertest.cpp
+++ b/storage/src/tests/persistence/mergehandlertest.cpp
@@ -2,6 +2,7 @@
#include <vespa/document/base/testdocman.h>
#include <vespa/storage/persistence/mergehandler.h>
+#include <vespa/storage/persistence/filestorage/mergestatus.h>
#include <tests/persistence/persistencetestutils.h>
#include <tests/persistence/common/persistenceproviderwrapper.h>
#include <tests/common/message_sender_stub.h>
@@ -176,7 +177,7 @@ MergeHandlerTest::HandleApplyBucketDiffReplyInvoker::~HandleApplyBucketDiffReply
void
MergeHandlerTest::SetUp() {
- _context.reset(new spi::Context(documentapi::LoadType::DEFAULT, 0, 0));
+ _context = std::make_unique<spi::Context>(0, 0);
SingleDiskPersistenceTestUtils::SetUp();
_location = 1234;
@@ -1066,7 +1067,7 @@ TEST_F(MergeHandlerTest, apply_bucket_diff_reply_spi_failures) {
TEST_F(MergeHandlerTest, remove_from_diff) {
framework::defaultimplementation::FakeClock clock;
- MergeStatus status(clock, documentapi::LoadType::DEFAULT, 0, 0);
+ MergeStatus status(clock, 0, 0);
std::vector<api::GetBucketDiffCommand::Entry> diff(2);
diff[0]._timestamp = 1234;
diff --git a/storage/src/tests/persistence/persistencetestutils.cpp b/storage/src/tests/persistence/persistencetestutils.cpp
index 47e516a1dcc..278cb36b50c 100644
--- a/storage/src/tests/persistence/persistencetestutils.cpp
+++ b/storage/src/tests/persistence/persistencetestutils.cpp
@@ -26,26 +26,25 @@ namespace storage {
namespace {
- spi::LoadType defaultLoadType(0, "default");
-
- vdstestlib::DirConfig initialize(const std::string & rootOfRoot) {
- vdstestlib::DirConfig config(getStandardConfig(true, rootOfRoot));
- std::string rootFolder = getRootFolder(config);
- vespalib::rmdir(rootFolder, true);
- vespalib::mkdir(vespalib::make_string("%s/disks/d0", rootFolder.c_str()), true);
- return config;
- }
+vdstestlib::DirConfig initialize(const std::string & rootOfRoot) {
+ vdstestlib::DirConfig config(getStandardConfig(true, rootOfRoot));
+ std::string rootFolder = getRootFolder(config);
+ vespalib::rmdir(rootFolder, true);
+ vespalib::mkdir(vespalib::make_string("%s/disks/d0", rootFolder.c_str()), true);
+ return config;
+}
+
+template<typename T>
+struct ConfigReader : public T::Subscriber
+{
+ T config;
- template<typename T>
- struct ConfigReader : public T::Subscriber
- {
- T config;
+ ConfigReader(const std::string& configId) {
+ T::subscribe(configId, *this);
+ }
+ void configure(const T& c) { config = c; }
+};
- ConfigReader(const std::string& configId) {
- T::subscribe(configId, *this);
- }
- void configure(const T& c) { config = c; }
- };
}
PersistenceTestEnvironment::PersistenceTestEnvironment(const std::string & rootOfRoot)
@@ -156,7 +155,7 @@ PersistenceTestUtils::doPutOnDisk(
{
document::Document::SP doc(createRandomDocumentAtLocation(location, timestamp, minSize, maxSize));
spi::Bucket b(makeSpiBucket(document::BucketId(16, location)));
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
getPersistenceProvider().createBucket(b, context);
getPersistenceProvider().put(spi::Bucket(b), timestamp, doc, context);
return doc;
@@ -169,7 +168,7 @@ PersistenceTestUtils::doRemoveOnDisk(
spi::Timestamp timestamp,
bool persistRemove)
{
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
if (persistRemove) {
spi::RemoveResult result = getPersistenceProvider().removeIfFound(makeSpiBucket(bucketId),timestamp, docId, context);
return result.wasFound();
@@ -185,7 +184,7 @@ PersistenceTestUtils::doUnrevertableRemoveOnDisk(
const document::DocumentId& docId,
spi::Timestamp timestamp)
{
- spi::Context context(defaultLoadType, spi::Priority(0),spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0),spi::Trace::TraceLevel(0));
spi::RemoveResult result = getPersistenceProvider().remove(makeSpiBucket(bucketId), timestamp, docId, context);
return result.wasFound();
}
@@ -194,7 +193,7 @@ spi::GetResult
PersistenceTestUtils::doGetOnDisk(const document::BucketId& bucketId, const document::DocumentId& docId)
{
auto fieldSet = std::make_unique<document::AllFields>();
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
return getPersistenceProvider().get(makeSpiBucket(bucketId), *fieldSet, docId, context);
}
@@ -234,7 +233,7 @@ void
PersistenceTestUtils::doPut(const document::Document::SP& doc, document::BucketId bid, spi::Timestamp time)
{
spi::Bucket b(makeSpiBucket(bid));
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
getPersistenceProvider().createBucket(b, context);
getPersistenceProvider().put(b, time, std::move(doc), context);
}
@@ -244,7 +243,7 @@ PersistenceTestUtils::doUpdate(document::BucketId bid,
const document::DocumentUpdate::SP& update,
spi::Timestamp time)
{
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
return getPersistenceProvider().update(makeSpiBucket(bid), time, update, context);
}
@@ -255,7 +254,7 @@ PersistenceTestUtils::doRemove(const document::DocumentId& id, spi::Timestamp ti
document::BucketId bucket(
_env->_component.getBucketIdFactory().getBucketId(id));
bucket.setUsedBits(usedBits);
- spi::Context context(defaultLoadType, spi::Priority(0), spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
if (unrevertableRemove) {
getPersistenceProvider().remove(
makeSpiBucket(bucket), time, id, context);
diff --git a/storage/src/tests/persistence/persistencethread_splittest.cpp b/storage/src/tests/persistence/persistencethread_splittest.cpp
index 3dd8075176d..45b9f5aeaac 100644
--- a/storage/src/tests/persistence/persistencethread_splittest.cpp
+++ b/storage/src/tests/persistence/persistencethread_splittest.cpp
@@ -11,9 +11,6 @@ using document::test::makeDocumentBucket;
using namespace ::testing;
namespace storage {
-namespace {
-spi::LoadType defaultLoadType(0, "default");
-}
struct PersistenceThreadSplitTest : public SingleDiskPersistenceTestUtils {
enum SplitCase {
@@ -182,8 +179,7 @@ PersistenceThreadSplitTest::doTest(SplitCase splitCase)
uint64_t location = 0;
uint64_t splitMask = 1ULL << (splitLevelToDivide - 1);
- spi::Context context(defaultLoadType, spi::Priority(0),
- spi::Trace::TraceLevel(0));
+ spi::Context context(spi::Priority(0), spi::Trace::TraceLevel(0));
spi::Bucket bucket(makeSpiBucket(document::BucketId(currentSplitLevel, 1)));
spi::PersistenceProvider& spi(getPersistenceProvider());
spi.deleteBucket(bucket, context);
diff --git a/storage/src/tests/persistence/splitbitdetectortest.cpp b/storage/src/tests/persistence/splitbitdetectortest.cpp
index 69f8268ff2c..4982383685a 100644
--- a/storage/src/tests/persistence/splitbitdetectortest.cpp
+++ b/storage/src/tests/persistence/splitbitdetectortest.cpp
@@ -16,10 +16,6 @@ using namespace ::testing;
namespace storage {
-namespace {
-spi::LoadType defaultLoadType(0, "default");
-}
-
struct SplitBitDetectorTest : Test {
document::TestDocMan testDocMan;
spi::dummy::DummyPersistence provider;
@@ -30,8 +26,7 @@ struct SplitBitDetectorTest : Test {
: testDocMan(),
provider(testDocMan.getTypeRepoSP()),
bucket(makeSpiBucket(document::BucketId(1, 1))),
- context(defaultLoadType, spi::Priority(0),
- spi::Trace::TraceLevel(0))
+ context(spi::Priority(0), spi::Trace::TraceLevel(0))
{
provider.initialize();
provider.createBucket(bucket, context);
diff --git a/storage/src/tests/persistence/testandsettest.cpp b/storage/src/tests/persistence/testandsettest.cpp
index a952e227b70..4b509033d2e 100644
--- a/storage/src/tests/persistence/testandsettest.cpp
+++ b/storage/src/tests/persistence/testandsettest.cpp
@@ -41,7 +41,7 @@ struct TestAndSetTest : SingleDiskPersistenceTestUtils {
TestAndSetTest()
: persistenceHandler(),
asyncHandler(nullptr),
- context(spi::LoadType(0, "default"), 0, 0)
+ context(0, 0)
{}
void SetUp() override {
diff --git a/storage/src/tests/storageserver/documentapiconvertertest.cpp b/storage/src/tests/storageserver/documentapiconvertertest.cpp
index bc52d7508dc..a1293a842c1 100644
--- a/storage/src/tests/storageserver/documentapiconvertertest.cpp
+++ b/storage/src/tests/storageserver/documentapiconvertertest.cpp
@@ -10,6 +10,7 @@
#include <vespa/document/update/documentupdate.h>
#include <vespa/documentapi/documentapi.h>
#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/error.h>
#include <vespa/storage/common/bucket_resolver.h>
#include <vespa/storage/storageserver/documentapiconverter.h>
#include <vespa/storageapi/message/datagram.h>
diff --git a/storage/src/tests/visiting/memory_bounded_trace_test.cpp b/storage/src/tests/visiting/memory_bounded_trace_test.cpp
index 543531ef55a..e43fa702fb5 100644
--- a/storage/src/tests/visiting/memory_bounded_trace_test.cpp
+++ b/storage/src/tests/visiting/memory_bounded_trace_test.cpp
@@ -32,12 +32,12 @@ TEST(MemoryBoundedTraceTest, memory_used_is_accumulated_recursively_for_non_leaf
TEST(MemoryBoundedTraceTest, trace_nodes_can_be_moved_and_implicitly_cleared) {
MemoryBoundedTrace trace(100);
EXPECT_TRUE(trace.add(mbus::TraceNode("hello world", epoch)));
- mbus::TraceNode target;
+ mbus::Trace target;
trace.moveTraceTo(target);
EXPECT_EQ(1, target.getNumChildren());
EXPECT_EQ(0, trace.getApproxMemoryUsed());
- mbus::TraceNode emptinessCheck;
+ mbus::Trace emptinessCheck;
trace.moveTraceTo(emptinessCheck);
EXPECT_EQ(0, emptinessCheck.getNumChildren());
}
@@ -52,7 +52,7 @@ TEST(MemoryBoundedTraceTest, trace_nodes_can_be_moved_and_implicitly_cleared) {
TEST(MemoryBoundedTraceTest, moved_trace_tree_is_marked_as_strict) {
MemoryBoundedTrace trace(100);
EXPECT_TRUE(trace.add(mbus::TraceNode("hello world", epoch)));
- mbus::TraceNode target;
+ mbus::Trace target;
trace.moveTraceTo(target);
EXPECT_EQ(1, target.getNumChildren());
EXPECT_TRUE(target.getChild(0).isStrict());
@@ -69,7 +69,7 @@ TEST(MemoryBoundedTraceTest, can_not_add_more_nodes_when_memory_used_exceeds_upp
"the freeway", epoch)));
EXPECT_EQ(11, trace.getApproxMemoryUsed());
- mbus::TraceNode target;
+ mbus::Trace target;
trace.moveTraceTo(target);
// Twice nested node (root -> added trace tree -> leaf with txt).
EXPECT_EQ(1, target.getNumChildren());
@@ -82,7 +82,7 @@ TEST(MemoryBoundedTraceTest, moved_tree_includes_stats_node_when_nodes_omitted)
EXPECT_TRUE(trace.add(mbus::TraceNode("abcdef", epoch)));
EXPECT_FALSE(trace.add(mbus::TraceNode("ghijkjlmn", epoch)));
- mbus::TraceNode target;
+ mbus::Trace target;
trace.moveTraceTo(target);
EXPECT_EQ(1, target.getNumChildren());
EXPECT_EQ(2, target.getChild(0).getNumChildren());
diff --git a/storage/src/tests/visiting/visitormanagertest.cpp b/storage/src/tests/visiting/visitormanagertest.cpp
index ed54565c3c7..7943790f13d 100644
--- a/storage/src/tests/visiting/visitormanagertest.cpp
+++ b/storage/src/tests/visiting/visitormanagertest.cpp
@@ -3,9 +3,9 @@
#include <vespa/document/datatype/datatype.h>
#include <vespa/document/fieldvalue/intfieldvalue.h>
#include <vespa/document/fieldvalue/stringfieldvalue.h>
-#include <vespa/document/fieldvalue/rawfieldvalue.h>
#include <vespa/storageapi/message/datagram.h>
#include <vespa/storageapi/message/persistence.h>
+#include <vespa/storageapi/message/bucket.h>
#include <vespa/storage/persistence/filestorage/filestormanager.h>
#include <vespa/storage/visiting/visitormanager.h>
#include <vespa/storageframework/defaultimplementation/clock/realclock.h>
diff --git a/storage/src/tests/visiting/visitortest.cpp b/storage/src/tests/visiting/visitortest.cpp
index 72668564345..f727cdf8eb2 100644
--- a/storage/src/tests/visiting/visitortest.cpp
+++ b/storage/src/tests/visiting/visitortest.cpp
@@ -799,15 +799,16 @@ TEST_F(VisitorTest, no_mbus_tracing_if_trace_level_is_zero) {
cmd->getTrace().setLevel(0);
std::shared_ptr<api::CreateVisitorReply> reply;
ASSERT_NO_FATAL_FAILURE(doCompleteVisitingSession(cmd, reply));
- EXPECT_TRUE(reply->getTrace().getRoot().isEmpty());
+ EXPECT_TRUE(reply->getTrace().isEmpty());
}
TEST_F(VisitorTest, reply_contains_trace_if_trace_level_above_zero) {
std::shared_ptr<api::CreateVisitorCommand> cmd(makeCreateVisitor());
cmd->getTrace().setLevel(1);
+ cmd->getTrace().trace(1,"at least one trace.");
std::shared_ptr<api::CreateVisitorReply> reply;
ASSERT_NO_FATAL_FAILURE(doCompleteVisitingSession(cmd, reply));
- EXPECT_FALSE(reply->getTrace().getRoot().isEmpty());
+ EXPECT_FALSE(reply->getTrace().isEmpty());
}
TEST_F(VisitorTest, no_more_iterators_sent_while_memory_used_above_limit) {
diff --git a/storage/src/vespa/storage/config/stor-communicationmanager.def b/storage/src/vespa/storage/config/stor-communicationmanager.def
index adb50465adc..e075f57514b 100644
--- a/storage/src/vespa/storage/config/stor-communicationmanager.def
+++ b/storage/src/vespa/storage/config/stor-communicationmanager.def
@@ -61,7 +61,7 @@ skip_thread bool default=false
use_direct_storageapi_rpc bool default=false
## The number of network (FNET) threads used by the shared rpc resource.
-rpc.num_network_threads int default=1
+rpc.num_network_threads int default=2
## The number of (FNET) RPC targets to use per node in the cluster.
##
diff --git a/storage/src/vespa/storage/distributor/bucketdbupdater.h b/storage/src/vespa/storage/distributor/bucketdbupdater.h
index b756c5ca7bb..7f2a9b30a33 100644
--- a/storage/src/vespa/storage/distributor/bucketdbupdater.h
+++ b/storage/src/vespa/storage/distributor/bucketdbupdater.h
@@ -9,7 +9,6 @@
#include "operation_routing_snapshot.h"
#include "outdated_nodes_map.h"
#include <vespa/document/bucket/bucket.h>
-#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/storageapi/message/bucket.h>
#include <vespa/vdslib/state/clusterstate.h>
#include <vespa/storage/common/storagelink.h>
diff --git a/storage/src/vespa/storage/distributor/operations/external/getoperation.cpp b/storage/src/vespa/storage/distributor/operations/external/getoperation.cpp
index c4fb9b8228c..c72e5731a59 100644
--- a/storage/src/vespa/storage/distributor/operations/external/getoperation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/external/getoperation.cpp
@@ -144,9 +144,7 @@ GetOperation::onReceive(DistributorMessageSender& sender, const std::shared_ptr<
LOG(debug, "Received %s", msg->toString(true).c_str());
- if ( ! getreply->getTrace().getRoot().isEmpty()) {
- _msg->getTrace().getRoot().addChild(getreply->getTrace().getRoot());
- }
+ _msg->getTrace().addChild(getreply->steal_trace());
bool allDone = true;
for (auto& response : _responses) {
for (uint32_t i = 0; i < response.second.size(); i++) {
diff --git a/storage/src/vespa/storage/distributor/operations/external/getoperation.h b/storage/src/vespa/storage/distributor/operations/external/getoperation.h
index 57e878d9e40..546cf7a7543 100644
--- a/storage/src/vespa/storage/distributor/operations/external/getoperation.h
+++ b/storage/src/vespa/storage/distributor/operations/external/getoperation.h
@@ -6,6 +6,7 @@
#include <vespa/storage/distributor/operations/operation.h>
#include <vespa/storage/bucketdb/bucketdatabase.h>
#include <vespa/storageapi/messageapi/storagemessage.h>
+#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/storageframework/generic/clock/timer.h>
#include <optional>
diff --git a/storage/src/vespa/storage/distributor/operations/external/putoperation.h b/storage/src/vespa/storage/distributor/operations/external/putoperation.h
index 79b3dd74b82..61149839ed1 100644
--- a/storage/src/vespa/storage/distributor/operations/external/putoperation.h
+++ b/storage/src/vespa/storage/distributor/operations/external/putoperation.h
@@ -3,7 +3,6 @@
#pragma once
#include <vespa/storage/distributor/operations/sequenced_operation.h>
-#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/storage/distributor/persistencemessagetracker.h>
namespace document {
diff --git a/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.cpp b/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.cpp
index 3866ee4e6f7..d7dd0be7f4f 100644
--- a/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.cpp
@@ -27,21 +27,22 @@ TwoPhaseUpdateOperation::TwoPhaseUpdateOperation(
DistributorMetricSet& metrics,
SequencingHandle sequencingHandle)
: SequencedOperation(std::move(sequencingHandle)),
- _updateMetric(metrics.updates[msg->getLoadType()]),
- _putMetric(metrics.update_puts[msg->getLoadType()]),
- _getMetric(metrics.update_gets[msg->getLoadType()]),
- _metadata_get_metrics(metrics.update_metadata_gets[msg->getLoadType()]),
- _updateCmd(std::move(msg)),
- _updateReply(),
- _manager(manager),
- _bucketSpace(bucketSpace),
- _sendState(SendState::NONE_SENT),
- _mode(Mode::FAST_PATH),
- _single_get_latency_timer(),
- _fast_path_repair_source_node(0xffff),
- _use_initial_cheap_metadata_fetch_phase(
+ _updateMetric(metrics.updates[msg->getLoadType()]),
+ _putMetric(metrics.update_puts[msg->getLoadType()]),
+ _getMetric(metrics.update_gets[msg->getLoadType()]),
+ _metadata_get_metrics(metrics.update_metadata_gets[msg->getLoadType()]),
+ _updateCmd(std::move(msg)),
+ _updateReply(),
+ _manager(manager),
+ _bucketSpace(bucketSpace),
+ _sendState(SendState::NONE_SENT),
+ _mode(Mode::FAST_PATH),
+ _trace(_updateCmd->getTrace().getLevel()),
+ _single_get_latency_timer(),
+ _fast_path_repair_source_node(0xffff),
+ _use_initial_cheap_metadata_fetch_phase(
_manager.getDistributor().getConfig().enable_metadata_only_fetch_phase_for_inconsistent_updates()),
- _replySent(false)
+ _replySent(false)
{
document::BucketIdFactory idFactory;
_updateDocBucketId = idFactory.getBucketId(_updateCmd->getDocumentId());
@@ -132,9 +133,7 @@ TwoPhaseUpdateOperation::sendReply(
std::shared_ptr<api::StorageReply>& reply)
{
assert(!_replySent);
- if (!_trace.isEmpty()) {
- reply->getTrace().getRoot().addChild(_trace);
- }
+ reply->getTrace().addChild(std::move(_trace));
sender.sendReply(reply);
_replySent = true;
}
@@ -645,11 +644,9 @@ TwoPhaseUpdateOperation::satisfiesUpdateTimestampConstraint(api::Timestamp ts) c
}
void
-TwoPhaseUpdateOperation::addTraceFromReply(const api::StorageReply& reply)
+TwoPhaseUpdateOperation::addTraceFromReply(api::StorageReply & reply)
{
- if ( ! reply.getTrace().getRoot().isEmpty()) {
- _trace.addChild(reply.getTrace().getRoot());
- }
+ _trace.addChild(reply.steal_trace());
}
void
diff --git a/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.h b/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.h
index 2d8f3e8494d..a98fbe98c38 100644
--- a/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.h
+++ b/storage/src/vespa/storage/distributor/operations/external/twophaseupdateoperation.h
@@ -2,7 +2,6 @@
#pragma once
#include "newest_replica.h"
-#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/storage/distributor/persistencemessagetracker.h>
#include <vespa/storage/distributor/operations/sequenced_operation.h>
#include <vespa/document/update/documentupdate.h>
@@ -18,6 +17,7 @@ namespace storage {
namespace api {
class UpdateCommand;
class CreateBucketReply;
+class ReturnCode;
}
class UpdateMetricSet;
@@ -125,7 +125,7 @@ private:
DistributorMessageSender& sender,
const document::Document& candidateDoc);
bool satisfiesUpdateTimestampConstraint(api::Timestamp) const;
- void addTraceFromReply(const api::StorageReply& reply);
+ void addTraceFromReply(api::StorageReply& reply);
bool hasTasCondition() const noexcept;
void replyWithTasFailure(DistributorMessageSender& sender,
vespalib::stringref message);
@@ -146,7 +146,7 @@ private:
SentMessageMap _sentMessageMap;
SendState _sendState;
Mode _mode;
- mbus::TraceNode _trace;
+ mbus::Trace _trace;
document::BucketId _updateDocBucketId;
std::vector<std::pair<document::BucketId, uint16_t>> _replicas_at_get_send_time;
std::optional<framework::MilliSecTimer> _single_get_latency_timer;
diff --git a/storage/src/vespa/storage/distributor/operations/external/updateoperation.h b/storage/src/vespa/storage/distributor/operations/external/updateoperation.h
index 9d9bce9160b..2e69a52a644 100644
--- a/storage/src/vespa/storage/distributor/operations/external/updateoperation.h
+++ b/storage/src/vespa/storage/distributor/operations/external/updateoperation.h
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/storage/distributor/persistencemessagetracker.h>
namespace document {
diff --git a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp
index 5a03e05d563..0868f18e88a 100644
--- a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.cpp
@@ -161,7 +161,7 @@ VisitorOperation::markOperationAsFailedDueToNodeError(
result.getResult(),
vespalib::make_string("[from content node %u] %s",
fromFailingNodeIndex,
- result.getMessage().c_str()));
+ vespalib::string(result.getMessage()).c_str()));
}
void
@@ -171,7 +171,7 @@ VisitorOperation::onReceive(
{
api::CreateVisitorReply& reply = static_cast<api::CreateVisitorReply&>(*r);
- _trace.add(reply.getTrace().getRoot());
+ _trace.add(reply.steal_trace());
SentMessagesMap::iterator iter = _sentMessages.find(reply.getMsgId());
assert(iter != _sentMessages.end());
@@ -828,8 +828,8 @@ VisitorOperation::sendReply(const api::ReturnCode& code, DistributorMessageSende
{
if (!_sentReply) {
// Send create visitor reply
- api::CreateVisitorReply::SP reply(new api::CreateVisitorReply(*_msg));
- _trace.moveTraceTo(reply->getTrace().getRoot());
+ auto reply = std::make_shared<api::CreateVisitorReply>(*_msg);
+ _trace.moveTraceTo(reply->getTrace());
reply->setLastBucket(getLastBucketVisited());
reply->setResult(code);
diff --git a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h
index 42b0bd56b9e..24c7157b481 100644
--- a/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h
+++ b/storage/src/vespa/storage/distributor/operations/external/visitoroperation.h
@@ -38,7 +38,7 @@ public:
const Config& config,
VisitorMetricSet& metrics);
- ~VisitorOperation();
+ ~VisitorOperation() override;
void onClose(DistributorMessageSender& sender) override;
void onStart(DistributorMessageSender& sender) override;
diff --git a/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp b/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp
index 52c8344b820..bc77f4cf88f 100644
--- a/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/idealstate/idealstateoperation.cpp
@@ -105,7 +105,6 @@ IdealStateOperation::setCommandMeta(api::MaintenanceCommand& cmd) const
{
cmd.setPriority(_priority);
cmd.setReason(_detailedReason);
- cmd.setLoadType((*_manager->getLoadTypes())["maintenance"]);
}
std::string
diff --git a/storage/src/vespa/storage/distributor/operations/idealstate/removebucketoperation.cpp b/storage/src/vespa/storage/distributor/operations/idealstate/removebucketoperation.cpp
index 9a94a5a62ad..3bd2406cd85 100644
--- a/storage/src/vespa/storage/distributor/operations/idealstate/removebucketoperation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/idealstate/removebucketoperation.cpp
@@ -78,7 +78,7 @@ RemoveBucketOperation::onReceiveInternal(const std::shared_ptr<api::StorageReply
"%s on node %u: %s. Reinserting node into bucket db with %s",
getBucketId().toString().c_str(),
node,
- rep->getResult().getMessage().c_str(),
+ vespalib::string(rep->getResult().getMessage()).c_str(),
rep->getBucketInfo().toString().c_str());
_manager->getDistributorComponent().updateBucketDatabase(
@@ -104,8 +104,7 @@ RemoveBucketOperation::onReceiveInternal(const std::shared_ptr<api::StorageReply
void
-RemoveBucketOperation::onReceive(DistributorMessageSender&,
- const std::shared_ptr<api::StorageReply> &msg)
+RemoveBucketOperation::onReceive(DistributorMessageSender&, const std::shared_ptr<api::StorageReply> &msg)
{
if (onReceiveInternal(msg)) {
done();
diff --git a/storage/src/vespa/storage/distributor/operations/idealstate/splitoperation.cpp b/storage/src/vespa/storage/distributor/operations/idealstate/splitoperation.cpp
index 57f8bc92316..508dcf13916 100644
--- a/storage/src/vespa/storage/distributor/operations/idealstate/splitoperation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/idealstate/splitoperation.cpp
@@ -121,7 +121,7 @@ SplitOperation::onReceive(DistributorMessageSender&, const api::StorageReply::SP
LOGBP(debug, "Split failed for %s: bucket not found. Storage and "
"distributor bucket databases might be out of sync: %s",
getBucketId().toString().c_str(),
- rep.getResult().getMessage().c_str());
+ vespalib::string(rep.getResult().getMessage()).c_str());
_ok = false;
} else if (rep.getResult().isBusy()) {
LOG(debug, "Split failed for %s, node was busy. Will retry later",
diff --git a/storage/src/vespa/storage/distributor/operations/operation.cpp b/storage/src/vespa/storage/distributor/operations/operation.cpp
index 9e9e3f8a60a..ee695dae606 100644
--- a/storage/src/vespa/storage/distributor/operations/operation.cpp
+++ b/storage/src/vespa/storage/distributor/operations/operation.cpp
@@ -2,7 +2,6 @@
#include "operation.h"
#include <vespa/storage/common/distributorcomponent.h>
-#include <vespa/storageapi/messageapi/storagemessage.h>
#include <vespa/storageapi/messageapi/storagecommand.h>
#include <vespa/storageapi/messageapi/storagereply.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -17,9 +16,7 @@ Operation::Operation()
{
}
-Operation::~Operation()
-{
-}
+Operation::~Operation() = default;
std::string
Operation::getStatus() const
@@ -42,7 +39,6 @@ Operation::copyMessageSettings(const api::StorageCommand& source, api::StorageCo
target.getTrace().setLevel(source.getTrace().getLevel());
target.setTimeout(source.getTimeout());
target.setPriority(source.getPriority());
- target.setLoadType(source.getLoadType());
}
}
diff --git a/storage/src/vespa/storage/distributor/operations/operation.h b/storage/src/vespa/storage/distributor/operations/operation.h
index 5f4dd63c2f6..538f15d2bf2 100644
--- a/storage/src/vespa/storage/distributor/operations/operation.h
+++ b/storage/src/vespa/storage/distributor/operations/operation.h
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/vdslib/state/nodetype.h>
#include <vespa/storage/distributor/distributormessagesender.h>
#include <vespa/storageframework/generic/clock/time.h>
diff --git a/storage/src/vespa/storage/distributor/pendingmessagetracker.h b/storage/src/vespa/storage/distributor/pendingmessagetracker.h
index 275eba7f20c..2dba244dc9f 100644
--- a/storage/src/vespa/storage/distributor/pendingmessagetracker.h
+++ b/storage/src/vespa/storage/distributor/pendingmessagetracker.h
@@ -6,7 +6,6 @@
#include <vespa/storageframework/generic/status/htmlstatusreporter.h>
#include <vespa/storageframework/generic/component/componentregister.h>
#include <vespa/storageframework/generic/component/component.h>
-#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/storageapi/message/bucket.h>
#include <vespa/vespalib/stllike/hash_set.h>
#include <boost/multi_index_container.hpp>
diff --git a/storage/src/vespa/storage/distributor/persistencemessagetracker.cpp b/storage/src/vespa/storage/distributor/persistencemessagetracker.cpp
index 6362db6c002..584cb379400 100644
--- a/storage/src/vespa/storage/distributor/persistencemessagetracker.cpp
+++ b/storage/src/vespa/storage/distributor/persistencemessagetracker.cpp
@@ -22,6 +22,7 @@ PersistenceMessageTrackerImpl::PersistenceMessageTrackerImpl(
_reply(std::move(reply)),
_manager(link),
_revertTimestamp(revertTimestamp),
+ _trace(_reply->getTrace().getLevel()),
_requestTimer(link.getClock()),
_n_persistence_replies_total(0),
_n_successful_persistence_replies(0),
@@ -163,25 +164,18 @@ PersistenceMessageTrackerImpl::addBucketInfoFromReply(
bucket.toString().c_str(),
bucketInfo.toString().c_str(),
node);
- _remapBucketInfo[bucket].push_back(
- BucketCopy(_manager.getUniqueTimestamp(),
- node,
- bucketInfo));
+ _remapBucketInfo[bucket].emplace_back(_manager.getUniqueTimestamp(), node, bucketInfo);
} else {
LOG(debug, "Bucket %s: Received bucket info %s from node %d",
bucket.toString().c_str(),
bucketInfo.toString().c_str(),
node);
- _bucketInfo[bucket].push_back(
- BucketCopy(_manager.getUniqueTimestamp(),
- node,
- bucketInfo));
+ _bucketInfo[bucket].emplace_back(_manager.getUniqueTimestamp(), node, bucketInfo);
}
}
void
-PersistenceMessageTrackerImpl::logSuccessfulReply(uint16_t node,
- const api::BucketInfoReply& reply) const
+PersistenceMessageTrackerImpl::logSuccessfulReply(uint16_t node, const api::BucketInfoReply& reply) const
{
LOG(spam, "Bucket %s: Received successful reply %s",
reply.getBucketId().toString().c_str(),
@@ -230,7 +224,7 @@ PersistenceMessageTrackerImpl::sendReply(MessageSender& sender)
updateMetrics();
_trace.setStrict(false);
if ( ! _trace.isEmpty()) {
- _reply->getTrace().getRoot().addChild(_trace);
+ _reply->getTrace().addChild(std::move(_trace));
}
sender.sendReply(_reply);
@@ -292,9 +286,7 @@ PersistenceMessageTrackerImpl::updateFromReply(
api::BucketInfoReply& reply,
uint16_t node)
{
- if ( ! reply.getTrace().getRoot().isEmpty()) {
- _trace.addChild(reply.getTrace().getRoot());
- }
+ _trace.addChild(reply.steal_trace());
if (reply.getType() == api::MessageType::CREATEBUCKET_REPLY) {
handleCreateBucketReply(reply, node);
diff --git a/storage/src/vespa/storage/distributor/persistencemessagetracker.h b/storage/src/vespa/storage/distributor/persistencemessagetracker.h
index 4392aa1fc30..cf9f4017eda 100644
--- a/storage/src/vespa/storage/distributor/persistencemessagetracker.h
+++ b/storage/src/vespa/storage/distributor/persistencemessagetracker.h
@@ -73,7 +73,7 @@ private:
DistributorComponent& _manager;
api::Timestamp _revertTimestamp;
std::vector<BucketNodePair> _revertNodes;
- mbus::TraceNode _trace;
+ mbus::Trace _trace;
framework::MilliSecTimer _requestTimer;
uint32_t _n_persistence_replies_total;
uint32_t _n_successful_persistence_replies;
diff --git a/storage/src/vespa/storage/distributor/sentmessagemap.cpp b/storage/src/vespa/storage/distributor/sentmessagemap.cpp
index d74f52b2f86..6f1db429b97 100644
--- a/storage/src/vespa/storage/distributor/sentmessagemap.cpp
+++ b/storage/src/vespa/storage/distributor/sentmessagemap.cpp
@@ -3,6 +3,7 @@
#include "sentmessagemap.h"
#include <vespa/storage/distributor/operations/operation.h>
#include <sstream>
+#include <set>
#include <vespa/log/log.h>
LOG_SETUP(".distributor.callback.map");
@@ -14,15 +15,13 @@ SentMessageMap::SentMessageMap()
{
}
-SentMessageMap::~SentMessageMap()
-{
-}
+SentMessageMap::~SentMessageMap() = default;
std::shared_ptr<Operation>
SentMessageMap::pop()
{
- std::map<api::StorageMessage::Id, std::shared_ptr<Operation> >::iterator found = _map.begin();
+ auto found = _map.begin();
if (found != _map.end()) {
std::shared_ptr<Operation> retVal = found->second;
@@ -36,11 +35,10 @@ SentMessageMap::pop()
std::shared_ptr<Operation>
SentMessageMap::pop(api::StorageMessage::Id id)
{
- std::map<api::StorageMessage::Id, std::shared_ptr<Operation> >::iterator found = _map.find(id);
+ auto found = _map.find(id);
if (found != _map.end()) {
- LOG(spam, "Found Id %" PRIu64 " in callback map: %p", id,
- found->second.get());
+ LOG(spam, "Found Id %" PRIu64 " in callback map: %p", id, found->second.get());
std::shared_ptr<Operation> retVal = found->second;
_map.erase(found);
@@ -55,8 +53,7 @@ SentMessageMap::pop(api::StorageMessage::Id id)
void
SentMessageMap::insert(api::StorageMessage::Id id, const std::shared_ptr<Operation> & callback)
{
- LOG(spam, "Inserting callback %p for message %" PRIu64 "",
- callback.get(), id);
+ LOG(spam, "Inserting callback %p for message %" PRIu64 "", callback.get(), id);
_map[id] = callback;
}
@@ -67,17 +64,11 @@ SentMessageMap::toString() const
std::ostringstream ost;
std::set<std::string> messages;
- for (Map::const_iterator iter = _map.begin();
- iter != _map.end();
- ++iter)
- {
- messages.insert(iter->second.get()->toString());
+ for (const auto & entry : _map) {
+ messages.insert(entry.second.get()->toString());
}
- for (std::set<std::string>::const_iterator
- it(messages.begin()), e(messages.end());
- it != e; ++it)
- {
- ost << *it << "\n";
+ for (const auto & message : messages) {
+ ost << message << "\n";
}
return ost.str();
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h b/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h
index aafc87aa84f..8b031af4b69 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h
+++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandler.h
@@ -13,10 +13,10 @@
#pragma once
-#include "mergestatus.h"
#include <vespa/document/bucket/bucket.h>
#include <vespa/storage/storageutil/resumeguard.h>
#include <vespa/storage/common/messagesender.h>
+#include <vespa/storageapi/messageapi/storagemessage.h>
namespace storage {
namespace api {
@@ -34,6 +34,7 @@ struct FileStorMetrics;
struct MessageSender;
struct ServiceLayerComponentRegister;
class AbortBucketOperationsCommand;
+class MergeStatus;
class FileStorHandler : public MessageSender {
public:
@@ -220,7 +221,7 @@ public:
/**
* Add a new merge state to the registry.
*/
- virtual void addMergeStatus(const document::Bucket&, MergeStatus::SP) = 0;
+ virtual void addMergeStatus(const document::Bucket&, std::shared_ptr<MergeStatus>) = 0;
/**
* Returns the reference to the current merge status for the given bucket.
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp
index 01f44d2df42..5a7a598fb4c 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp
@@ -2,6 +2,7 @@
#include "filestorhandlerimpl.h"
#include "filestormetrics.h"
+#include "mergestatus.h"
#include <vespa/storageapi/message/bucketsplitting.h>
#include <vespa/storageapi/message/persistence.h>
#include <vespa/storageapi/message/removelocation.h>
@@ -77,7 +78,7 @@ FileStorHandlerImpl::FileStorHandlerImpl(uint32_t numThreads, uint32_t numStripe
FileStorHandlerImpl::~FileStorHandlerImpl() = default;
void
-FileStorHandlerImpl::addMergeStatus(const document::Bucket& bucket, MergeStatus::SP status)
+FileStorHandlerImpl::addMergeStatus(const document::Bucket& bucket, std::shared_ptr<MergeStatus> status)
{
std::lock_guard mlock(_mergeStatesLock);
if (_mergeStates.find(bucket) != _mergeStates.end()) {
@@ -90,7 +91,7 @@ MergeStatus&
FileStorHandlerImpl::editMergeStatus(const document::Bucket& bucket)
{
std::lock_guard mlock(_mergeStatesLock);
- MergeStatus::SP status = _mergeStates[bucket];
+ std::shared_ptr<MergeStatus> status = _mergeStates[bucket];
if ( ! status ) {
throw vespalib::IllegalStateException("No merge state exist for " + bucket.toString(), VESPA_STRLOC);
}
@@ -140,7 +141,7 @@ FileStorHandlerImpl::clearMergeStatus(const document::Bucket& bucket, const api:
return;
}
if (code != 0) {
- MergeStatus::SP statusPtr(it->second);
+ std::shared_ptr<MergeStatus> statusPtr(it->second);
assert(statusPtr.get());
MergeStatus& status(*statusPtr);
if (status.reply.get()) {
@@ -325,12 +326,9 @@ FileStorHandlerImpl::updateMetrics(const MetricLockGuard &)
_metrics->pendingMerges.addValue(_mergeStates.size());
_metrics->queueSize.addValue(getQueueSize());
- for (auto & entry : _metrics->averageQueueWaitingTime.getMetricMap()) {
- metrics::LoadType loadType(entry.first, "ignored");
- for (const auto & stripe : _metrics->stripes) {
- const auto & m = stripe->averageQueueWaitingTime[loadType];
- entry.second->addTotalValueWithCount(m.getTotal(), m.getCount());
- }
+ for (const auto & stripe : _metrics->stripes) {
+ const auto & m = stripe->averageQueueWaitingTime;
+ _metrics->averageQueueWaitingTime.addTotalValueWithCount(m.getTotal(), m.getCount());
}
}
@@ -944,8 +942,7 @@ FileStorHandlerImpl::Stripe::get_next_async_message(monitor_guard& guard)
FileStorHandler::LockedMessage
FileStorHandlerImpl::Stripe::getMessage(monitor_guard & guard, PriorityIdx & idx, PriorityIdx::iterator iter) {
- api::StorageMessage & m(*iter->_command);
- std::chrono::milliseconds waitTime(uint64_t(iter->_timer.stop(_metrics->averageQueueWaitingTime[m.getLoadType()])));
+ std::chrono::milliseconds waitTime(uint64_t(iter->_timer.stop(_metrics->averageQueueWaitingTime)));
std::shared_ptr<api::StorageMessage> msg = std::move(iter->_command);
document::Bucket bucket(iter->_bucket);
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h
index 549de164229..b3d4ff0c730 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h
+++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.h
@@ -16,11 +16,11 @@
#pragma once
#include "filestorhandler.h"
-#include "mergestatus.h"
#include <vespa/document/bucket/bucketid.h>
#include <vespa/metrics/metrics.h>
#include <vespa/storage/common/servicelayercomponent.h>
#include <vespa/storageframework/generic/metric/metricupdatehook.h>
+#include <vespa/storageapi/messageapi/storagereply.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
@@ -218,7 +218,7 @@ public:
return stripe(bucket).lock(bucket, lockReq);
}
- void addMergeStatus(const document::Bucket&, MergeStatus::SP) override;
+ void addMergeStatus(const document::Bucket&, std::shared_ptr<MergeStatus>) override;
MergeStatus& editMergeStatus(const document::Bucket&) override;
bool isMerging(const document::Bucket&) const override;
uint32_t getNumActiveMerges() const override;
@@ -242,7 +242,7 @@ private:
MessageSender& _messageSender;
const document::BucketIdFactory& _bucketIdFactory;
mutable std::mutex _mergeStatesLock;
- std::map<document::Bucket, MergeStatus::SP> _mergeStates;
+ std::map<document::Bucket, std::shared_ptr<MergeStatus>> _mergeStates;
vespalib::duration _getNextMessageTimeout;
const uint32_t _max_active_merges_per_stripe; // Read concurrently by stripes.
mutable std::mutex _pauseMonitor;
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
index cba3969cd68..c1c412fefeb 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
@@ -673,10 +673,10 @@ FileStorManager::onInternal(const shared_ptr<api::InternalCommand>& msg)
}
case DestroyIteratorCommand::ID:
{
- spi::Context context(msg->getLoadType(), msg->getPriority(), msg->getTrace().getLevel());
+ spi::Context context(msg->getPriority(), msg->getTrace().getLevel());
shared_ptr<DestroyIteratorCommand> cmd(std::static_pointer_cast<DestroyIteratorCommand>(msg));
_provider->destroyIterator(cmd->getIteratorId(), context);
- msg->getTrace().getRoot().addChild(context.getTrace().getRoot());
+ msg->getTrace().addChild(context.steal_trace());
return true;
}
case ReadBucketList::ID:
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.h b/storage/src/vespa/storage/persistence/filestorage/filestormanager.h
index 2953462dd1e..7abb41f4741 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.h
+++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.h
@@ -34,6 +34,7 @@ namespace storage {
namespace api {
class ReturnCode;
class StorageReply;
+ class BucketCommand;
}
struct FileStorManagerTest;
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormetrics.cpp b/storage/src/vespa/storage/persistence/filestorage/filestormetrics.cpp
index ed42f1932eb..996e3bfe515 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestormetrics.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/filestormetrics.cpp
@@ -189,27 +189,19 @@ FileStorThreadMetrics::FileStorThreadMetrics(const std::string& name, const std:
FileStorThreadMetrics::~FileStorThreadMetrics() = default;
-FileStorStripeMetrics::FileStorStripeMetrics(const std::string& name, const std::string& description,
- const LoadTypeSet& loadTypes)
+FileStorStripeMetrics::FileStorStripeMetrics(const std::string& name, const std::string& description)
: MetricSet(name, {{"partofsum"}}, description),
- averageQueueWaitingTime(loadTypes,
- metrics::DoubleAverageMetric("averagequeuewait", {},
- "Average time an operation spends in input queue."),
- this)
+ averageQueueWaitingTime("averagequeuewait", {}, "Average time an operation spends in input queue.", this)
{
}
FileStorStripeMetrics::~FileStorStripeMetrics() = default;
-FileStorDiskMetrics::FileStorDiskMetrics(const std::string& name, const std::string& description,
- const metrics::LoadTypeSet& loadTypes, MetricSet* owner)
+FileStorDiskMetrics::FileStorDiskMetrics(const std::string& name, const std::string& description, MetricSet* owner)
: MetricSet(name, {{"partofsum"}}, description, owner),
sumThreads("allthreads", {{"sum"}}, "", this),
sumStripes("allstripes", {{"sum"}}, "", this),
- averageQueueWaitingTime(loadTypes,
- metrics::DoubleAverageMetric("averagequeuewait", {},
- "Average time an operation spends in input queue."),
- this),
+ averageQueueWaitingTime("averagequeuewait", {}, "Average time an operation spends in input queue.", this),
queueSize("queuesize", {}, "Size of input message queue.", this),
pendingMerges("pendingmerge", {}, "Number of buckets currently being merged.", this),
waitingForLockHitRate("waitingforlockrate", {},
@@ -244,7 +236,7 @@ FileStorDiskMetrics::initDiskMetrics(const LoadTypeSet& loadTypes, uint32_t numS
std::ostringstream name;
name << "stripe" << i;
desc << "Stripe " << i << '/' << numStripes;
- stripes[i] = std::make_shared<FileStorStripeMetrics>(name.str(), desc.str(), loadTypes);
+ stripes[i] = std::make_shared<FileStorStripeMetrics>(name.str(), desc.str());
registerMetric(*stripes[i]);
sumStripes.addMetricToSum(*stripes[i]);
}
@@ -267,7 +259,7 @@ void FileStorMetrics::initDiskMetrics(const LoadTypeSet& loadTypes, uint32_t num
assert( ! disk);
// Currently FileStorHandlerImpl expects metrics to exist for
// disks that are not in use too.
- disk = std::make_shared<FileStorDiskMetrics>( "disk_0", "Disk 0", loadTypes, this);
+ disk = std::make_shared<FileStorDiskMetrics>( "disk_0", "Disk 0", this);
sum.addMetricToSum(*disk);
disk->initDiskMetrics(loadTypes, numStripes, threadsPerDisk);
}
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormetrics.h b/storage/src/vespa/storage/persistence/filestorage/filestormetrics.h
index 86daf022fb2..aecbfc1ae2b 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestormetrics.h
+++ b/storage/src/vespa/storage/persistence/filestorage/filestormetrics.h
@@ -129,9 +129,8 @@ class FileStorStripeMetrics : public metrics::MetricSet
{
public:
using SP = std::shared_ptr<FileStorStripeMetrics>;
- metrics::LoadMetric<metrics::DoubleAverageMetric> averageQueueWaitingTime;
- FileStorStripeMetrics(const std::string& name, const std::string& description,
- const metrics::LoadTypeSet& loadTypes);
+ metrics::DoubleAverageMetric averageQueueWaitingTime;
+ FileStorStripeMetrics(const std::string& name, const std::string& description);
~FileStorStripeMetrics() override;
};
@@ -144,14 +143,13 @@ public:
std::vector<FileStorStripeMetrics::SP> stripes;
metrics::SumMetric<MetricSet> sumThreads;
metrics::SumMetric<MetricSet> sumStripes;
- metrics::LoadMetric<metrics::DoubleAverageMetric> averageQueueWaitingTime;
+ metrics::DoubleAverageMetric averageQueueWaitingTime;
metrics::LongAverageMetric queueSize;
metrics::LongAverageMetric pendingMerges;
metrics::DoubleAverageMetric waitingForLockHitRate;
metrics::DoubleAverageMetric lockWaitTime;
- FileStorDiskMetrics(const std::string& name, const std::string& description,
- const metrics::LoadTypeSet& loadTypes, MetricSet* owner);
+ FileStorDiskMetrics(const std::string& name, const std::string& description, MetricSet* owner);
~FileStorDiskMetrics() override;
void initDiskMetrics(const metrics::LoadTypeSet& loadTypes, uint32_t numStripes, uint32_t threadsPerDisk);
diff --git a/storage/src/vespa/storage/persistence/filestorage/mergestatus.cpp b/storage/src/vespa/storage/persistence/filestorage/mergestatus.cpp
index 2e390db69be..d75f09dfb3c 100644
--- a/storage/src/vespa/storage/persistence/filestorage/mergestatus.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/mergestatus.cpp
@@ -9,12 +9,12 @@ LOG_SETUP(".mergestatus");
namespace storage {
-MergeStatus::MergeStatus(const framework::Clock& clock, const metrics::LoadType& lt,
+MergeStatus::MergeStatus(const framework::Clock& clock,
api::StorageMessage::Priority priority,
uint32_t traceLevel)
: reply(), full_node_list(), nodeList(), maxTimestamp(0), diff(), pendingId(0),
pendingGetDiff(), pendingApplyDiff(), timeout(0), startTime(clock),
- context(lt, priority, traceLevel)
+ context(priority, traceLevel)
{}
MergeStatus::~MergeStatus() = default;
@@ -30,8 +30,7 @@ MergeStatus::removeFromDiff(
const std::vector<api::MergeBucketCommand::Node> &nodes)
{
std::deque<api::GetBucketDiffCommand::Entry>::iterator it(diff.begin());
- std::vector<api::ApplyBucketDiffCommand::Entry>::const_iterator it2(
- part.begin());
+ std::vector<api::ApplyBucketDiffCommand::Entry>::const_iterator it2(part.begin());
bool altered = false;
HasMaskRemapper remap_mask(nodeList, nodes);
// We expect part array to be sorted in the same order as in the diff,
diff --git a/storage/src/vespa/storage/persistence/filestorage/mergestatus.h b/storage/src/vespa/storage/persistence/filestorage/mergestatus.h
index 51930f337c6..97a66ef56c1 100644
--- a/storage/src/vespa/storage/persistence/filestorage/mergestatus.h
+++ b/storage/src/vespa/storage/persistence/filestorage/mergestatus.h
@@ -15,8 +15,6 @@ namespace storage {
class MergeStatus : public document::Printable {
public:
- using SP = std::shared_ptr<MergeStatus>;
-
std::shared_ptr<api::StorageReply> reply;
std::vector<api::MergeBucketCommand::Node> full_node_list;
std::vector<api::MergeBucketCommand::Node> nodeList;
@@ -29,8 +27,8 @@ public:
framework::MilliSecTimer startTime;
spi::Context context;
- MergeStatus(const framework::Clock&, const metrics::LoadType&, api::StorageMessage::Priority, uint32_t traceLevel);
- ~MergeStatus();
+ MergeStatus(const framework::Clock&, api::StorageMessage::Priority, uint32_t traceLevel);
+ ~MergeStatus() override;
/**
* Note: hasMask parameter and _entry._hasMask in part vector are per-reply masks,
@@ -41,7 +39,7 @@ public:
*/
bool removeFromDiff(const std::vector<api::ApplyBucketDiffCommand::Entry>& part, uint16_t hasMask, const std::vector<api::MergeBucketCommand::Node> &nodes);
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
- bool isFirstNode() const { return (reply.get() != 0); }
+ bool isFirstNode() const { return static_cast<bool>(reply); }
};
} // storage
diff --git a/storage/src/vespa/storage/persistence/mergehandler.cpp b/storage/src/vespa/storage/persistence/mergehandler.cpp
index cab35e77bac..2e65302ee3b 100644
--- a/storage/src/vespa/storage/persistence/mergehandler.cpp
+++ b/storage/src/vespa/storage/persistence/mergehandler.cpp
@@ -4,6 +4,7 @@
#include "persistenceutil.h"
#include "apply_bucket_diff_entry_complete.h"
#include "apply_bucket_diff_entry_result.h"
+#include <vespa/storage/persistence/filestorage/mergestatus.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vdslib/distribution/distribution.h>
#include <vespa/document/fieldset/fieldsets.h>
@@ -145,7 +146,6 @@ MergeHandler::populateMetaData(
bool
MergeHandler::buildBucketInfoList(
const spi::Bucket& bucket,
- const documentapi::LoadType& /*loadType*/,
Timestamp maxTimestamp,
uint8_t myNodeIndex,
std::vector<api::GetBucketDiffCommand::Entry>& output,
@@ -320,7 +320,6 @@ namespace {
void
MergeHandler::fetchLocalData(
const spi::Bucket& bucket,
- const documentapi::LoadType& /*loadType*/,
std::vector<api::ApplyBucketDiffCommand::Entry>& diff,
uint8_t nodeIndex,
spi::Context& context) const
@@ -510,7 +509,6 @@ MergeHandler::applyDiffEntry(const spi::Bucket& bucket,
api::BucketInfo
MergeHandler::applyDiffLocally(
const spi::Bucket& bucket,
- const documentapi::LoadType& /*loadType*/,
std::vector<api::ApplyBucketDiffCommand::Entry>& diff,
uint8_t nodeIndex,
spi::Context& context) const
@@ -804,7 +802,7 @@ MergeHandler::processBucketMerge(const spi::Bucket& bucket, MergeStatus& status,
cmd->setTimeout(status.timeout);
if (applyDiffNeedLocalData(cmd->getDiff(), 0, true)) {
framework::MilliSecTimer startTime(_clock);
- fetchLocalData(bucket, cmd->getLoadType(), cmd->getDiff(), 0, context);
+ fetchLocalData(bucket, cmd->getDiff(), 0, context);
_env._metrics.merge_handler_metrics.mergeDataReadLatency.addValue(startTime.getElapsedTimeAsDouble());
}
status.pendingId = cmd->getMsgId();
@@ -847,7 +845,7 @@ MergeHandler::handleMergeBucket(api::MergeBucketCommand& cmd, MessageTracker::UP
if (cmd.getNodes().size() < 2) {
LOG(debug, "Attempt to merge a single instance of a bucket");
- tracker->fail(ReturnCode::ILLEGAL_PARAMETERS, "Cannot merge a single copy");
+ tracker->fail(api::ReturnCode::ILLEGAL_PARAMETERS, "Cannot merge a single copy");
return tracker;
}
@@ -856,7 +854,7 @@ MergeHandler::handleMergeBucket(api::MergeBucketCommand& cmd, MessageTracker::UP
for (uint16_t i = 0; i < cmd.getNodes().size(); ++i) {
if (i == 0) {
if (cmd.getNodes()[i].sourceOnly) {
- tracker->fail(ReturnCode::ILLEGAL_PARAMETERS,
+ tracker->fail(api::ReturnCode::ILLEGAL_PARAMETERS,
"Attempted to merge a chain where the first node "
"in the chain is source only.");
return tracker;
@@ -865,7 +863,7 @@ MergeHandler::handleMergeBucket(api::MergeBucketCommand& cmd, MessageTracker::UP
if (!cmd.getNodes()[i].sourceOnly
&& cmd.getNodes()[i-1].sourceOnly)
{
- tracker->fail(ReturnCode::ILLEGAL_PARAMETERS,
+ tracker->fail(api::ReturnCode::ILLEGAL_PARAMETERS,
"Attempted to merge a chain where the source only "
"copies are not in end of chain.");
return tracker;
@@ -876,15 +874,13 @@ MergeHandler::handleMergeBucket(api::MergeBucketCommand& cmd, MessageTracker::UP
if (_env._fileStorHandler.isMerging(bucket.getBucket())) {
const char* err = "A merge is already running on this bucket.";
LOG(debug, "%s", err);
- tracker->fail(ReturnCode::BUSY, err);
+ tracker->fail(api::ReturnCode::BUSY, err);
return tracker;
}
checkResult(_spi.createBucket(bucket, tracker->context()), bucket, "create bucket");
MergeStateDeleter stateGuard(_env._fileStorHandler, bucket.getBucket());
- auto s = std::make_shared<MergeStatus>(
- _clock, cmd.getLoadType(),
- cmd.getPriority(), cmd.getTrace().getLevel());
+ auto s = std::make_shared<MergeStatus>(_clock, cmd.getPriority(), cmd.getTrace().getLevel());
_env._fileStorHandler.addMergeStatus(bucket.getBucket(), s);
s->full_node_list = cmd.getNodes();
s->nodeList = cmd.getNodes();
@@ -893,9 +889,9 @@ MergeHandler::handleMergeBucket(api::MergeBucketCommand& cmd, MessageTracker::UP
s->startTime = framework::MilliSecTimer(_clock);
auto cmd2 = std::make_shared<api::GetBucketDiffCommand>(bucket.getBucket(), s->nodeList, s->maxTimestamp.getTime());
- if (!buildBucketInfoList(bucket, cmd.getLoadType(), s->maxTimestamp, 0, cmd2->getDiff(), tracker->context())) {
+ if (!buildBucketInfoList(bucket, s->maxTimestamp, 0, cmd2->getDiff(), tracker->context())) {
LOG(debug, "Bucket non-existing in db. Failing merge.");
- tracker->fail(ReturnCode::BUCKET_DELETED, "Bucket not found in buildBucketInfo step");
+ tracker->fail(api::ReturnCode::BUCKET_DELETED, "Bucket not found in buildBucketInfo step");
return tracker;
}
_env._metrics.merge_handler_metrics.mergeMetadataReadLatency.addValue(s->startTime.getElapsedTimeAsDouble());
@@ -1057,7 +1053,7 @@ MergeHandler::handleGetBucketDiff(api::GetBucketDiffCommand& cmd, MessageTracker
checkResult(_spi.createBucket(bucket, tracker->context()), bucket, "create bucket");
if (_env._fileStorHandler.isMerging(bucket.getBucket())) {
- tracker->fail(ReturnCode::BUSY, "A merge is already running on this bucket.");
+ tracker->fail(api::ReturnCode::BUSY, "A merge is already running on this bucket.");
return tracker;
}
uint8_t index = findOwnIndex(cmd.getNodes(), _env._nodeIndex);
@@ -1065,12 +1061,11 @@ MergeHandler::handleGetBucketDiff(api::GetBucketDiffCommand& cmd, MessageTracker
std::vector<api::GetBucketDiffCommand::Entry>& remote(cmd.getDiff());
std::vector<api::GetBucketDiffCommand::Entry> local;
framework::MilliSecTimer startTime(_clock);
- if (!buildBucketInfoList(bucket, cmd.getLoadType(),
- Timestamp(cmd.getMaxTimestamp()),
+ if (!buildBucketInfoList(bucket, Timestamp(cmd.getMaxTimestamp()),
index, local, tracker->context()))
{
LOG(debug, "Bucket non-existing in db. Failing merge.");
- tracker->fail(ReturnCode::BUCKET_DELETED, "Bucket not found in buildBucketInfo step");
+ tracker->fail(api::ReturnCode::BUCKET_DELETED, "Bucket not found in buildBucketInfo step");
return tracker;
}
if (!mergeLists(remote, local, local)) {
@@ -1106,9 +1101,7 @@ MergeHandler::handleGetBucketDiff(api::GetBucketDiffCommand& cmd, MessageTracker
// When not the last node in merge chain, we must save reply, and
// send command on.
MergeStateDeleter stateGuard(_env._fileStorHandler, bucket.getBucket());
- auto s = std::make_shared<MergeStatus>(_clock,
- cmd.getLoadType(), cmd.getPriority(),
- cmd.getTrace().getLevel());
+ auto s = std::make_shared<MergeStatus>(_clock, cmd.getPriority(), cmd.getTrace().getLevel());
_env._fileStorHandler.addMergeStatus(bucket.getBucket(), s);
s->pendingGetDiff = std::make_shared<api::GetBucketDiffReply>(cmd);
@@ -1241,8 +1234,7 @@ MergeHandler::handleApplyBucketDiff(api::ApplyBucketDiffCommand& cmd, MessageTra
LOG(debug, "%s", cmd.toString().c_str());
if (_env._fileStorHandler.isMerging(bucket.getBucket())) {
- tracker->fail(ReturnCode::BUSY,
- "A merge is already running on this bucket.");
+ tracker->fail(api::ReturnCode::BUSY, "A merge is already running on this bucket.");
return tracker;
}
@@ -1250,19 +1242,17 @@ MergeHandler::handleApplyBucketDiff(api::ApplyBucketDiffCommand& cmd, MessageTra
bool lastInChain = index + 1u >= cmd.getNodes().size();
if (applyDiffNeedLocalData(cmd.getDiff(), index, !lastInChain)) {
framework::MilliSecTimer startTime(_clock);
- fetchLocalData(bucket, cmd.getLoadType(), cmd.getDiff(), index, tracker->context());
+ fetchLocalData(bucket, cmd.getDiff(), index, tracker->context());
_env._metrics.merge_handler_metrics.mergeDataReadLatency.addValue(startTime.getElapsedTimeAsDouble());
} else {
LOG(spam, "Merge(%s): Moving %zu entries, didn't need "
"local data on node %u (%u).",
- bucket.toString().c_str(),
- cmd.getDiff().size(),
- _env._nodeIndex,
- index);
+ bucket.toString().c_str(), cmd.getDiff().size(),
+ _env._nodeIndex, index);
}
if (applyDiffHasLocallyNeededData(cmd.getDiff(), index)) {
framework::MilliSecTimer startTime(_clock);
- (void) applyDiffLocally(bucket, cmd.getLoadType(), cmd.getDiff(), index, tracker->context());
+ (void) applyDiffLocally(bucket, cmd.getDiff(), index, tracker->context());
_env._metrics.merge_handler_metrics.mergeDataWriteLatency.addValue(
startTime.getElapsedTimeAsDouble());
} else {
@@ -1296,9 +1286,7 @@ MergeHandler::handleApplyBucketDiff(api::ApplyBucketDiffCommand& cmd, MessageTra
// When not the last node in merge chain, we must save reply, and
// send command on.
MergeStateDeleter stateGuard(_env._fileStorHandler, bucket.getBucket());
- auto s = std::make_shared<MergeStatus>(_clock,
- cmd.getLoadType(), cmd.getPriority(),
- cmd.getTrace().getLevel());
+ auto s = std::make_shared<MergeStatus>(_clock, cmd.getPriority(), cmd.getTrace().getLevel());
_env._fileStorHandler.addMergeStatus(bucket.getBucket(), s);
s->pendingApplyDiff = std::make_shared<api::ApplyBucketDiffReply>(cmd);
@@ -1352,12 +1340,12 @@ MergeHandler::handleApplyBucketDiffReply(api::ApplyBucketDiffReply& reply,Messag
uint8_t index = findOwnIndex(reply.getNodes(), _env._nodeIndex);
if (applyDiffNeedLocalData(diff, index, false)) {
framework::MilliSecTimer startTime(_clock);
- fetchLocalData(bucket, reply.getLoadType(), diff, index, s.context);
+ fetchLocalData(bucket, diff, index, s.context);
_env._metrics.merge_handler_metrics.mergeDataReadLatency.addValue(startTime.getElapsedTimeAsDouble());
}
if (applyDiffHasLocallyNeededData(diff, index)) {
framework::MilliSecTimer startTime(_clock);
- (void) applyDiffLocally(bucket, reply.getLoadType(), diff, index, s.context);
+ (void) applyDiffLocally(bucket, diff, index, s.context);
_env._metrics.merge_handler_metrics.mergeDataWriteLatency.addValue(startTime.getElapsedTimeAsDouble());
} else {
LOG(spam, "Merge(%s): Didn't need fetched data on node %u (%u)",
diff --git a/storage/src/vespa/storage/persistence/mergehandler.h b/storage/src/vespa/storage/persistence/mergehandler.h
index 64b1448577a..25b7f281ef0 100644
--- a/storage/src/vespa/storage/persistence/mergehandler.h
+++ b/storage/src/vespa/storage/persistence/mergehandler.h
@@ -16,15 +16,18 @@
#include "types.h"
#include <vespa/persistence/spi/bucket.h>
#include <vespa/persistence/spi/docentry.h>
-#include <vespa/storage/persistence/filestorage/mergestatus.h>
#include <vespa/storageapi/message/bucket.h>
#include <vespa/storage/common/messagesender.h>
namespace storage {
-namespace spi { struct PersistenceProvider; }
+namespace spi {
+ struct PersistenceProvider;
+ class Context;
+}
class PersistenceUtil;
class ApplyBucketDiffEntryResult;
+class MergeStatus;
class MergeHandler : public Types {
@@ -42,19 +45,16 @@ public:
bool buildBucketInfoList(
const spi::Bucket& bucket,
- const documentapi::LoadType&,
Timestamp maxTimestamp,
uint8_t myNodeIndex,
std::vector<api::GetBucketDiffCommand::Entry>& output,
spi::Context& context) const;
void fetchLocalData(const spi::Bucket& bucket,
- const documentapi::LoadType&,
std::vector<api::ApplyBucketDiffCommand::Entry>& diff,
uint8_t nodeIndex,
spi::Context& context) const;
api::BucketInfo applyDiffLocally(
const spi::Bucket& bucket,
- const documentapi::LoadType&,
std::vector<api::ApplyBucketDiffCommand::Entry>& diff,
uint8_t nodeIndex,
spi::Context& context) const;
diff --git a/storage/src/vespa/storage/persistence/persistenceutil.cpp b/storage/src/vespa/storage/persistence/persistenceutil.cpp
index 42d67573763..6e0004af88a 100644
--- a/storage/src/vespa/storage/persistence/persistenceutil.cpp
+++ b/storage/src/vespa/storage/persistence/persistenceutil.cpp
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "persistenceutil.h"
+#include <vespa/storageapi/messageapi/bucketinforeply.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/log/bufferedlogger.h>
@@ -42,7 +43,7 @@ MessageTracker::MessageTracker(const framework::MilliSecTimer & timer,
_updateBucketInfo(updateBucketInfo && hasBucketInfo(msg->getType().getId())),
_bucketLock(std::move(bucketLock)),
_msg(std::move(msg)),
- _context(_msg->getLoadType(), _msg->getPriority(), _msg->getTrace().getLevel()),
+ _context(_msg->getPriority(), _msg->getTrace().getLevel()),
_env(env),
_replySender(replySender),
_metric(nullptr),
@@ -93,9 +94,7 @@ MessageTracker::sendReply() {
_msg->toString(true).c_str(), vespalib::to_s(duration));
}
if (hasReply()) {
- if ( ! _context.getTrace().getRoot().isEmpty()) {
- getReply().getTrace().getRoot().addChild(_context.getTrace().getRoot());
- }
+ getReply().getTrace().addChild(_context.steal_trace());
if (_updateBucketInfo) {
if (getReply().getResult().success()) {
_env.setBucketInfo(*this, _bucketLock->getBucket());
@@ -104,13 +103,10 @@ MessageTracker::sendReply() {
if (getReply().getResult().success()) {
_metric->latency.addValue(_timer.getElapsedTimeAsDouble());
}
- LOG(spam, "Sending reply up: %s %" PRIu64,
- getReply().toString().c_str(), getReply().getMsgId());
+ LOG(spam, "Sending reply up: %s %" PRIu64, getReply().toString().c_str(), getReply().getMsgId());
_replySender.sendReplyDirectly(std::move(_reply));
} else {
- if ( ! _context.getTrace().getRoot().isEmpty()) {
- _msg->getTrace().getRoot().addChild(_context.getTrace().getRoot());
- }
+ _msg->getTrace().addChild(_context.steal_trace());
}
}
@@ -128,7 +124,7 @@ MessageTracker::checkForError(const spi::Result& response)
}
void
-MessageTracker::fail(const ReturnCode& result)
+MessageTracker::fail(const api::ReturnCode& result)
{
_result = result;
LOG(debug, "Failing operation with error: %s", _result.toString().c_str());
diff --git a/storage/src/vespa/storage/persistence/persistenceutil.h b/storage/src/vespa/storage/persistence/persistenceutil.h
index 8eeea6dddd2..47233b0ded6 100644
--- a/storage/src/vespa/storage/persistence/persistenceutil.h
+++ b/storage/src/vespa/storage/persistence/persistenceutil.h
@@ -6,11 +6,18 @@
#include <vespa/storage/common/servicelayercomponent.h>
#include <vespa/storage/persistence/filestorage/filestorhandler.h>
#include <vespa/storage/persistence/filestorage/filestormetrics.h>
+#include <vespa/storageframework/generic/clock/timer.h>
+#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/storage/storageutil/utils.h>
+namespace storage::api {
+ class StorageMessage;
+ class StorageReply;
+}
namespace storage {
+
class PersistenceUtil;
class MessageTracker : protected Types {
@@ -18,7 +25,7 @@ public:
typedef std::unique_ptr<MessageTracker> UP;
MessageTracker(const framework::MilliSecTimer & timer, const PersistenceUtil & env, MessageSender & replySender,
- FileStorHandler::BucketLockInterface::SP bucketLock, api::StorageMessage::SP msg);
+ FileStorHandler::BucketLockInterface::SP bucketLock, std::shared_ptr<api::StorageMessage> msg);
~MessageTracker();
@@ -29,17 +36,17 @@ public:
* non-default reply. They should call this function as soon as they create
* a reply, to ensure it is stored in case of failure after reply creation.
*/
- void setReply(api::StorageReply::SP reply) {
+ void setReply(std::shared_ptr<api::StorageReply> reply) {
assert( ! _reply );
_reply = std::move(reply);
}
/** Utility function to be able to write a bit less in client. */
void fail(uint32_t result, const String& message = "") {
- fail(ReturnCode((api::ReturnCode::Result)result, message));
+ fail(api::ReturnCode((api::ReturnCode::Result)result, message));
}
/** Set the request to fail with the given failure. */
- void fail(const ReturnCode&);
+ void fail(const api::ReturnCode&);
/** Don't send reply for the command being processed. Used by multi chain
* commands like merge. */
@@ -52,7 +59,7 @@ public:
api::StorageReply & getReply() {
return *_reply;
}
- api::StorageReply::SP && stealReplySP() && {
+ std::shared_ptr<api::StorageReply> && stealReplySP() && {
return std::move(_reply);
}
@@ -71,23 +78,23 @@ public:
static MessageTracker::UP
createForTesting(const framework::MilliSecTimer & timer, PersistenceUtil & env, MessageSender & replySender,
- FileStorHandler::BucketLockInterface::SP bucketLock, api::StorageMessage::SP msg);
+ FileStorHandler::BucketLockInterface::SP bucketLock, std::shared_ptr<api::StorageMessage> msg);
private:
MessageTracker(const framework::MilliSecTimer & timer, const PersistenceUtil & env, MessageSender & replySender, bool updateBucketInfo,
- FileStorHandler::BucketLockInterface::SP bucketLock, api::StorageMessage::SP msg);
+ FileStorHandler::BucketLockInterface::SP bucketLock, std::shared_ptr<api::StorageMessage> msg);
[[nodiscard]] bool count_result_as_failure() const noexcept;
bool _sendReply;
bool _updateBucketInfo;
FileStorHandler::BucketLockInterface::SP _bucketLock;
- api::StorageMessage::SP _msg;
+ std::shared_ptr<api::StorageMessage> _msg;
spi::Context _context;
const PersistenceUtil &_env;
MessageSender &_replySender;
FileStorThreadMetrics::Op *_metric; // needs a better and thread safe solution
- api::StorageReply::SP _reply;
+ std::shared_ptr<api::StorageReply> _reply;
api::ReturnCode _result;
framework::MilliSecTimer _timer;
};
diff --git a/storage/src/vespa/storage/persistence/simplemessagehandler.cpp b/storage/src/vespa/storage/persistence/simplemessagehandler.cpp
index 3d5a4efc1af..6ed928245db 100644
--- a/storage/src/vespa/storage/persistence/simplemessagehandler.cpp
+++ b/storage/src/vespa/storage/persistence/simplemessagehandler.cpp
@@ -4,6 +4,7 @@
#include "persistenceutil.h"
#include <vespa/persistence/spi/persistenceprovider.h>
#include <vespa/storage/common/bucketoperationlogger.h>
+#include <vespa/storageapi/message/bucket.h>
#include <vespa/document/base/exceptions.h>
#include <vespa/document/fieldset/fieldsetrepo.h>
diff --git a/storage/src/vespa/storage/persistence/splitjoinhandler.cpp b/storage/src/vespa/storage/persistence/splitjoinhandler.cpp
index 6049dabc2fa..c64a892d6fb 100644
--- a/storage/src/vespa/storage/persistence/splitjoinhandler.cpp
+++ b/storage/src/vespa/storage/persistence/splitjoinhandler.cpp
@@ -7,6 +7,7 @@
#include "messages.h"
#include <vespa/storage/common/bucketmessages.h>
#include <vespa/persistence/spi/persistenceprovider.h>
+#include <vespa/storageapi/message/bucket.h>
#include <vespa/log/log.h>
LOG_SETUP(".persistence.splitjoinhandler");
@@ -297,19 +298,19 @@ bool
SplitJoinHandler::validateJoinCommand(const api::JoinBucketsCommand& cmd, MessageTracker& tracker)
{
if (cmd.getSourceBuckets().size() != 2) {
- tracker.fail(ReturnCode::ILLEGAL_PARAMETERS,
+ tracker.fail(api::ReturnCode::ILLEGAL_PARAMETERS,
"Join needs exactly two buckets to be joined together" + cmd.getBucketId().toString());
return false;
}
// Verify that source and target buckets look sane.
for (uint32_t i = 0; i < cmd.getSourceBuckets().size(); i++) {
if (cmd.getSourceBuckets()[i] == cmd.getBucketId()) {
- tracker.fail(ReturnCode::ILLEGAL_PARAMETERS,
+ tracker.fail(api::ReturnCode::ILLEGAL_PARAMETERS,
"Join had both source and target bucket " + cmd.getBucketId().toString());
return false;
}
if (!cmd.getBucketId().contains(cmd.getSourceBuckets()[i])) {
- tracker.fail(ReturnCode::ILLEGAL_PARAMETERS,
+ tracker.fail(api::ReturnCode::ILLEGAL_PARAMETERS,
"Source bucket " + cmd.getSourceBuckets()[i].toString()
+ " is not contained in target " + cmd.getBucketId().toString());
return false;
diff --git a/storage/src/vespa/storage/persistence/types.h b/storage/src/vespa/storage/persistence/types.h
index 6a73adc8b0c..b36155bde8f 100644
--- a/storage/src/vespa/storage/persistence/types.h
+++ b/storage/src/vespa/storage/persistence/types.h
@@ -7,7 +7,6 @@
#include <vespa/document/fieldvalue/document.h>
#include <vespa/storage/bucketdb/storbucketdb.h>
#include <vespa/storageapi/buckets/bucketinfo.h>
-#include <vespa/storageapi/messageapi/returncode.h>
#include <vespa/storageapi/defs.h>
#include <vespa/vespalib/stllike/string.h>
#include <vespa/storageframework/generic/clock/time.h>
@@ -25,8 +24,6 @@ struct Types {
typedef Timestamp RevertToken;
typedef vespalib::string String;
typedef api::BucketInfo BucketInfo;
- typedef api::ReturnCode ReturnCode;
- typedef StorBucketDatabase::WrappedEntry BucketDBEntry;
using MessageTrackerUP = std::unique_ptr<MessageTracker>;
static const framework::MicroSecTime MAX_TIMESTAMP;
diff --git a/storage/src/vespa/storage/storageserver/communicationmanager.cpp b/storage/src/vespa/storage/storageserver/communicationmanager.cpp
index a35b9d1d59a..c296f215c8c 100644
--- a/storage/src/vespa/storage/storageserver/communicationmanager.cpp
+++ b/storage/src/vespa/storage/storageserver/communicationmanager.cpp
@@ -102,7 +102,7 @@ CommunicationManager::handleMessage(std::unique_ptr<mbus::Message> msg)
return;
}
- cmd->setTrace(docMsgPtr->getTrace());
+ cmd->setTrace(docMsgPtr->steal_trace());
cmd->setTransportContext(std::make_unique<StorageTransportContext>(std::move(docMsgPtr)));
enqueue_or_process(std::move(cmd));
@@ -114,7 +114,7 @@ CommunicationManager::handleMessage(std::unique_ptr<mbus::Message> msg)
//TODO: Can it be moved ?
std::shared_ptr<api::StorageCommand> cmd = storMsgPtr->getCommand();
cmd->setTimeout(storMsgPtr->getTimeRemaining());
- cmd->setTrace(storMsgPtr->getTrace());
+ cmd->setTrace(storMsgPtr->steal_trace());
cmd->setTransportContext(std::make_unique<StorageTransportContext>(std::move(storMsgPtr)));
enqueue_or_process(std::move(cmd));
@@ -203,12 +203,12 @@ CommunicationManager::handleReply(std::unique_ptr<mbus::Reply> reply)
_docApiConverter.toStorageAPI(static_cast<documentapi::DocumentReply&>(*reply), *originalCommand));
if (sar) {
- sar->setTrace(reply->getTrace());
+ sar->setTrace(reply->steal_trace());
receiveStorageReply(sar);
}
} else if (protocolName == mbusprot::StorageProtocol::NAME) {
mbusprot::StorageReply* sr(static_cast<mbusprot::StorageReply*>(reply.get()));
- sr->getReply()->setTrace(reply->getTrace());
+ sr->getReply()->setTrace(reply->steal_trace());
receiveStorageReply(sr->getReply());
} else {
LOGBM(warning, "Received unsupported reply type %d for protocol '%s'.",
@@ -260,7 +260,7 @@ convert_to_rpc_compression_config(const vespa::config::content::core::StorCommun
CommunicationManager::CommunicationManager(StorageComponentRegister& compReg, const config::ConfigUri & configUri)
: StorageLink("Communication manager"),
_component(compReg, "communicationmanager"),
- _metrics(_component.getLoadTypes()->getMetricLoadTypes()),
+ _metrics(),
_shared_rpc_resources(), // Created upon initial configuration
_storage_api_rpc_service(), // (ditto)
_cc_rpc_service(), // (ditto)
@@ -466,11 +466,11 @@ CommunicationManager::process(const std::shared_ptr<api::StorageMessage>& msg)
}
LOG(spam, "Done processing: %s", msg->toString().c_str());
- _metrics.messageProcessTime[msg->getLoadType()].addValue(startTime.getElapsedTimeAsDouble());
+ _metrics.messageProcessTime.addValue(startTime.getElapsedTimeAsDouble());
} catch (std::exception& e) {
LOGBP(error, "When running command %s, caught exception %s. Discarding message",
msg->toString().c_str(), e.what());
- _metrics.exceptionMessageProcessTime[msg->getLoadType()].addValue(startTime.getElapsedTimeAsDouble());
+ _metrics.exceptionMessageProcessTime.addValue(startTime.getElapsedTimeAsDouble());
}
}
@@ -583,8 +583,8 @@ CommunicationManager::sendCommand(
cmd->setContext(mbus::Context(msg->getMsgId()));
cmd->setRetryEnabled(false);
cmd->setTimeRemaining(msg->getTimeout());
- cmd->setTrace(msg->getTrace());
- sendMessageBusMessage(msg, std::move(cmd), address.getRoute());
+ cmd->setTrace(msg->steal_trace());
+ sendMessageBusMessage(msg, std::move(cmd), address.to_mbus_route());
}
break;
}
@@ -596,14 +596,14 @@ CommunicationManager::sendCommand(
if (mbusMsg) {
MBUS_TRACE(msg->getTrace(), 7, "Communication manager: Converted OK");
- mbusMsg->setTrace(msg->getTrace());
+ mbusMsg->setTrace(msg->steal_trace());
mbusMsg->setRetryEnabled(false);
{
std::lock_guard lock(_messageBusSentLock);
_messageBusSent[msg->getMsgId()] = msg;
}
- sendMessageBusMessage(msg, std::move(mbusMsg), address.getRoute());
+ sendMessageBusMessage(msg, std::move(mbusMsg), address.to_mbus_route());
break;
} else {
LOGBM(warning, "This type of message can't be sent via messagebus");
@@ -660,7 +660,8 @@ CommunicationManager::sendDirectRPCReply(
activate_reply.activateVersion(), activate_reply.actualVersion());
} else {
request.addReturnInt(reply->getResult().getResult());
- request.addReturnString(reply->getResult().getMessage().c_str());
+ vespalib::stringref m = reply->getResult().getMessage();
+ request.addReturnString(m.data(), m.size());
if (reply->getType() == api::MessageType::GETNODESTATE_REPLY) {
api::GetNodeStateReply& gns(static_cast<api::GetNodeStateReply&>(*reply));
@@ -690,13 +691,13 @@ CommunicationManager::sendMessageBusReply(
if (reply->getResult().getResult() == api::ReturnCode::WRONG_DISTRIBUTION) {
replyUP = std::make_unique<documentapi::WrongDistributionReply>(reply->getResult().getMessage());
replyUP->swapState(*context._docAPIMsg);
- replyUP->setTrace(reply->getTrace());
+ replyUP->setTrace(reply->steal_trace());
replyUP->addError(mbus::Error(documentapi::DocumentProtocol::ERROR_WRONG_DISTRIBUTION,
reply->getResult().getMessage()));
} else {
replyUP = context._docAPIMsg->createReply();
replyUP->swapState(*context._docAPIMsg);
- replyUP->setTrace(reply->getTrace());
+ replyUP->setTrace(reply->steal_trace());
replyUP->setMessage(std::move(context._docAPIMsg));
_docApiConverter.transferReplyState(*reply, *replyUP);
}
@@ -707,7 +708,7 @@ CommunicationManager::sendMessageBusReply(
}
replyUP->swapState(*context._storageProtocolMsg);
- replyUP->setTrace(reply->getTrace());
+ replyUP->setTrace(reply->steal_trace());
replyUP->setMessage(std::move(context._storageProtocolMsg));
}
diff --git a/storage/src/vespa/storage/storageserver/communicationmanagermetrics.cpp b/storage/src/vespa/storage/storageserver/communicationmanagermetrics.cpp
index 5f2bed07f66..8df19ba6639 100644
--- a/storage/src/vespa/storage/storageserver/communicationmanagermetrics.cpp
+++ b/storage/src/vespa/storage/storageserver/communicationmanagermetrics.cpp
@@ -1,23 +1,17 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "communicationmanagermetrics.h"
-#include <vespa/documentapi/loadtypes/loadtypeset.h>
using namespace metrics;
namespace storage {
-CommunicationManagerMetrics::CommunicationManagerMetrics(const LoadTypeSet& loadTypes, MetricSet* owner)
+CommunicationManagerMetrics::CommunicationManagerMetrics(MetricSet* owner)
: MetricSet("communication", {}, "Metrics for the communication manager", owner),
queueSize("messagequeue", {}, "Size of input message queue.", this),
- messageProcessTime(loadTypes,
- DoubleAverageMetric("messageprocesstime", {},
- "Time transport thread uses to process a single message"),
- this),
- exceptionMessageProcessTime(loadTypes,
- DoubleAverageMetric("exceptionmessageprocesstime", {},
- "Time transport thread uses to process a single message "
- "that fails with an exception thrown into communication manager"),
- this),
+ messageProcessTime("messageprocesstime", {}, "Time transport thread uses to process a single message", this),
+ exceptionMessageProcessTime("exceptionmessageprocesstime", {},
+ "Time transport thread uses to process a single message "
+ "that fails with an exception thrown into communication manager", this),
failedDueToTooLittleMemory("toolittlememory", {}, "Number of messages failed due to too little memory available", this),
convertToStorageAPIFailures("convertfailures", {},
"Number of messages that failed to get converted to storage API messages", this),
diff --git a/storage/src/vespa/storage/storageserver/communicationmanagermetrics.h b/storage/src/vespa/storage/storageserver/communicationmanagermetrics.h
index 2a2b8bdb5c1..cc62b93e6d4 100644
--- a/storage/src/vespa/storage/storageserver/communicationmanagermetrics.h
+++ b/storage/src/vespa/storage/storageserver/communicationmanagermetrics.h
@@ -14,15 +14,15 @@ namespace storage {
struct CommunicationManagerMetrics : public metrics::MetricSet {
metrics::LongAverageMetric queueSize;
- metrics::LoadMetric<metrics::DoubleAverageMetric> messageProcessTime;
- metrics::LoadMetric<metrics::DoubleAverageMetric> exceptionMessageProcessTime;
+ metrics::DoubleAverageMetric messageProcessTime;
+ metrics::DoubleAverageMetric exceptionMessageProcessTime;
metrics::LongCountMetric failedDueToTooLittleMemory;
metrics::LongCountMetric convertToStorageAPIFailures;
metrics::LongCountMetric bucketSpaceMappingFailures;
metrics::DoubleAverageMetric sendCommandLatency;
metrics::DoubleAverageMetric sendReplyLatency;
- CommunicationManagerMetrics(const metrics::LoadTypeSet& loadTypes, metrics::MetricSet* owner = 0);
+ CommunicationManagerMetrics(metrics::MetricSet* owner = nullptr);
~CommunicationManagerMetrics();
};
diff --git a/storage/src/vespa/storage/storageserver/documentapiconverter.cpp b/storage/src/vespa/storage/storageserver/documentapiconverter.cpp
index f0c987ee333..b1c8c1e43a4 100644
--- a/storage/src/vespa/storage/storageserver/documentapiconverter.cpp
+++ b/storage/src/vespa/storage/storageserver/documentapiconverter.cpp
@@ -14,6 +14,7 @@
#include <vespa/storageapi/message/searchresult.h>
#include <vespa/storageapi/message/stat.h>
#include <vespa/storageapi/message/visitor.h>
+#include <vespa/messagebus/error.h>
#include <vespa/log/log.h>
LOG_SETUP(".documentapiconverter");
@@ -67,8 +68,7 @@ DocumentApiConverter::toStorageAPI(documentapi::DocumentMessage& fromMsg)
case DocumentProtocol::MESSAGE_GETDOCUMENT:
{
documentapi::GetDocumentMessage& from(static_cast<documentapi::GetDocumentMessage&>(fromMsg));
- auto to = std::make_unique<api::GetCommand>(bucketResolver()->bucketFromId(from.getDocumentId()), from.getDocumentId(), from.getFieldSet());
- toMsg.reset(to.release());
+ toMsg = std::make_unique<api::GetCommand>(bucketResolver()->bucketFromId(from.getDocumentId()), from.getDocumentId(), from.getFieldSet());
break;
}
case DocumentProtocol::MESSAGE_CREATEVISITOR:
@@ -130,8 +130,7 @@ DocumentApiConverter::toStorageAPI(documentapi::DocumentMessage& fromMsg)
{
documentapi::RemoveLocationMessage& from(static_cast<documentapi::RemoveLocationMessage&>(fromMsg));
document::Bucket bucket(bucketResolver()->bucketSpaceFromName(from.getBucketSpace()), document::BucketId(0));
- api::RemoveLocationCommand::UP to(new api::RemoveLocationCommand(from.getDocumentSelection(), bucket));
- toMsg.reset(to.release());
+ toMsg = std::make_unique<api::RemoveLocationCommand>(from.getDocumentSelection(), bucket);
break;
}
default:
@@ -145,10 +144,9 @@ DocumentApiConverter::toStorageAPI(documentapi::DocumentMessage& fromMsg)
: 1ms*INT_MAX;
toMsg->setTimeout(cappedTimeout);
toMsg->setPriority(_priConverter->toStoragePriority(fromMsg.getPriority()));
- toMsg->setLoadType(fromMsg.getLoadType());
- LOG(spam, "Converted command %s, loadtype %d, mapped priority %d to %d",
- toMsg->toString().c_str(), toMsg->getLoadType().getId(),
+ LOG(spam, "Converted command %s, mapped priority %d to %d",
+ toMsg->toString().c_str(),
fromMsg.getPriority(), toMsg->getPriority());
}
return toMsg;
diff --git a/storage/src/vespa/storage/storageserver/mergethrottler.cpp b/storage/src/vespa/storage/storageserver/mergethrottler.cpp
index 5e8e6809422..2e3c65ef70c 100644
--- a/storage/src/vespa/storage/storageserver/mergethrottler.cpp
+++ b/storage/src/vespa/storage/storageserver/mergethrottler.cpp
@@ -5,6 +5,7 @@
#include <vespa/storage/common/nodestateupdater.h>
#include <vespa/storage/persistence/messages.h>
#include <vespa/messagebus/message.h>
+#include <vespa/messagebus/error.h>
#include <vespa/config/common/exceptions.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -762,7 +763,7 @@ MergeThrottler::handleMessageUp(
if (mergeReply.getResult().getResult() != api::ReturnCode::OK) {
LOG(debug, "Merging failed for %s (%s)",
mergeReply.toString().c_str(),
- mergeReply.getResult().getMessage().c_str());
+ vespalib::string(mergeReply.getResult().getMessage()).c_str());
}
processMergeReply(msg, true, msgGuard);
diff --git a/storage/src/vespa/storage/storageserver/rpc/storage_api_rpc_service.cpp b/storage/src/vespa/storage/storageserver/rpc/storage_api_rpc_service.cpp
index cb1d0380bf1..c465315a5a6 100644
--- a/storage/src/vespa/storage/storageserver/rpc/storage_api_rpc_service.cpp
+++ b/storage/src/vespa/storage/storageserver/rpc/storage_api_rpc_service.cpp
@@ -208,7 +208,7 @@ void StorageApiRpcService::encode_rpc_v1_response(FRT_RPCRequest& request, api::
// TODO skip encoding header altogether if no relevant fields set?
protobuf::ResponseHeader hdr;
if (reply.getTrace().getLevel() > 0) {
- hdr.set_trace_payload(reply.getTrace().getRoot().encode());
+ hdr.set_trace_payload(reply.getTrace().encode());
}
// TODO consistent naming...
encode_header_into_rpc_params(hdr, *ret);
@@ -289,7 +289,7 @@ void StorageApiRpcService::RequestDone(FRT_RPCRequest* raw_req) {
assert(reply);
if (!hdr.trace_payload().empty()) {
- cmd.getTrace().getRoot().addChild(mbus::TraceNode::decode(hdr.trace_payload()));
+ cmd.getTrace().addChild(mbus::TraceNode::decode(hdr.trace_payload()));
}
if (cmd.getTrace().shouldTrace(TraceLevel::SEND_RECEIVE)) {
cmd.getTrace().trace(TraceLevel::SEND_RECEIVE,
diff --git a/storage/src/vespa/storage/visiting/memory_bounded_trace.cpp b/storage/src/vespa/storage/visiting/memory_bounded_trace.cpp
index 9f9eb2942c1..05bb026f238 100644
--- a/storage/src/vespa/storage/visiting/memory_bounded_trace.cpp
+++ b/storage/src/vespa/storage/visiting/memory_bounded_trace.cpp
@@ -6,7 +6,7 @@
namespace storage {
MemoryBoundedTrace::MemoryBoundedTrace(size_t softMemoryUpperBound)
- : _node(),
+ : _trace(),
_currentMemoryUsed(0),
_omittedNodes(0),
_omittedBytes(0),
@@ -14,53 +14,49 @@ MemoryBoundedTrace::MemoryBoundedTrace(size_t softMemoryUpperBound)
{
}
-namespace {
-
-size_t
-computeTraceTreeMemoryUsage(const mbus::TraceNode& node)
+bool
+MemoryBoundedTrace::add(const mbus::TraceNode& node)
{
- if (node.isLeaf()) {
- return node.getNote().size();
- }
- size_t childSum = 0;
- const uint32_t childCount = node.getNumChildren();
- for (uint32_t i = 0; i < childCount; ++i) {
- childSum += computeTraceTreeMemoryUsage(node.getChild(i));
+ const size_t nodeFootprint = node.computeMemoryUsage();
+
+ if (_currentMemoryUsed >= _softMemoryUpperBound) {
+ ++_omittedNodes;
+ _omittedBytes += nodeFootprint;
+ return false;
}
- return childSum;
+ _trace.addChild(vespalib::TraceNode(node));
+ _currentMemoryUsed += nodeFootprint;
+ return true;
}
-} // anon ns
-
bool
-MemoryBoundedTrace::add(const mbus::TraceNode& node)
+MemoryBoundedTrace::add(mbus::Trace && node)
{
- const size_t nodeFootprint = computeTraceTreeMemoryUsage(node);
+ const size_t nodeFootprint = node.computeMemoryUsage();
if (_currentMemoryUsed >= _softMemoryUpperBound) {
++_omittedNodes;
_omittedBytes += nodeFootprint;
return false;
}
- _node.addChild(node);
+ _trace.addChild(std::move(node));
_currentMemoryUsed += nodeFootprint;
return true;
}
void
-MemoryBoundedTrace::moveTraceTo(mbus::TraceNode& out)
+MemoryBoundedTrace::moveTraceTo(mbus::Trace& out)
{
- if (_node.isEmpty()) {
+ if (_trace.isEmpty()) {
return;
}
if (_omittedNodes > 0) {
- _node.addChild(vespalib::make_string(
+ _trace.trace(0, vespalib::make_string(
"Trace too large; omitted %zu subsequent trace trees "
"containing a total of %zu bytes",
- _omittedNodes, _omittedBytes));
+ _omittedNodes, _omittedBytes), false);
}
- out.addChild(_node); // XXX rvalue support should be added to TraceNode.
- _node.clear();
+ out.addChild(std::move(_trace));
_currentMemoryUsed = 0;
_omittedNodes = 0;
_omittedBytes = 0;
diff --git a/storage/src/vespa/storage/visiting/memory_bounded_trace.h b/storage/src/vespa/storage/visiting/memory_bounded_trace.h
index 71ce1be51ac..2c75c809af6 100644
--- a/storage/src/vespa/storage/visiting/memory_bounded_trace.h
+++ b/storage/src/vespa/storage/visiting/memory_bounded_trace.h
@@ -23,6 +23,7 @@ public:
* otherwise.
*/
bool add(const mbus::TraceNode& node);
+ bool add(mbus::Trace && trace);
/**
* Append current trace tree to the output trace node and clear internal
@@ -33,14 +34,14 @@ public:
*
* If current trace is empty, no nodes are added to `out`.
*/
- void moveTraceTo(mbus::TraceNode& out);
+ void moveTraceTo(mbus::Trace& out);
size_t getApproxMemoryUsed() const noexcept {
return _currentMemoryUsed;
}
private:
- mbus::TraceNode _node;
+ mbus::Trace _trace;
size_t _currentMemoryUsed;
size_t _omittedNodes;
size_t _omittedBytes;
diff --git a/storage/src/vespa/storage/visiting/visitor.cpp b/storage/src/vespa/storage/visiting/visitor.cpp
index 982bcd78f6f..16a9b26a754 100644
--- a/storage/src/vespa/storage/visiting/visitor.cpp
+++ b/storage/src/vespa/storage/visiting/visitor.cpp
@@ -6,6 +6,7 @@
#include <vespa/storageapi/message/datagram.h>
#include <vespa/storage/persistence/messages.h>
#include <vespa/documentapi/messagebus/messages/visitor.h>
+#include <vespa/documentapi/loadtypes/loadtype.h>
#include <vespa/document/select/node.h>
#include <vespa/document/fieldset/fieldsets.h>
#include <vespa/vespalib/stllike/hash_map.hpp>
@@ -146,9 +147,7 @@ Visitor::BucketIterationState::~BucketIterationState()
{
if (_iteratorId != 0) {
// Making the assumption that this is effectively nothrow.
- std::shared_ptr<DestroyIteratorCommand> cmd(
- new DestroyIteratorCommand(_iteratorId));
- cmd->setLoadType(_visitor._initiatingCmd->getLoadType());
+ auto cmd = std::make_shared<DestroyIteratorCommand>(_iteratorId);
cmd->getTrace().setLevel(_visitor._traceLevel);
cmd->setPriority(0);
@@ -178,7 +177,7 @@ Visitor::VisitorTarget::VisitorTarget()
{
}
-Visitor::VisitorTarget::~VisitorTarget() {}
+Visitor::VisitorTarget::~VisitorTarget() = default;
Visitor::Visitor(StorageComponent& component)
: _component(component),
@@ -223,10 +222,9 @@ Visitor::sendMessage(documentapi::DocumentMessage::UP cmd)
{
assert(cmd.get());
if (!isRunning()) return;
- cmd->setRoute(_dataDestination->getRoute());
+ cmd->setRoute(*_dataDestination);
cmd->setPriority(_documentPriority);
- cmd->setLoadType(_initiatingCmd->getLoadType());
framework::MicroSecTime time(_component.getClock().getTimeInMicros());
@@ -293,7 +291,7 @@ Visitor::sendInfoMessage(documentapi::VisitorInfoMessage::UP cmd)
if (!isRunning()) return;
if (_controlDestination->toString().length()) {
- cmd->setRoute(_controlDestination->getRoute());
+ cmd->setRoute(*_controlDestination);
cmd->setPriority(_documentPriority);
cmd->setTimeRemaining(std::chrono::milliseconds(_visitorInfoTimeout.getTime()));
auto& msgMeta = _visitorTarget.insertMessage(std::move(cmd));
@@ -372,10 +370,9 @@ Visitor::sendReplyOnce()
std::shared_ptr<api::StorageReply> reply(_initiatingCmd->makeReply());
_hitCounter->updateVisitorStatistics(_visitorStatistics);
- static_cast<api::CreateVisitorReply*>(reply.get())
- ->setVisitorStatistics(_visitorStatistics);
+ static_cast<api::CreateVisitorReply*>(reply.get())->setVisitorStatistics(_visitorStatistics);
if (shouldAddMbusTrace()) {
- _trace.moveTraceTo(reply->getTrace().getRoot());
+ _trace.moveTraceTo(reply->getTrace());
}
reply->setResult(_result);
LOG(debug, "Sending %s", reply->toString(true).c_str());
@@ -557,8 +554,8 @@ Visitor::start(api::VisitorId id, api::StorageMessage::Id cmdId,
void
Visitor::attach(std::shared_ptr<api::StorageCommand> initiatingCmd,
- const api::StorageMessageAddress& controlAddress,
- const api::StorageMessageAddress& dataAddress,
+ const mbus::Route& controlAddress,
+ const mbus::Route& dataAddress,
framework::MilliSecTime timeout)
{
_priority = initiatingCmd->getPriority();
@@ -572,9 +569,8 @@ Visitor::attach(std::shared_ptr<api::StorageCommand> initiatingCmd,
_traceLevel = _initiatingCmd->getTrace().getLevel();
{
// Set new address
- _controlDestination.reset(
- new api::StorageMessageAddress(controlAddress));
- _dataDestination.reset(new api::StorageMessageAddress(dataAddress));
+ _controlDestination = std::make_unique<mbus::Route>(controlAddress);
+ _dataDestination = std::make_unique<mbus::Route>(dataAddress);
}
LOG(debug, "Visitor '%s' has control destination %s and data "
"destination %s.",
@@ -603,15 +599,14 @@ bool
Visitor::addBoundedTrace(uint32_t level, const vespalib::string &message) {
mbus::Trace tempTrace;
tempTrace.trace(level, message);
- return _trace.add(tempTrace.getRoot());
+ return _trace.add(std::move(tempTrace));
}
void
-Visitor::handleDocumentApiReply(mbus::Reply::UP reply,
- VisitorThreadMetrics& metrics)
+Visitor::handleDocumentApiReply(mbus::Reply::UP reply, VisitorThreadMetrics& metrics)
{
if (shouldAddMbusTrace()) {
- _trace.add(reply->getTrace().getRoot());
+ _trace.add(reply->steal_trace());
}
mbus::Message::UP message = reply->getMessage();
@@ -736,7 +731,6 @@ Visitor::onCreateIteratorReply(
LOG(debug, "Visitor '%s' starting to visit bucket %s.",
_id.c_str(), bucketId.toString().c_str());
auto cmd = std::make_shared<GetIterCommand>(bucket, bucketState.getIteratorId(), _docBlockSize);
- cmd->setLoadType(_initiatingCmd->getLoadType());
cmd->getTrace().setLevel(_traceLevel);
cmd->setPriority(_priority);
++bucketState._pendingIterators;
@@ -832,7 +826,7 @@ Visitor::onGetIterReply(const std::shared_ptr<GetIterReply>& reply,
}
if (shouldAddMbusTrace()) {
- _trace.add(reply->getTrace().getRoot());
+ _trace.add(reply->steal_trace());
}
LOG(debug, "Continuing visitor %s.", _id.c_str());
@@ -1142,7 +1136,6 @@ Visitor::getIterators()
}
auto cmd = std::make_shared<GetIterCommand>(
bucketState.getBucket(), bucketState.getIteratorId(), _docBlockSize);
- cmd->setLoadType(_initiatingCmd->getLoadType());
cmd->getTrace().setLevel(_traceLevel);
cmd->setPriority(_priority);
_messageHandler->send(cmd, *this);
@@ -1188,7 +1181,6 @@ Visitor::getIterators()
spi::NEWEST_DOCUMENT_OR_REMOVE :
spi::NEWEST_DOCUMENT_ONLY));
- cmd->setLoadType(_initiatingCmd->getLoadType());
cmd->getTrace().setLevel(_traceLevel);
cmd->setPriority(_initiatingCmd->getPriority());
cmd->setReadConsistency(getRequiredReadConsistency());
diff --git a/storage/src/vespa/storage/visiting/visitor.h b/storage/src/vespa/storage/visiting/visitor.h
index 8a1f675a4c5..8eb02e6ccfc 100644
--- a/storage/src/vespa/storage/visiting/visitor.h
+++ b/storage/src/vespa/storage/visiting/visitor.h
@@ -330,8 +330,8 @@ protected:
documentapi::Priority::Value _documentPriority;
std::string _id;
- std::unique_ptr<api::StorageMessageAddress> _controlDestination;
- std::unique_ptr<api::StorageMessageAddress> _dataDestination;
+ std::unique_ptr<mbus::Route> _controlDestination;
+ std::unique_ptr<mbus::Route> _dataDestination;
std::shared_ptr<document::select::Node> _documentSelection;
std::string _documentSelectionString;
vdslib::VisitorStatistics _visitorStatistics;
@@ -355,10 +355,12 @@ public:
framework::MicroSecTime getStartTime() const { return _startTime; }
api::VisitorId getVisitorId() const { return _visitorId; }
const std::string& getVisitorName() const { return _id; }
- const api::StorageMessageAddress* getControlDestination() const
- { return _controlDestination.get(); } // Can't be null if attached
- const api::StorageMessageAddress* getDataDestination() const
- { return _dataDestination.get(); } // Can't be null if attached
+ const mbus::Route* getControlDestination() const {
+ return _controlDestination.get(); // Can't be null if attached
+ }
+ const mbus::Route* getDataDestination() const {
+ return _dataDestination.get(); // Can't be null if attached
+ }
void setMaxPending(unsigned int maxPending)
{ _visitorOptions._maxPending = maxPending; }
@@ -471,8 +473,8 @@ public:
documentapi::Priority::Value);
void attach(std::shared_ptr<api::StorageCommand> initiatingCmd,
- const api::StorageMessageAddress& controlAddress,
- const api::StorageMessageAddress& dataAddress,
+ const mbus::Route& controlAddress,
+ const mbus::Route& dataAddress,
framework::MilliSecTime timeout);
void handleDocumentApiReply(mbus::Reply::UP reply,
diff --git a/storage/src/vespa/storage/visiting/visitorthread.cpp b/storage/src/vespa/storage/visiting/visitorthread.cpp
index acaa96bc418..2839d3566aa 100644
--- a/storage/src/vespa/storage/visiting/visitorthread.cpp
+++ b/storage/src/vespa/storage/visiting/visitorthread.cpp
@@ -2,6 +2,7 @@
#include "visitorthread.h"
#include "messages.h"
+#include <vespa/documentapi/loadtypes/loadtype.h>
#include <vespa/document/select/bodyfielddetector.h>
#include <vespa/document/select/parser.h>
#include <vespa/messagebus/rpcmessagebus.h>
@@ -381,19 +382,18 @@ VisitorThread::createVisitor(vespalib::stringref libName,
}
namespace {
- std::unique_ptr<api::StorageMessageAddress>
- getDataAddress(const api::CreateVisitorCommand& cmd)
- {
- return std::make_unique<api::StorageMessageAddress>(
- mbus::Route::parse(cmd.getDataDestination()));
- }
- std::unique_ptr<api::StorageMessageAddress>
- getControlAddress(const api::CreateVisitorCommand& cmd)
- {
- return std::make_unique<api::StorageMessageAddress>(
- mbus::Route::parse(cmd.getControlDestination()));
- }
+std::unique_ptr<mbus::Route>
+getDataAddress(const api::CreateVisitorCommand& cmd)
+{
+ return std::make_unique<mbus::Route>(mbus::Route::parse(cmd.getDataDestination()));
+}
+
+std::unique_ptr<mbus::Route>
+getControlAddress(const api::CreateVisitorCommand& cmd)
+{
+ return std::make_unique<mbus::Route>(mbus::Route::parse(cmd.getControlDestination()));
+}
void
validateDocumentSelection(const document::DocumentTypeRepo& repo,
@@ -423,8 +423,8 @@ VisitorThread::onCreateVisitor(
assert(_currentlyRunningVisitor == _visitors.end());
ReturnCode result(ReturnCode::OK);
std::unique_ptr<document::select::Node> docSelection;
- std::unique_ptr<api::StorageMessageAddress> controlAddress;
- std::unique_ptr<api::StorageMessageAddress> dataAddress;
+ std::unique_ptr<mbus::Route> controlAddress;
+ std::unique_ptr<mbus::Route> dataAddress;
std::shared_ptr<Visitor> visitor;
do {
// If no buckets are specified, fail command
diff --git a/storageapi/src/tests/mbusprot/storageprotocoltest.cpp b/storageapi/src/tests/mbusprot/storageprotocoltest.cpp
index 9fd2c96f27e..e413a62ae39 100644
--- a/storageapi/src/tests/mbusprot/storageprotocoltest.cpp
+++ b/storageapi/src/tests/mbusprot/storageprotocoltest.cpp
@@ -16,9 +16,7 @@
#include <vespa/document/test/make_document_bucket.h>
#include <vespa/document/test/make_bucket_space.h>
#include <vespa/vespalib/util/growablebytebuffer.h>
-#include <vespa/vespalib/objects/nbostream.h>
-#include <iomanip>
#include <sstream>
#include <vespa/vespalib/gtest/gtest.h>
@@ -119,7 +117,7 @@ namespace {
TEST_F(StorageProtocolTest, testAddress50) {
StorageMessageAddress address("foo", lib::NodeType::STORAGE, 3);
EXPECT_EQ(vespalib::string("storage/cluster.foo/storage/3/default"),
- address.getRoute().toString());
+ address.to_mbus_route().toString());
}
template<typename Command> std::shared_ptr<Command>
@@ -162,11 +160,9 @@ StorageProtocolTest::copyReply(const std::shared_ptr<Reply>& m)
TEST_P(StorageProtocolTest, put) {
auto cmd = std::make_shared<PutCommand>(_bucket, _testDoc, 14);
cmd->setUpdateTimestamp(Timestamp(13));
- cmd->setLoadType(_loadTypes["foo"]);
auto cmd2 = copyCommand(cmd);
EXPECT_EQ(_bucket, cmd2->getBucket());
EXPECT_EQ(*_testDoc, *cmd2->getDocument());
- EXPECT_EQ(vespalib::string("foo"), cmd2->getLoadType().getName());
EXPECT_EQ(Timestamp(14), cmd2->getTimestamp());
EXPECT_EQ(Timestamp(13), cmd2->getUpdateTimestamp());
@@ -221,12 +217,10 @@ TEST_P(StorageProtocolTest, request_metadata_is_propagated) {
auto cmd = std::make_shared<PutCommand>(_bucket, _testDoc, 14);
cmd->forceMsgId(12345);
cmd->setPriority(50);
- cmd->setLoadType(_loadTypes["foo"]);
cmd->setSourceIndex(321);
auto cmd2 = copyCommand(cmd);
EXPECT_EQ(12345, cmd2->getMsgId());
EXPECT_EQ(50, cmd2->getPriority());
- EXPECT_EQ(_loadTypes["foo"].getId(), cmd2->getLoadType().getId());
EXPECT_EQ(321, cmd2->getSourceIndex());
}
@@ -821,4 +815,26 @@ TEST_P(StorageProtocolTest, serialized_size_is_used_to_set_approx_size_of_storag
}
}
+TEST_P(StorageProtocolTest, track_memory_footprint_for_some_messages) {
+ EXPECT_EQ(64u, sizeof(StorageMessage));
+ EXPECT_EQ(80u, sizeof(StorageReply));
+ EXPECT_EQ(104u, sizeof(BucketReply));
+ EXPECT_EQ(8u, sizeof(document::BucketId));
+ EXPECT_EQ(16u, sizeof(document::Bucket));
+ EXPECT_EQ(32u, sizeof(BucketInfo));
+ EXPECT_EQ(136u, sizeof(BucketInfoReply));
+ EXPECT_EQ(280u, sizeof(PutReply));
+ EXPECT_EQ(264u, sizeof(UpdateReply));
+ EXPECT_EQ(256u, sizeof(RemoveReply));
+ EXPECT_EQ(344u, sizeof(GetReply));
+ EXPECT_EQ(80u, sizeof(StorageCommand));
+ EXPECT_EQ(104u, sizeof(BucketCommand));
+ EXPECT_EQ(104u, sizeof(BucketInfoCommand));
+ EXPECT_EQ(104u + sizeof(std::string), sizeof(TestAndSetCommand));
+ EXPECT_EQ(136u + sizeof(std::string), sizeof(PutCommand));
+ EXPECT_EQ(136u + sizeof(std::string), sizeof(UpdateCommand));
+ EXPECT_EQ(216u + sizeof(std::string), sizeof(RemoveCommand));
+ EXPECT_EQ(288u, sizeof(GetCommand));
+}
+
} // storage::api
diff --git a/storageapi/src/tests/messageapi/storage_message_address_test.cpp b/storageapi/src/tests/messageapi/storage_message_address_test.cpp
index c340cba4b28..f7f254e9119 100644
--- a/storageapi/src/tests/messageapi/storage_message_address_test.cpp
+++ b/storageapi/src/tests/messageapi/storage_message_address_test.cpp
@@ -22,15 +22,19 @@ TEST(StorageMessageAddressTest, storage_hash_covers_all_expected_fields) {
hash_of("foo", lib::NodeType::DISTRIBUTOR, 0));
EXPECT_EQ(hash_of("foo", lib::NodeType::STORAGE, 123),
hash_of("foo", lib::NodeType::STORAGE, 123));
+ EXPECT_EQ(hash_of("foo", lib::NodeType::STORAGE, 0),
+ hash_of("bar", lib::NodeType::STORAGE, 0));
// These tests are all true with extremely high probability, though they do
// depend on a hash function that may inherently cause collisions.
EXPECT_NE(hash_of("foo", lib::NodeType::STORAGE, 0),
- hash_of("bar", lib::NodeType::STORAGE, 0));
- EXPECT_NE(hash_of("foo", lib::NodeType::STORAGE, 0),
hash_of("foo", lib::NodeType::DISTRIBUTOR, 0));
EXPECT_NE(hash_of("foo", lib::NodeType::STORAGE, 0),
hash_of("foo", lib::NodeType::STORAGE, 1));
+
+ EXPECT_EQ(88u, sizeof(StorageMessageAddress));
+ EXPECT_EQ(64u, sizeof(StorageMessage));
+ EXPECT_EQ(16u, sizeof(mbus::Trace));
}
} // storage::api
diff --git a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp
index 8a0204c8fc9..2c7b4a1e6f8 100644
--- a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp
+++ b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_0.cpp
@@ -102,7 +102,7 @@ ProtocolSerialization5_0::onDecodeCommand(BBuf& buf, api::StorageCommand& msg) c
uint8_t priority = SH::getByte(buf);
msg.setPriority(priority);
msg.setSourceIndex(SH::getShort(buf));
- msg.setLoadType(_loadTypes[SH::getInt(buf)]);
+ (void)SH::getInt(buf);
}
diff --git a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.cpp b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.cpp
index b6d0d297a69..ee577881d82 100644
--- a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.cpp
+++ b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.cpp
@@ -5,8 +5,7 @@
#include "storagecommand.h"
#include "serializationhelper.h"
-namespace storage {
-namespace mbusprot {
+namespace storage::mbusprot {
using documentapi::TestAndSetCondition;
@@ -62,5 +61,4 @@ void ProtocolSerialization5_2::encodeTasCondition(GBBuf & buf, const api::Storag
buf.putString(cmd.getCondition().getSelection());
}
-} // mbusprot
-} // storage
+}
diff --git a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.h b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.h
index 42e9c17e192..f6f9443248c 100644
--- a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.h
+++ b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization5_2.h
@@ -7,8 +7,7 @@
#include <vespa/vespalib/util/growablebytebuffer.h>
#include <vespa/storageapi/message/persistence.h>
-namespace storage {
-namespace mbusprot {
+namespace storage::mbusprot {
class ProtocolSerialization5_2 : public ProtocolSerialization5_1
{
@@ -31,5 +30,4 @@ protected:
static void encodeTasCondition(GBBuf & buf, const api::StorageCommand & cmd);
};
-} // mbusprot
-} // storage
+}
diff --git a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization7.cpp b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization7.cpp
index 5d66d86036c..b356ce59999 100644
--- a/storageapi/src/vespa/storageapi/mbusprot/protocolserialization7.cpp
+++ b/storageapi/src/vespa/storageapi/mbusprot/protocolserialization7.cpp
@@ -253,7 +253,6 @@ public:
dest.forceMsgId(_hdr.message_id());
dest.setPriority(static_cast<uint8_t>(_hdr.priority()));
dest.setSourceIndex(static_cast<uint16_t>(_hdr.source_index()));
- dest.setLoadType(_load_types[_hdr.loadtype_id()]);
}
ProtobufType& request() noexcept { return *_proto_obj; }
diff --git a/storageapi/src/vespa/storageapi/message/bucket.h b/storageapi/src/vespa/storageapi/message/bucket.h
index e5ec7698329..072d02c7d32 100644
--- a/storageapi/src/vespa/storageapi/message/bucket.h
+++ b/storageapi/src/vespa/storageapi/message/bucket.h
@@ -199,7 +199,7 @@ public:
GetBucketDiffCommand(const document::Bucket &bucket,
const std::vector<Node>&,
Timestamp maxTimestamp);
- ~GetBucketDiffCommand();
+ ~GetBucketDiffCommand() override;
const std::vector<Node>& getNodes() const { return _nodes; }
Timestamp getMaxTimestamp() const { return _maxTimestamp; }
diff --git a/storageapi/src/vespa/storageapi/message/visitor.h b/storageapi/src/vespa/storageapi/message/visitor.h
index 67c41a0cc4d..8440591ecde 100644
--- a/storageapi/src/vespa/storageapi/message/visitor.h
+++ b/storageapi/src/vespa/storageapi/message/visitor.h
@@ -205,18 +205,19 @@ public:
VisitorInfoCommand();
~VisitorInfoCommand() override;
- void setErrorCode(const ReturnCode& code) { _error = code; }
+ void setErrorCode(ReturnCode && code) { _error = std::move(code); }
void setCompleted() { _completed = true; }
- void setBucketCompleted(const document::BucketId& id, Timestamp lastVisited)
- {
+ void setBucketCompleted(const document::BucketId& id, Timestamp lastVisited) {
_bucketsCompleted.push_back(BucketTimestampPair(id, lastVisited));
}
- void setBucketsCompleted(const std::vector<BucketTimestampPair>& bc)
- { _bucketsCompleted = bc; }
+ void setBucketsCompleted(const std::vector<BucketTimestampPair>& bc) {
+ _bucketsCompleted = bc;
+ }
const ReturnCode& getErrorCode() const { return _error; }
- const std::vector<BucketTimestampPair>& getCompletedBucketsList() const
- { return _bucketsCompleted; }
+ const std::vector<BucketTimestampPair>& getCompletedBucketsList() const {
+ return _bucketsCompleted;
+ }
bool visitorCompleted() const { return _completed; }
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
diff --git a/storageapi/src/vespa/storageapi/messageapi/bucketreply.cpp b/storageapi/src/vespa/storageapi/messageapi/bucketreply.cpp
index 1385fc331ac..e5fb7764aeb 100644
--- a/storageapi/src/vespa/storageapi/messageapi/bucketreply.cpp
+++ b/storageapi/src/vespa/storageapi/messageapi/bucketreply.cpp
@@ -7,8 +7,7 @@
using document::Bucket;
using document::BucketId;
-namespace storage {
-namespace api {
+namespace storage::api {
BucketReply::BucketReply(const BucketCommand& cmd,
const ReturnCode& code)
@@ -42,5 +41,4 @@ BucketReply::print(std::ostream& out, bool verbose,
}
}
-} // api
-} // storage
+}
diff --git a/storageapi/src/vespa/storageapi/messageapi/returncode.cpp b/storageapi/src/vespa/storageapi/messageapi/returncode.cpp
index d5c8cb7da68..dbe2f602703 100644
--- a/storageapi/src/vespa/storageapi/messageapi/returncode.cpp
+++ b/storageapi/src/vespa/storageapi/messageapi/returncode.cpp
@@ -10,17 +10,39 @@ ReturnCode::ReturnCode()
_message()
{}
-ReturnCode::ReturnCode(const ReturnCode &) = default;
-ReturnCode & ReturnCode::operator = (const ReturnCode &) = default;
ReturnCode & ReturnCode::operator = (ReturnCode &&) noexcept = default;
ReturnCode::~ReturnCode() = default;
-ReturnCode::ReturnCode(Result result, vespalib::stringref msg)
+ReturnCode::ReturnCode(Result result)
: _result(result),
- _message(msg)
+ _message()
{}
-vespalib::string ReturnCode::getResultString(Result result) {
+ReturnCode::ReturnCode(Result result, vespalib::stringref msg)
+ : _result(result),
+ _message()
+{
+ if ( ! msg.empty()) {
+ _message = std::make_unique<vespalib::string>(msg);
+ }
+}
+
+ReturnCode::ReturnCode(const ReturnCode & rhs)
+ : _result(rhs._result),
+ _message()
+{
+ if (rhs._message) {
+ _message = std::make_unique<vespalib::string>(*rhs._message);
+ }
+}
+
+ReturnCode &
+ReturnCode::operator = (const ReturnCode & rhs) {
+ return operator=(ReturnCode(rhs));
+}
+
+vespalib::string
+ReturnCode::getResultString(Result result) {
return documentapi::DocumentProtocol::getErrorName(result);
}
@@ -28,9 +50,9 @@ vespalib::string
ReturnCode::toString() const {
vespalib::string ret = "ReturnCode(";
ret += getResultString(_result);
- if ( ! _message.empty()) {
+ if ( _message && ! _message->empty()) {
ret += ", ";
- ret += _message;
+ ret += *_message;
}
ret += ")";
return ret;
@@ -146,4 +168,20 @@ ReturnCode::isBucketDisappearance() const
}
}
+vespalib::stringref
+ReturnCode::getMessage() const {
+ return _message
+ ? _message->operator vespalib::stringref()
+ : vespalib::stringref();
+}
+
+bool
+ReturnCode::operator==(const ReturnCode& code) const {
+ return (_result == code._result) && (getMessage() == code.getMessage());
+}
+
+bool
+ReturnCode::operator!=(const ReturnCode& code) const {
+ return (_result != code._result) || (getMessage() != code.getMessage());
+}
}
diff --git a/storageapi/src/vespa/storageapi/messageapi/returncode.h b/storageapi/src/vespa/storageapi/messageapi/returncode.h
index 0149ae29a05..bef59a334d9 100644
--- a/storageapi/src/vespa/storageapi/messageapi/returncode.h
+++ b/storageapi/src/vespa/storageapi/messageapi/returncode.h
@@ -59,18 +59,18 @@ public:
private:
Result _result;
- vespalib::string _message;
+ std::unique_ptr<vespalib::string> _message;
public:
ReturnCode();
- explicit ReturnCode(Result result, vespalib::stringref msg = "");
+ explicit ReturnCode(Result result);
+ explicit ReturnCode(Result result, vespalib::stringref msg);
ReturnCode(const ReturnCode &);
ReturnCode & operator = (const ReturnCode &);
ReturnCode(ReturnCode &&) noexcept = default;
ReturnCode & operator = (ReturnCode &&) noexcept;
~ReturnCode();
- const vespalib::string& getMessage() const { return _message; }
- void setMessage(vespalib::stringref message) { _message = message; }
+ vespalib::stringref getMessage() const;
Result getResult() const { return _result; }
@@ -85,10 +85,8 @@ public:
bool operator==(Result res) const { return _result == res; }
bool operator!=(Result res) const { return _result != res; }
- bool operator==(const ReturnCode& code) const
- { return _result == code._result && _message == code._message; }
- bool operator!=(const ReturnCode& code) const
- { return _result != code._result || _message != code._message; }
+ bool operator==(const ReturnCode& code) const;
+ bool operator!=(const ReturnCode& code) const;
// To avoid lots of code matching various return codes in storage, we define
// some functions they can use to match those codes that corresponds to what
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagecommand.cpp b/storageapi/src/vespa/storageapi/messageapi/storagecommand.cpp
index d9bbf34141a..397c869f4fb 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagecommand.cpp
+++ b/storageapi/src/vespa/storageapi/messageapi/storagecommand.cpp
@@ -11,7 +11,6 @@ StorageCommand::StorageCommand(const StorageCommand& other)
_timeout(other._timeout),
_sourceIndex(other._sourceIndex)
{
- setTrace(other.getTrace());
}
StorageCommand::StorageCommand(const MessageType& type, Priority p)
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagecommand.h b/storageapi/src/vespa/storageapi/messageapi/storagecommand.h
index c835168c5b7..bd55ee57a8f 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagecommand.h
+++ b/storageapi/src/vespa/storageapi/messageapi/storagecommand.h
@@ -30,7 +30,7 @@ protected:
public:
DECLARE_POINTER_TYPEDEFS(StorageCommand);
- virtual ~StorageCommand();
+ ~StorageCommand() override;
bool sourceIndexSet() const { return (_sourceIndex != 0xffff); }
void setSourceIndex(uint16_t sourceIndex) { _sourceIndex = sourceIndex; }
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp b/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp
index 0f5546a5510..9c5df379d22 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp
+++ b/storageapi/src/vespa/storageapi/messageapi/storagemessage.cpp
@@ -1,7 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "storagemessage.h"
-
+#include <vespa/documentapi/loadtypes/loadtype.h>
#include <vespa/messagebus/routing/verbatimdirective.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/stllike/asciistream.h>
@@ -139,15 +139,6 @@ MessageType::print(std::ostream& out, bool verbose, const std::string& indent) c
out << ")";
}
-StorageMessageAddress::StorageMessageAddress(const mbus::Route& route)
- : _route(route),
- _protocol(DOCUMENT),
- _precomputed_storage_hash(0),
- _cluster(""),
- _type(nullptr),
- _index(0xFFFF)
-{ }
-
std::ostream & operator << (std::ostream & os, const StorageMessageAddress & addr) {
return os << addr.toString();
}
@@ -160,39 +151,45 @@ createAddress(vespalib::stringref cluster, const lib::NodeType& type, uint16_t i
return os.str();
}
+size_t
+calculate_node_hash(const lib::NodeType& type, uint16_t index)
+{
+ uint16_t buf[] = { type, index };
+ return vespalib::hashValue(&buf, sizeof(buf));
+}
+
// TODO we ideally want this removed. Currently just in place to support usage as map key when emplacement not available
StorageMessageAddress::StorageMessageAddress()
- : _route(),
- _protocol(Protocol::STORAGE),
+ : _cluster(),
_precomputed_storage_hash(0),
- _cluster(),
_type(nullptr),
+ _protocol(Protocol::STORAGE),
_index(0)
{}
-
StorageMessageAddress::StorageMessageAddress(vespalib::stringref cluster, const lib::NodeType& type,
uint16_t index, Protocol protocol)
- : _route(),
- _protocol(protocol),
- _precomputed_storage_hash(0),
- _cluster(cluster),
+ : _cluster(cluster),
+ _precomputed_storage_hash(calculate_node_hash(type, index)),
_type(&type),
+ _protocol(protocol),
_index(index)
{
- std::vector<mbus::IHopDirective::SP> directives;
- auto address_as_str = createAddress(cluster, type, index);
- // We reuse the string representation and pass it to vespalib's hashValue instead of
- // explicitly combining a running hash over the individual fields. This is because
- // hashValue internally uses xxhash, which offers great dispersion of bits even for
- // minimal changes in the input (such as single bit differences in the index).
- _precomputed_storage_hash = vespalib::hashValue(address_as_str.data(), address_as_str.size());
- directives.emplace_back(std::make_shared<mbus::VerbatimDirective>(std::move(address_as_str)));
- _route.addHop(mbus::Hop(std::move(directives), false));
}
StorageMessageAddress::~StorageMessageAddress() = default;
+mbus::Route
+StorageMessageAddress::to_mbus_route() const
+{
+ mbus::Route result;
+ auto address_as_str = createAddress(_cluster, *_type, _index);
+ std::vector<mbus::IHopDirective::SP> directives;
+ directives.emplace_back(std::make_shared<mbus::VerbatimDirective>(std::move(address_as_str)));
+ result.addHop(mbus::Hop(std::move(directives), false));
+ return result;
+}
+
uint16_t
StorageMessageAddress::getIndex() const
{
@@ -251,7 +248,7 @@ StorageMessageAddress::print(vespalib::asciistream & out) const
out << "Document protocol";
}
if (!_type) {
- out << ", " << _route.toString() << ")";
+ out << ", " << to_mbus_route().toString() << ")";
} else {
out << ", cluster " << _cluster << ", nodetype " << *_type
<< ", index " << _index << ")";
@@ -269,26 +266,32 @@ StorageMessage::generateMsgId()
StorageMessage::StorageMessage(const MessageType& type, Id id)
: _type(type),
_msgId(id),
- _priority(NORMAL),
_address(),
- _loadType(documentapi::LoadType::DEFAULT),
- _approxByteSize(50)
+ _trace(),
+ _approxByteSize(50),
+ _priority(NORMAL)
{
}
StorageMessage::StorageMessage(const StorageMessage& other, Id id)
: _type(other._type),
_msgId(id),
- _priority(other._priority),
_address(),
- _loadType(other._loadType),
- _approxByteSize(other._approxByteSize)
+ _trace(other.getTrace().getLevel()),
+ _approxByteSize(other._approxByteSize),
+ _priority(other._priority)
{
}
StorageMessage::~StorageMessage() = default;
-void StorageMessage::setNewMsgId()
+const documentapi::LoadType&
+StorageMessage::getLoadType() const {
+ return documentapi::LoadType::DEFAULT;
+}
+
+void
+StorageMessage::setNewMsgId()
{
_msgId = generateMsgId();
}
@@ -298,7 +301,8 @@ StorageMessage::getSummary() const {
return toString();
}
-const char* to_string(LockingRequirements req) noexcept {
+const char*
+to_string(LockingRequirements req) noexcept {
switch (req) {
case LockingRequirements::Exclusive: return "Exclusive";
case LockingRequirements::Shared: return "Shared";
@@ -306,12 +310,14 @@ const char* to_string(LockingRequirements req) noexcept {
}
}
-std::ostream& operator<<(std::ostream& os, LockingRequirements req) {
+std::ostream&
+operator<<(std::ostream& os, LockingRequirements req) {
os << to_string(req);
return os;
}
-const char* to_string(InternalReadConsistency consistency) noexcept {
+const char*
+to_string(InternalReadConsistency consistency) noexcept {
switch (consistency) {
case InternalReadConsistency::Strong: return "Strong";
case InternalReadConsistency::Weak: return "Weak";
@@ -319,7 +325,8 @@ const char* to_string(InternalReadConsistency consistency) noexcept {
}
}
-std::ostream& operator<<(std::ostream& os, InternalReadConsistency consistency) {
+std::ostream&
+operator<<(std::ostream& os, InternalReadConsistency consistency) {
os << to_string(consistency);
return os;
}
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagemessage.h b/storageapi/src/vespa/storageapi/messageapi/storagemessage.h
index aa23d3a0cae..98552e473c1 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagemessage.h
+++ b/storageapi/src/vespa/storageapi/messageapi/storagemessage.h
@@ -12,7 +12,6 @@
#pragma once
#include "messagehandler.h"
-#include <vespa/documentapi/loadtypes/loadtype.h>
#include <vespa/messagebus/routing/route.h>
#include <vespa/messagebus/trace.h>
#include <vespa/vdslib/state/nodetype.h>
@@ -21,9 +20,8 @@
#include <map>
#include <iosfwd>
-namespace vespalib {
- class asciistream;
-}
+namespace vespalib { class asciistream; }
+namespace documentapi { class LoadType; }
// The following macros are provided as a way to write storage messages simply.
// They implement the parts of the code that can easily be automaticly
// generated.
@@ -268,17 +266,15 @@ public:
enum Protocol { STORAGE, DOCUMENT };
private:
- mbus::Route _route;
- Protocol _protocol;
+ vespalib::string _cluster;
// Used for internal VDS addresses only
size_t _precomputed_storage_hash;
- vespalib::string _cluster;
const lib::NodeType* _type;
+ Protocol _protocol;
uint16_t _index;
public:
StorageMessageAddress(); // Only to be used when transient default ctor semantics are needed by containers
- StorageMessageAddress(const mbus::Route& route);
StorageMessageAddress(vespalib::stringref clusterName,
const lib::NodeType& type, uint16_t index,
Protocol protocol = STORAGE);
@@ -286,13 +282,13 @@ public:
void setProtocol(Protocol p) { _protocol = p; }
- const mbus::Route& getRoute() const { return _route; }
+ mbus::Route to_mbus_route() const;
Protocol getProtocol() const { return _protocol; }
uint16_t getIndex() const;
const lib::NodeType& getNodeType() const;
const vespalib::string& getCluster() const;
- // Returns precomputed hash over <cluster, type, index> tuple. Other fields not included.
+ // Returns precomputed hash over <type, index> pair. Other fields not included.
[[nodiscard]] size_t internal_storage_hash() const noexcept {
return _precomputed_storage_hash;
}
@@ -359,12 +355,11 @@ protected:
static Id generateMsgId();
const MessageType& _type;
- Id _msgId;
- Priority _priority;
+ Id _msgId;
std::unique_ptr<StorageMessageAddress> _address;
- documentapi::LoadType _loadType;
- mbus::Trace _trace;
- uint32_t _approxByteSize;
+ vespalib::Trace _trace;
+ uint32_t _approxByteSize;
+ Priority _priority;
StorageMessage(const MessageType& code, Id id);
StorageMessage(const StorageMessage&, Id id);
@@ -373,7 +368,7 @@ protected:
public:
StorageMessage& operator=(const StorageMessage&) = delete;
StorageMessage(const StorageMessage&) = delete;
- virtual ~StorageMessage();
+ ~StorageMessage() override;
Id getMsgId() const { return _msgId; }
@@ -394,7 +389,7 @@ public:
const StorageMessageAddress* getAddress() const { return _address.get(); }
void setAddress(const StorageMessageAddress& address) {
- _address.reset(new StorageMessageAddress(address));
+ _address = std::make_unique<StorageMessageAddress>(address);
}
/**
@@ -432,16 +427,16 @@ public:
*/
virtual bool callHandler(MessageHandler&, const StorageMessage::SP&) const = 0;
- const documentapi::LoadType& getLoadType() const { return _loadType; }
- void setLoadType(const documentapi::LoadType& type) { _loadType = type; }
+ const documentapi::LoadType& getLoadType() const;
+ mbus::Trace && steal_trace() { return std::move(_trace); }
mbus::Trace& getTrace() { return _trace; }
const mbus::Trace& getTrace() const { return _trace; }
/**
Sets the trace object for this message.
*/
- void setTrace(const mbus::Trace &trace) { _trace = trace; }
+ void setTrace(vespalib::Trace && trace) { _trace = std::move(trace); }
/**
* Cheap version of tostring().
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagereply.cpp b/storageapi/src/vespa/storageapi/messageapi/storagereply.cpp
index 81cdadb3623..2bb9fabd7d5 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagereply.cpp
+++ b/storageapi/src/vespa/storageapi/messageapi/storagereply.cpp
@@ -14,7 +14,12 @@ StorageReply::StorageReply(const StorageCommand& cmd, ReturnCode code)
if (cmd.getAddress()) {
setAddress(*cmd.getAddress());
}
- setTrace(cmd.getTrace());
+ // TODD do we really need copy construction
+ if ( ! cmd.getTrace().isEmpty()) {
+ setTrace(vespalib::Trace(cmd.getTrace()));
+ } else {
+ getTrace().setLevel(cmd.getTrace().getLevel());
+ }
setTransportContext(cmd.getTransportContext());
}
diff --git a/storageapi/src/vespa/storageapi/messageapi/storagereply.h b/storageapi/src/vespa/storageapi/messageapi/storagereply.h
index 1a3bbe35eb4..53516949110 100644
--- a/storageapi/src/vespa/storageapi/messageapi/storagereply.h
+++ b/storageapi/src/vespa/storageapi/messageapi/storagereply.h
@@ -30,7 +30,7 @@ public:
~StorageReply() override;
DECLARE_POINTER_TYPEDEFS(StorageReply);
- void setResult(const ReturnCode& r) { _result = r; }
+ void setResult(ReturnCode r) { _result = std::move(r); }
void setResult(ReturnCode::Result r) { _result = ReturnCode(r); }
const ReturnCode& getResult() const { return _result; }
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
diff --git a/storageserver/src/tests/storageservertest.cpp b/storageserver/src/tests/storageservertest.cpp
index 2f165c69470..363dc6240dd 100644
--- a/storageserver/src/tests/storageservertest.cpp
+++ b/storageserver/src/tests/storageservertest.cpp
@@ -5,6 +5,7 @@
#include <vespa/storage/storageserver/servicelayernode.h>
#include <vespa/storageserver/app/distributorprocess.h>
#include <vespa/storageserver/app/dummyservicelayerprocess.h>
+#include <vespa/messagebus/message.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/log/log.h>
@@ -21,7 +22,7 @@ struct StorageServerTest : public ::testing::Test {
std::unique_ptr<vdstestlib::DirConfig> storConfig;
StorageServerTest();
- ~StorageServerTest();
+ ~StorageServerTest() override;
void SetUp() override;
void TearDown() override;
@@ -34,7 +35,7 @@ StorageServerTest::~StorageServerTest() = default;
namespace {
struct Node {
- virtual ~Node() {}
+ virtual ~Node() = default;
virtual StorageNode& getNode() = 0;
virtual StorageNodeContext& getContext() = 0;
};
@@ -43,10 +44,10 @@ struct Distributor : public Node {
DistributorProcess _process;
Distributor(vdstestlib::DirConfig& config);
- ~Distributor();
+ ~Distributor() override;
- virtual StorageNode& getNode() override { return _process.getNode(); }
- virtual StorageNodeContext& getContext() override { return _process.getContext(); }
+ StorageNode& getNode() override { return _process.getNode(); }
+ StorageNodeContext& getContext() override { return _process.getContext(); }
};
struct Storage : public Node {
@@ -54,10 +55,10 @@ struct Storage : public Node {
StorageComponent::UP _component;
Storage(vdstestlib::DirConfig& config);
- ~Storage();
+ ~Storage() override;
- virtual StorageNode& getNode() override { return _process.getNode(); }
- virtual StorageNodeContext& getContext() override { return _process.getContext(); }
+ StorageNode& getNode() override { return _process.getNode(); }
+ StorageNodeContext& getContext() override { return _process.getContext(); }
};
Distributor::Distributor(vdstestlib::DirConfig& config)
@@ -74,8 +75,8 @@ Storage::Storage(vdstestlib::DirConfig& config)
{
_process.setupConfig(60000ms);
_process.createNode();
- _component.reset(new StorageComponent(
- getContext().getComponentRegister(), "test"));
+ _component = std::make_unique<StorageComponent>(
+ getContext().getComponentRegister(), "test");
}
Storage::~Storage() = default;
@@ -87,9 +88,9 @@ StorageServerTest::SetUp()
{
[[maybe_unused]] int systemResult = system("chmod -R 755 vdsroot");
systemResult = system("rm -rf vdsroot*");
- slobrok.reset(new mbus::Slobrok);
- distConfig.reset(new vdstestlib::DirConfig(getStandardConfig(false)));
- storConfig.reset(new vdstestlib::DirConfig(getStandardConfig(true)));
+ slobrok = std::make_unique<mbus::Slobrok>();
+ distConfig = std::make_unique<vdstestlib::DirConfig>(getStandardConfig(false));
+ storConfig = std::make_unique<vdstestlib::DirConfig>(getStandardConfig(true));
addSlobrokConfig(*distConfig, *slobrok);
addSlobrokConfig(*storConfig, *slobrok);
storConfig->getConfig("stor-filestor").set("fail_disk_after_error_count", "1");
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
index 8cfbfd204ba..7119bde7a09 100644
--- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
@@ -4,6 +4,7 @@ package ai.vespa.hosted.plugin;
import ai.vespa.hosted.api.ControllerHttpClient;
import ai.vespa.hosted.api.Properties;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.yolean.Exceptions;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -15,6 +16,10 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.joining;
/**
* Base class for hosted Vespa plugin mojos.
@@ -61,7 +66,11 @@ public abstract class AbstractVespaMojo extends AbstractMojo {
throw e;
}
catch (Exception e) {
- throw new MojoExecutionException("Execution failed for application " + name(), e);
+ String message = "Execution failed for application " + name() + ":\n" + Exceptions.toMessageString(e);
+ if (e.getSuppressed().length > 0)
+ message += "\nSuppressed:\n" + Stream.of(e.getSuppressed()).map(Exceptions::toMessageString).collect(joining("\n"));
+
+ throw new MojoExecutionException(message, e);
}
}
diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/StripedExecutor.java b/vespajlib/src/main/java/com/yahoo/concurrent/StripedExecutor.java
index 37b560d1129..f5d5d1529bb 100644
--- a/vespajlib/src/main/java/com/yahoo/concurrent/StripedExecutor.java
+++ b/vespajlib/src/main/java/com/yahoo/concurrent/StripedExecutor.java
@@ -68,7 +68,7 @@ public class StripedExecutor<Key> {
command.run();
}
catch (RuntimeException e) {
- logger.log(Level.WARNING, () -> "Exception caught: " + Exceptions.toMessageString(e));
+ logger.log(Level.WARNING, e, () -> "Exception caught: " + Exceptions.toMessageString(e));
}
}
}
diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/ThreadLocalDirectory.java b/vespajlib/src/main/java/com/yahoo/concurrent/ThreadLocalDirectory.java
index d5235caef9f..5ab1c88775a 100644
--- a/vespajlib/src/main/java/com/yahoo/concurrent/ThreadLocalDirectory.java
+++ b/vespajlib/src/main/java/com/yahoo/concurrent/ThreadLocalDirectory.java
@@ -62,14 +62,13 @@ import java.util.List;
* example.
* </p>
*
- * @param AGGREGATOR
- * the type input data is aggregated into
- * @param SAMPLE
- * the type of input data
+ * @param <AGGREGATOR> the type input data is aggregated into
+ * @param <SAMPLE> the type of input data
*
- * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ * @author Steinar Knutsen
*/
public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
+
/**
* Factory interface to create the data container for each generation of
* samples, and putting data into it.
@@ -85,12 +84,11 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
* need to implement both.
* </p>
*
- * @param AGGREGATOR
- * The type of the data container to produce
- * @param SAMPLE
- * The type of the incoming data to store in the container.
+ * @param <AGGREGATOR> the type of the data container to produce
+ * @param <SAMPLE> the type of the incoming data to store in the container.
*/
public interface Updater<AGGREGATOR, SAMPLE> {
+
/**
* Create data container to receive produced data. This is invoked once
* on every instance every time ThreadLocalDirectory.fetch() is invoked.
@@ -137,7 +135,7 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
*
* @return a fresh structure to receive data
*/
- public AGGREGATOR createGenerationInstance(AGGREGATOR previous);
+ AGGREGATOR createGenerationInstance(AGGREGATOR previous);
/**
* Insert a data element of type S into the current generation of data
@@ -180,7 +178,8 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
* the data to insert
* @return the new current value, may be the same as previous
*/
- public AGGREGATOR update(AGGREGATOR current, SAMPLE x);
+ AGGREGATOR update(AGGREGATOR current, SAMPLE x);
+
}
/**
@@ -188,14 +187,12 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
* ThreadLocalDirectory without resetting the local instances in each
* thread.
*
- * @param <AGGREGATOR>
- * as for {@link Updater}
- * @param <SAMPLE>
- * as for {@link Updater}
+ * @param <AGGREGATOR> as for {@link Updater}
+ * @param <SAMPLE> as for {@link Updater}
* @see ThreadLocalDirectory#view()
*/
- public interface ObservableUpdater<AGGREGATOR, SAMPLE> extends
- Updater<AGGREGATOR, SAMPLE> {
+ public interface ObservableUpdater<AGGREGATOR, SAMPLE> extends Updater<AGGREGATOR, SAMPLE> {
+
/**
* Create an application specific copy of the AGGREGATOR for a thread.
*
@@ -203,7 +200,8 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
* the AGGREGATOR instance to copy
* @return a copy of the incoming parameter
*/
- public AGGREGATOR copy(AGGREGATOR current);
+ AGGREGATOR copy(AGGREGATOR current);
+
}
private final ThreadLocal<LocalInstance<AGGREGATOR, SAMPLE>> local = new ThreadLocal<>();
@@ -268,8 +266,7 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
* to have been instantiated with an updater implementing ObservableUpdater.
*
* @return a list of a copy of the current data in all producer threads
- * @throws IllegalStateException
- * if the updater does not implement {@link ObservableUpdater}
+ * @throws IllegalStateException if the updater does not implement {@link ObservableUpdater}
*/
public List<AGGREGATOR> view() {
if (observableUpdater == null) {
@@ -310,8 +307,7 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
/**
* Input data from a producer thread.
*
- * @param x
- * the data to insert
+ * @param x the data to insert
*/
public void update(SAMPLE x) {
update(x, getOrCreateLocal());
@@ -330,10 +326,8 @@ public final class ThreadLocalDirectory<AGGREGATOR, SAMPLE> {
* calls necessary to update(SAMPLE, LocalInstance&lt;AGGREGATOR, SAMPLE&gt;).
* </p>
*
- * @param x
- * the data to insert
- * @param localInstance
- * the local data insertion instance
+ * @param x the data to insert
+ * @param localInstance the local data insertion instance
*/
public void update(SAMPLE x, LocalInstance<AGGREGATOR, SAMPLE> localInstance) {
boolean isRegistered;
diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java
index 242bdb1161e..2c123779a1e 100644
--- a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java
+++ b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java
@@ -47,6 +47,7 @@ public abstract class Maintainer implements Runnable, AutoCloseable {
@Override
public void run() {
+ log.log(Level.FINE, () -> "Running " + this.getClass().getSimpleName());
try {
if (jobControl.isActive(name())) {
lockAndMaintain();
@@ -56,6 +57,7 @@ public abstract class Maintainer implements Runnable, AutoCloseable {
} catch (Throwable e) {
log.log(Level.WARNING, this + " failed. Will retry in " + interval.toMinutes() + " minutes", e);
}
+ log.log(Level.FINE, () -> "Finished " + this.getClass().getSimpleName());
}
@Override
diff --git a/vespalib/src/tests/spin_lock/spin_lock_test.cpp b/vespalib/src/tests/spin_lock/spin_lock_test.cpp
index 5ba0ca16222..3542bd5d51f 100644
--- a/vespalib/src/tests/spin_lock/spin_lock_test.cpp
+++ b/vespalib/src/tests/spin_lock/spin_lock_test.cpp
@@ -4,6 +4,7 @@
#include <vespa/vespalib/util/benchmark_timer.h>
#include <vespa/vespalib/util/time.h>
#include <vespa/vespalib/testkit/test_kit.h>
+#include <array>
using namespace vespalib;
diff --git a/vespalib/src/tests/trace/trace.cpp b/vespalib/src/tests/trace/trace.cpp
index 92bee3231b0..992317b0289 100644
--- a/vespalib/src/tests/trace/trace.cpp
+++ b/vespalib/src/tests/trace/trace.cpp
@@ -146,25 +146,25 @@ TEST("testTraceLevel")
t.setLevel(4);
EXPECT_EQUAL(4u, t.getLevel());
t.trace(9, "no");
- EXPECT_EQUAL(0u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(0u, t.getNumChildren());
t.trace(8, "no");
- EXPECT_EQUAL(0u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(0u, t.getNumChildren());
t.trace(7, "no");
- EXPECT_EQUAL(0u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(0u, t.getNumChildren());
t.trace(6, "no");
- EXPECT_EQUAL(0u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(0u, t.getNumChildren());
t.trace(5, "no");
- EXPECT_EQUAL(0u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(0u, t.getNumChildren());
t.trace(4, "yes");
- EXPECT_EQUAL(1u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(1u, t.getNumChildren());
t.trace(3, "yes");
- EXPECT_EQUAL(2u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(2u, t.getNumChildren());
t.trace(2, "yes");
- EXPECT_EQUAL(3u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(3u, t.getNumChildren());
t.trace(1, "yes");
- EXPECT_EQUAL(4u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(4u, t.getNumChildren());
t.trace(0, "yes");
- EXPECT_EQUAL(5u, t.getRoot().getNumChildren());
+ EXPECT_EQUAL(5u, t.getNumChildren());
}
TEST("testCompact")
@@ -261,10 +261,10 @@ TEST("testTraceDump")
b1.addChild(b2);
}
for (int i = 0; i < 10; ++i) {
- big.getRoot().addChild(b1);
+ big.addChild(TraceNode(b1));
}
string normal = big.toString();
- string full = big.getRoot().toString();
+ string full = big.toString(100000);
EXPECT_GREATER(normal.size(), 30000u);
EXPECT_LESS(normal.size(), 32000u);
EXPECT_GREATER(full.size(), 50000u);
diff --git a/vespalib/src/vespa/vespalib/data/slime/cursor.h b/vespalib/src/vespa/vespalib/data/slime/cursor.h
index 6815ad3ba83..34d7028a027 100644
--- a/vespalib/src/vespa/vespalib/data/slime/cursor.h
+++ b/vespalib/src/vespa/vespalib/data/slime/cursor.h
@@ -5,8 +5,7 @@
#include "inspector.h"
#include "external_memory.h"
-namespace vespalib {
-namespace slime {
+namespace vespalib::slime {
struct Cursor : public Inspector {
virtual Cursor &operator[](size_t idx) const override = 0;
@@ -46,6 +45,4 @@ struct Cursor : public Inspector {
virtual Symbol resolve(Memory symbol_name) = 0;
};
-} // namespace vespalib::slime
-} // namespace vespalib
-
+}
diff --git a/vespalib/src/vespa/vespalib/trace/trace.cpp b/vespalib/src/vespa/vespalib/trace/trace.cpp
index 8ca2ef6561c..be370aebbd2 100644
--- a/vespalib/src/vespa/vespalib/trace/trace.cpp
+++ b/vespalib/src/vespa/vespalib/trace/trace.cpp
@@ -6,43 +6,13 @@
namespace vespalib {
-Trace::Trace() :
- _level(0),
- _root()
+Trace::Trace(const Trace &rhs)
+ : _root(),
+ _level(rhs._level)
{
- // empty
-}
-
-Trace::Trace(uint32_t level) :
- _level(level),
- _root()
-{
- // empty
-}
-
-Trace::~Trace() = default;
-
-Trace &
-Trace::clear()
-{
- _level = 0;
- _root.clear();
- return *this;
-}
-
-Trace &
-Trace::swap(Trace &other)
-{
- std::swap(_level, other._level);
- _root.swap(other._root);
- return *this;
-}
-
-Trace &
-Trace::setLevel(uint32_t level)
-{
- _level = std::min(level, 9u);
- return *this;
+ if (!rhs.isEmpty()) {
+ _root = std::make_unique<TraceNode>(rhs.getRoot());
+ }
}
bool
@@ -53,12 +23,36 @@ Trace::trace(uint32_t level, const string &note, bool addTime)
}
if (addTime) {
struct timeval tv;
- gettimeofday(&tv, NULL);
- _root.addChild(make_string("[%ld.%06ld] %s", tv.tv_sec, static_cast<long>(tv.tv_usec), note.c_str()));
+ gettimeofday(&tv, nullptr);
+ ensureRoot().addChild(make_string("[%ld.%06ld] %s", tv.tv_sec, static_cast<long>(tv.tv_usec), note.c_str()));
} else {
- _root.addChild(note);
+ ensureRoot().addChild(note);
}
return true;
}
+string
+Trace::toString(size_t limit) const {
+ return _root ? _root->toString(limit) : "";
+}
+
+string
+Trace::encode() const {
+ return isEmpty() ? "" : getRoot().encode();
+}
+
+void
+Trace::clear() {
+ _level = 0;
+ _root.reset();
+}
+
+TraceNode &
+Trace::ensureRoot() {
+ if (!_root) {
+ _root = std::make_unique<TraceNode>();
+ }
+ return *_root;
+}
+
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/trace/trace.h b/vespalib/src/vespa/vespalib/trace/trace.h
index d1ca5c5d7e7..2f70785d77d 100644
--- a/vespalib/src/vespa/vespalib/trace/trace.h
+++ b/vespalib/src/vespa/vespalib/trace/trace.h
@@ -18,31 +18,23 @@ namespace vespalib {
* information will be traced.
*/
class Trace {
-private:
- uint32_t _level;
- TraceNode _root;
-
public:
/**
* Create an empty Trace with level set to 0 (no tracing)
*/
- Trace();
- ~Trace();
-
- /**
- * Create an empty trace with given level.
- *
- * @param level Level to set.
- */
- explicit Trace(uint32_t level);
+ Trace() : Trace(0) {}
+ explicit Trace(uint32_t level) : _root(), _level(level) { }
+ Trace & operator = (Trace &&) = default;
+ Trace(Trace &&) = default;
+ Trace(const Trace &);
+ Trace & operator = (const Trace &) = delete;
+ ~Trace() = default;
/**
* Remove all trace information and set the trace level to 0.
- *
- * @return This, to allow chaining.
*/
- Trace &clear();
+ void clear();
/**
* Swap the internals of this with another.
@@ -50,21 +42,16 @@ public:
* @param other The trace to swap internals with.
* @return This, to allow chaining.
*/
- Trace &swap(Trace &other);
+ Trace &swap(Trace &other) {
+ std::swap(_level, other._level);
+ _root.swap(other._root);
+ return *this;
+ }
- /**
- * Set the trace level. 0 means no tracing, 9 means enable all tracing.
- *
- * @param level The level to set.
- * @return This, to allow chaining.
- */
- Trace &setLevel(uint32_t level);
+ void setLevel(uint32_t level) {
+ _level = std::min(level, 9u);
+ }
- /**
- * Returns the trace level.
- *
- * @return The trace level.
- */
uint32_t getLevel() const { return _level; }
/**
@@ -91,19 +78,29 @@ public:
*/
bool trace(uint32_t level, const string &note, bool addTime = true);
- /**
- * Returns the root of the trace tree.
- *
- * @return The root.
- */
- TraceNode &getRoot() { return _root; }
+ void normalize() {
+ if (_root) {
+ _root->normalize();
+ }
+ }
- /**
- * Returns a const reference to the root of the trace tree.
- *
- * @return The root.
- */
- const TraceNode &getRoot() const { return _root; }
+ void setStrict(bool strict) {
+ ensureRoot().setStrict(strict);
+ }
+ void addChild(TraceNode && child) {
+ ensureRoot().addChild(std::move(child));
+ }
+ void addChild(Trace && child) {
+ if (!child.isEmpty()) {
+ addChild(std::move(*child._root));
+ }
+ }
+
+ bool isEmpty() const { return !_root || _root->isEmpty(); }
+
+ uint32_t getNumChildren() const { return _root ? _root->getNumChildren() : 0; }
+ const TraceNode & getChild(uint32_t child) const { return getRoot().getChild(child); }
+ string encode() const;
/**
* Returns a string representation of the contained trace tree. This is a
@@ -111,7 +108,16 @@ public:
*
* @return Readable trace string.
*/
- string toString() const { return _root.toString(31337); }
+ string toString(size_t limit=31337) const;
+ size_t computeMemoryUsage() const {
+ return _root ? _root->computeMemoryUsage() : 0;
+ }
+private:
+ const TraceNode &getRoot() const { return *_root; }
+ TraceNode &ensureRoot();
+
+ std::unique_ptr<TraceNode> _root;
+ uint32_t _level;
};
#define VESPALIB_TRACE2(ttrace, level, note, addTime) \
diff --git a/vespalib/src/vespa/vespalib/trace/tracenode.cpp b/vespalib/src/vespa/vespalib/trace/tracenode.cpp
index d34b45025d3..12dd51ac677 100644
--- a/vespalib/src/vespa/vespalib/trace/tracenode.cpp
+++ b/vespalib/src/vespa/vespalib/trace/tracenode.cpp
@@ -40,22 +40,22 @@ struct Cmp {
} // namespace <unnamed>
-TraceNode::TraceNode() :
- _parent(nullptr),
- _strict(true),
- _hasNote(false),
- _note(""),
- _children(),
- _timestamp()
+TraceNode::TraceNode()
+ : _note(""),
+ _children(),
+ _parent(nullptr),
+ _timestamp(),
+ _strict(true),
+ _hasNote(false)
{ }
-TraceNode::TraceNode(const TraceNode &rhs) :
- _parent(nullptr),
- _strict(rhs._strict),
- _hasNote(rhs._hasNote),
- _note(rhs._note),
- _children(),
- _timestamp(rhs._timestamp)
+TraceNode::TraceNode(const TraceNode &rhs)
+ : _note(rhs._note),
+ _children(),
+ _parent(nullptr),
+ _timestamp(rhs._timestamp),
+ _strict(rhs._strict),
+ _hasNote(rhs._hasNote)
{
addChildren(rhs._children);
}
@@ -65,22 +65,22 @@ TraceNode & TraceNode::operator =(const TraceNode &) = default;
TraceNode::~TraceNode() = default;
-TraceNode::TraceNode(const string &note, system_time timestamp) :
- _parent(nullptr),
- _strict(true),
- _hasNote(true),
- _note(note),
- _children(),
- _timestamp(timestamp)
+TraceNode::TraceNode(const string &note, system_time timestamp)
+ : _note(note),
+ _children(),
+ _parent(nullptr),
+ _timestamp(timestamp),
+ _strict(true),
+ _hasNote(true)
{ }
-TraceNode::TraceNode(system_time timestamp) :
- _parent(nullptr),
- _strict(true),
- _hasNote(false),
- _note(""),
- _children(),
- _timestamp(timestamp)
+TraceNode::TraceNode(system_time timestamp)
+ : _note(""),
+ _children(),
+ _parent(nullptr),
+ _timestamp(timestamp),
+ _strict(true),
+ _hasNote(false)
{ }
TraceNode &
@@ -342,4 +342,17 @@ TraceNode::accept(TraceVisitor & visitor) const
return visitor;
}
+size_t
+TraceNode::computeMemoryUsage() const
+{
+ if (isLeaf()) {
+ return getNote().size();
+ }
+ size_t childSum = 0;
+ for (const TraceNode & child : _children) {
+ childSum += child.computeMemoryUsage();
+ }
+ return childSum;
+}
+
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/trace/tracenode.h b/vespalib/src/vespa/vespalib/trace/tracenode.h
index 63e7bcd6dc0..7a7cdb89c69 100644
--- a/vespalib/src/vespa/vespalib/trace/tracenode.h
+++ b/vespalib/src/vespa/vespalib/trace/tracenode.h
@@ -23,12 +23,12 @@ struct TraceVisitor;
*/
class TraceNode {
private:
- TraceNode *_parent;
- bool _strict;
- bool _hasNote;
string _note;
std::vector<TraceNode> _children;
+ TraceNode *_parent;
system_time _timestamp;
+ bool _strict;
+ bool _hasNote;
public:
/**
@@ -253,6 +253,8 @@ public:
*/
TraceVisitor & accept(TraceVisitor & visitor) const;
+ size_t computeMemoryUsage() const;
+
};
} // namespace vespalib
diff --git a/zkfacade/abi-spec.json b/zkfacade/abi-spec.json
index e026559b283..e0de2622149 100644
--- a/zkfacade/abi-spec.json
+++ b/zkfacade/abi-spec.json
@@ -60,6 +60,7 @@
"com.yahoo.vespa.curator.Curator": {
"superClass": "java.lang.Object",
"interfaces": [
+ "com.yahoo.vespa.curator.api.VespaCurator",
"java.lang.AutoCloseable"
],
"attributes": [
@@ -68,6 +69,7 @@
"methods": [
"public static com.yahoo.vespa.curator.Curator create(java.lang.String)",
"public static com.yahoo.vespa.curator.Curator create(java.lang.String, java.util.Optional)",
+ "public void <init>(com.yahoo.cloud.config.CuratorConfig, com.yahoo.vespa.zookeeper.VespaZooKeeperServer)",
"public void <init>(com.yahoo.cloud.config.ConfigserverConfig, com.yahoo.vespa.zookeeper.VespaZooKeeperServer)",
"protected void <init>(java.lang.String, java.lang.String, java.util.function.Function)",
"public java.lang.String connectionSpec()",
@@ -89,7 +91,8 @@
"public org.apache.curator.framework.CuratorFramework framework()",
"public void close()",
"public java.lang.String zooKeeperEnsembleConnectionSpec()",
- "public int zooKeeperEnsembleCount()"
+ "public int zooKeeperEnsembleCount()",
+ "public bridge synthetic java.lang.AutoCloseable lock(com.yahoo.path.Path, java.time.Duration)"
],
"fields": [
"protected final org.apache.curator.RetryPolicy retryPolicy"
@@ -110,5 +113,18 @@
"public void close()"
],
"fields": []
+ },
+ "com.yahoo.vespa.curator.api.VespaCurator": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public",
+ "interface",
+ "abstract"
+ ],
+ "methods": [
+ "public abstract java.lang.AutoCloseable lock(com.yahoo.path.Path, java.time.Duration)"
+ ],
+ "fields": []
}
} \ No newline at end of file
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/ConnectionSpec.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/ConnectionSpec.java
new file mode 100644
index 00000000000..4409291419a
--- /dev/null
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/ConnectionSpec.java
@@ -0,0 +1,102 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.curator;
+
+import com.yahoo.net.HostName;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * A connection spec for Curator.
+ *
+ * @author mpolden
+ */
+class ConnectionSpec {
+
+ private final String local;
+ private final String ensemble;
+ private final int ensembleSize;
+
+ private ConnectionSpec(String local, String ensemble, int ensembleSize) {
+ this.local = requireNonEmpty(local, "local spec");
+ this.ensemble = requireNonEmpty(ensemble, "ensemble spec");
+ this.ensembleSize = ensembleSize;
+ }
+
+ /** Returns the local spec. This may be a subset of the ensemble spec */
+ public String local() {
+ return local;
+ }
+
+ /** Returns the ensemble spec. This always contains all nodes in the ensemble */
+ public String ensemble() {
+ return ensemble;
+ }
+
+ /** Returns the number of servers in the ensemble */
+ public int ensembleSize() {
+ return ensembleSize;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ConnectionSpec that = (ConnectionSpec) o;
+ return ensembleSize == that.ensembleSize &&
+ local.equals(that.local) &&
+ ensemble.equals(that.ensemble);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(local, ensemble, ensembleSize);
+ }
+
+ public static ConnectionSpec create(String spec) {
+ return create(spec, spec);
+ }
+
+ public static ConnectionSpec create(String localSpec, String ensembleSpec) {
+ return new ConnectionSpec(localSpec, ensembleSpec, ensembleSpec.split(",").length);
+ }
+
+ public static <T> ConnectionSpec create(List<T> servers,
+ Function<T, String> hostnameGetter,
+ Function<T, Integer> portGetter,
+ boolean localhostAffinity) {
+ String localSpec = createSpec(servers, hostnameGetter, portGetter, localhostAffinity);
+ String ensembleSpec = localhostAffinity ? createSpec(servers, hostnameGetter, portGetter, false) : localSpec;
+ return new ConnectionSpec(localSpec, ensembleSpec, servers.size());
+ }
+
+ private static <T> String createSpec(List<T> servers,
+ Function<T, String> hostnameGetter,
+ Function<T, Integer> portGetter,
+ boolean localhostAffinity) {
+ String thisServer = HostName.getLocalhost();
+ StringBuilder connectionSpec = new StringBuilder();
+ for (var server : servers) {
+ if (localhostAffinity && !thisServer.equals(hostnameGetter.apply(server))) continue;
+ connectionSpec.append(hostnameGetter.apply(server));
+ connectionSpec.append(':');
+ connectionSpec.append(portGetter.apply(server));
+ connectionSpec.append(',');
+ }
+ if (localhostAffinity && connectionSpec.length() == 0) {
+ throw new IllegalArgumentException("Unable to create connect string to localhost: " +
+ "There is no localhost server specified in config");
+ }
+ if (connectionSpec.length() > 0) {
+ connectionSpec.setLength(connectionSpec.length() - 1); // Remove trailing comma
+ }
+ return connectionSpec.toString();
+ }
+
+ private static String requireNonEmpty(String s, String field) {
+ if (Objects.requireNonNull(s).isEmpty()) throw new IllegalArgumentException(field + " must be non-empty");
+ return s;
+ }
+
+}
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java
index 04bd64219d4..90eec5760fc 100644
--- a/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/Curator.java
@@ -3,10 +3,11 @@ package com.yahoo.vespa.curator;
import com.google.inject.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.cloud.config.CuratorConfig;
import com.yahoo.io.IOUtils;
-import com.yahoo.net.HostName;
import com.yahoo.path.Path;
import com.yahoo.text.Utf8;
+import com.yahoo.vespa.curator.api.VespaCurator;
import com.yahoo.vespa.curator.recipes.CuratorCounter;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.zookeeper.VespaZooKeeperServer;
@@ -31,6 +32,7 @@ import java.io.File;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
@@ -47,7 +49,7 @@ import java.util.logging.Logger;
* @author vegardh
* @author bratseth
*/
-public class Curator implements AutoCloseable {
+public class Curator implements VespaCurator, AutoCloseable {
private static final Logger LOG = Logger.getLogger(Curator.class.getName());
private static final File ZK_CLIENT_CONFIG_FILE = new File(Defaults.getDefaults().underVespaHome("conf/zookeeper/zookeeper-client.cfg"));
@@ -55,81 +57,67 @@ public class Curator implements AutoCloseable {
private static final Duration ZK_CONNECTION_TIMEOUT = Duration.ofSeconds(30);
private static final Duration BASE_SLEEP_TIME = Duration.ofSeconds(1);
private static final int MAX_RETRIES = 10;
+ private static final RetryPolicy DEFAULT_RETRY_POLICY = new ExponentialBackoffRetry((int) BASE_SLEEP_TIME.toMillis(), MAX_RETRIES);
- protected final RetryPolicy retryPolicy;
+ protected final RetryPolicy retryPolicy = DEFAULT_RETRY_POLICY;
private final CuratorFramework curatorFramework;
- private final String connectionSpec; // May be a subset of the servers in the ensemble
- private final String zooKeeperEnsembleConnectionSpec;
- private final int zooKeeperEnsembleCount;
+ private final ConnectionSpec connectionSpec;
// All lock keys, to allow re-entrancy. This will grow forever, but this should be too slow to be a problem
private final ConcurrentHashMap<Path, Lock> locks = new ConcurrentHashMap<>();
/** Creates a curator instance from a comma-separated string of ZooKeeper host:port strings */
public static Curator create(String connectionSpec) {
- return new Curator(connectionSpec, connectionSpec, Optional.of(ZK_CLIENT_CONFIG_FILE));
+ return new Curator(ConnectionSpec.create(connectionSpec), Optional.of(ZK_CLIENT_CONFIG_FILE));
}
// For testing only, use Optional.empty for clientConfigFile parameter to create default zookeeper client config
public static Curator create(String connectionSpec, Optional<File> clientConfigFile) {
- return new Curator(connectionSpec, connectionSpec, clientConfigFile);
+ return new Curator(ConnectionSpec.create(connectionSpec), clientConfigFile);
}
- // Depend on ZooKeeperServer to make sure it is started first
- // TODO: Move zookeeperserver config out of configserverconfig (requires update of controller services.xml as well)
@Inject
- public Curator(ConfigserverConfig configserverConfig, VespaZooKeeperServer server) {
- this(configserverConfig, Optional.of(ZK_CLIENT_CONFIG_FILE));
+ public Curator(CuratorConfig curatorConfig, @SuppressWarnings("unused") VespaZooKeeperServer server) {
+ // Depends on ZooKeeperServer to make sure it is started first
+ this(ConnectionSpec.create(curatorConfig.server(),
+ CuratorConfig.Server::hostname,
+ CuratorConfig.Server::port,
+ curatorConfig.zookeeperLocalhostAffinity()),
+ Optional.of(ZK_CLIENT_CONFIG_FILE));
}
- Curator(ConfigserverConfig configserverConfig, Optional<File> clientConfigFile) {
- this(createConnectionSpec(configserverConfig), createEnsembleConnectionSpec(configserverConfig), clientConfigFile);
+ // TODO: This can be removed when this package is no longer public API.
+ public Curator(ConfigserverConfig configserverConfig, @SuppressWarnings("unused") VespaZooKeeperServer server) {
+ this(ConnectionSpec.create(configserverConfig.zookeeperserver(),
+ ConfigserverConfig.Zookeeperserver::hostname,
+ ConfigserverConfig.Zookeeperserver::port,
+ configserverConfig.zookeeperLocalhostAffinity()),
+ Optional.of(ZK_CLIENT_CONFIG_FILE));
}
- private Curator(String connectionSpec, String zooKeeperEnsembleConnectionSpec, Optional<File> clientConfigFile) {
- this(connectionSpec,
- zooKeeperEnsembleConnectionSpec,
- (retryPolicy) -> CuratorFrameworkFactory
- .builder()
- .retryPolicy(retryPolicy)
- .sessionTimeoutMs((int) ZK_SESSION_TIMEOUT.toMillis())
- .connectionTimeoutMs((int) ZK_CONNECTION_TIMEOUT.toMillis())
- .connectString(connectionSpec)
- .zookeeperFactory(new VespaZooKeeperFactory(createClientConfig(clientConfigFile)))
- .dontUseContainerParents() // TODO: Remove when we know ZooKeeper 3.5 works fine, consider waiting until Vespa 8
- .build());
- }
-
- protected Curator(String connectionSpec,
- String zooKeeperEnsembleConnectionSpec,
- Function<RetryPolicy, CuratorFramework> curatorFactory) {
- this(connectionSpec, zooKeeperEnsembleConnectionSpec, curatorFactory,
- new ExponentialBackoffRetry((int) BASE_SLEEP_TIME.toMillis(), MAX_RETRIES));
+ protected Curator(String connectionSpec, String zooKeeperEnsembleConnectionSpec, Function<RetryPolicy, CuratorFramework> curatorFactory) {
+ this(ConnectionSpec.create(connectionSpec, zooKeeperEnsembleConnectionSpec), curatorFactory.apply(DEFAULT_RETRY_POLICY));
}
- private Curator(String connectionSpec,
- String zooKeeperEnsembleConnectionSpec,
- Function<RetryPolicy, CuratorFramework> curatorFactory,
- RetryPolicy retryPolicy) {
- this.connectionSpec = connectionSpec;
- this.retryPolicy = retryPolicy;
- this.curatorFramework = curatorFactory.apply(retryPolicy);
- if (this.curatorFramework != null) {
- validateConnectionSpec(connectionSpec);
- validateConnectionSpec(zooKeeperEnsembleConnectionSpec);
- addLoggingListener();
- curatorFramework.start();
- }
-
- this.zooKeeperEnsembleConnectionSpec = zooKeeperEnsembleConnectionSpec;
- this.zooKeeperEnsembleCount = zooKeeperEnsembleConnectionSpec.split(",").length;
+ Curator(ConnectionSpec connectionSpec, Optional<File> clientConfigFile) {
+ this(connectionSpec,
+ CuratorFrameworkFactory
+ .builder()
+ .retryPolicy(DEFAULT_RETRY_POLICY)
+ .sessionTimeoutMs((int) ZK_SESSION_TIMEOUT.toMillis())
+ .connectionTimeoutMs((int) ZK_CONNECTION_TIMEOUT.toMillis())
+ .connectString(connectionSpec.local())
+ .zookeeperFactory(new VespaZooKeeperFactory(createClientConfig(clientConfigFile)))
+ .dontUseContainerParents() // TODO: Remove when we know ZooKeeper 3.5 works fine, consider waiting until Vespa 8
+ .build());
}
- private static String createConnectionSpec(ConfigserverConfig configserverConfig) {
- return configserverConfig.zookeeperLocalhostAffinity()
- ? createConnectionSpecForLocalhost(configserverConfig)
- : createEnsembleConnectionSpec(configserverConfig);
+ private Curator(ConnectionSpec connectionSpec, CuratorFramework curatorFramework) {
+ this.connectionSpec = Objects.requireNonNull(connectionSpec);
+ this.curatorFramework = Objects.requireNonNull(curatorFramework);
+ addLoggingListener();
+ curatorFramework.start();
}
private static ZKClientConfig createClientConfig(Optional<File> clientConfigFile) {
@@ -148,39 +136,6 @@ public class Curator implements AutoCloseable {
}
}
- private static String createEnsembleConnectionSpec(ConfigserverConfig config) {
- StringBuilder connectionSpec = new StringBuilder();
- for (int i = 0; i < config.zookeeperserver().size(); i++) {
- if (connectionSpec.length() > 0) {
- connectionSpec.append(',');
- }
- ConfigserverConfig.Zookeeperserver server = config.zookeeperserver(i);
- connectionSpec.append(server.hostname());
- connectionSpec.append(':');
- connectionSpec.append(server.port());
- }
- return connectionSpec.toString();
- }
-
- static String createConnectionSpecForLocalhost(ConfigserverConfig config) {
- String thisServer = HostName.getLocalhost();
-
- for (int i = 0; i < config.zookeeperserver().size(); i++) {
- ConfigserverConfig.Zookeeperserver server = config.zookeeperserver(i);
- if (thisServer.equals(server.hostname())) {
- return String.format("%s:%d", server.hostname(), server.port());
- }
- }
-
- throw new IllegalArgumentException("Unable to create connect string to localhost: " +
- "There is no localhost server specified in config: " + config);
- }
-
- private static void validateConnectionSpec(String connectionSpec) {
- if (connectionSpec == null || connectionSpec.isEmpty())
- throw new IllegalArgumentException(String.format("Connections spec '%s' is not valid", connectionSpec));
- }
-
/**
* Returns the ZooKeeper "connect string" used by curator: a comma-separated list of
* host:port of ZooKeeper endpoints to connect to. This may be a subset of
@@ -189,7 +144,7 @@ public class Curator implements AutoCloseable {
*
* This may be empty but never null
*/
- public String connectionSpec() { return connectionSpec; }
+ public String connectionSpec() { return connectionSpec.local(); }
/** For internal use; prefer creating a {@link CuratorCounter} */
public DistributedAtomicLong createAtomicCounter(String path) {
@@ -243,13 +198,14 @@ public class Curator implements AutoCloseable {
* A convenience method which sets some content at a path.
* If the path and any of its parents does not exists they are created.
*/
+ // TODO: Use create().orSetData() in Curator 4 and later
public void set(Path path, byte[] data) {
+ if ( ! exists(path))
+ create(path);
+
String absolutePath = path.getAbsolute();
try {
- if ( ! exists(path))
- framework().create().creatingParentsIfNeeded().forPath(absolutePath, data);
- else
- framework().setData().forPath(absolutePath, data);
+ framework().setData().forPath(absolutePath, data);
} catch (Exception e) {
throw new RuntimeException("Could not set data at " + absolutePath, e);
}
@@ -432,7 +388,7 @@ public class Curator implements AutoCloseable {
* TODO: Move method out of this class.
*/
public String zooKeeperEnsembleConnectionSpec() {
- return zooKeeperEnsembleConnectionSpec;
+ return connectionSpec.ensemble();
}
/**
@@ -440,7 +396,7 @@ public class Curator implements AutoCloseable {
* WARNING: This may be different from the number of servers this Curator may connect to.
* TODO: Move method out of this class.
*/
- public int zooKeeperEnsembleCount() { return zooKeeperEnsembleCount; }
+ public int zooKeeperEnsembleCount() { return connectionSpec.ensembleSize(); }
private static Optional<String> getEnvironmentVariable(String variableName) {
return Optional.ofNullable(System.getenv().get(variableName))
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/CuratorCompletionWaiter.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/CuratorCompletionWaiter.java
index fb8c303db66..0f8c524fa98 100644
--- a/zkfacade/src/main/java/com/yahoo/vespa/curator/CuratorCompletionWaiter.java
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/CuratorCompletionWaiter.java
@@ -57,6 +57,8 @@ class CuratorCompletionWaiter implements Curator.CompletionWaiter {
List<String> respondents;
do {
respondents = curator.framework().getChildren().forPath(barrierPath);
+ log.log(Level.FINE, respondents.size() + "/" + curator.zooKeeperEnsembleCount() + " responded: " +
+ respondents + ", all participants: " + curator.zooKeeperEnsembleConnectionSpec());
// First, check if all config servers responded
if (respondents.size() == curator.zooKeeperEnsembleCount()) {
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java
new file mode 100644
index 00000000000..0b6fa55719e
--- /dev/null
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java
@@ -0,0 +1,19 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.curator.api;
+
+import com.yahoo.path.Path;
+
+import java.time.Duration;
+
+/**
+ * A client for a ZooKeeper cluster running inside Vespa. Applications that want to use ZooKeeper can inject this in
+ * their code.
+ *
+ * @author mpolden
+ */
+public interface VespaCurator {
+
+ /** Create and acquire a re-entrant lock in given path. This blocks until the lock is acquired or timeout elapses. */
+ AutoCloseable lock(Path path, Duration timeout);
+
+}
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/api/package-info.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/api/package-info.java
new file mode 100644
index 00000000000..679dd3750cb
--- /dev/null
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/api/package-info.java
@@ -0,0 +1,10 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author mpolden
+ */
+@PublicApi
+@ExportPackage
+package com.yahoo.vespa.curator.api;
+
+import com.yahoo.api.annotations.PublicApi;
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java
index 1f583ada7a1..26f1c336874 100644
--- a/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java
@@ -1,110 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.curator.mock;
-import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.google.inject.Inject;
-import com.yahoo.collections.Pair;
-import com.yahoo.concurrent.Lock;
-import com.yahoo.concurrent.Locks;
import com.yahoo.path.Path;
-import com.yahoo.vespa.curator.CompletionTimeoutException;
import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.curator.recipes.CuratorLockException;
-import org.apache.curator.CuratorZookeeperClient;
-import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.framework.WatcherRemoveCuratorFramework;
-import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable;
-import org.apache.curator.framework.api.ACLCreateModeBackgroundPathAndBytesable;
-import org.apache.curator.framework.api.ACLCreateModePathAndBytesable;
-import org.apache.curator.framework.api.ACLCreateModeStatBackgroundPathAndBytesable;
-import org.apache.curator.framework.api.ACLPathAndBytesable;
-import org.apache.curator.framework.api.ACLableExistBuilderMain;
-import org.apache.curator.framework.api.BackgroundCallback;
-import org.apache.curator.framework.api.BackgroundPathAndBytesable;
-import org.apache.curator.framework.api.BackgroundPathable;
-import org.apache.curator.framework.api.BackgroundVersionable;
-import org.apache.curator.framework.api.ChildrenDeletable;
-import org.apache.curator.framework.api.CreateBackgroundModeStatACLable;
-import org.apache.curator.framework.api.CreateBuilder;
-import org.apache.curator.framework.api.CreateBuilder2;
-import org.apache.curator.framework.api.CreateBuilderMain;
-import org.apache.curator.framework.api.CreateProtectACLCreateModePathAndBytesable;
-import org.apache.curator.framework.api.CuratorListener;
-import org.apache.curator.framework.api.CuratorWatcher;
-import org.apache.curator.framework.api.DeleteBuilder;
-import org.apache.curator.framework.api.DeleteBuilderMain;
-import org.apache.curator.framework.api.ErrorListenerPathAndBytesable;
-import org.apache.curator.framework.api.ErrorListenerPathable;
-import org.apache.curator.framework.api.ExistsBuilder;
-import org.apache.curator.framework.api.GetACLBuilder;
-import org.apache.curator.framework.api.GetChildrenBuilder;
-import org.apache.curator.framework.api.GetConfigBuilder;
-import org.apache.curator.framework.api.GetDataBuilder;
-import org.apache.curator.framework.api.GetDataWatchBackgroundStatable;
-import org.apache.curator.framework.api.PathAndBytesable;
-import org.apache.curator.framework.api.Pathable;
-import org.apache.curator.framework.api.ProtectACLCreateModeStatPathAndBytesable;
-import org.apache.curator.framework.api.ReconfigBuilder;
-import org.apache.curator.framework.api.RemoveWatchesBuilder;
-import org.apache.curator.framework.api.SetACLBuilder;
-import org.apache.curator.framework.api.SetDataBackgroundVersionable;
-import org.apache.curator.framework.api.SetDataBuilder;
-import org.apache.curator.framework.api.SyncBuilder;
-import org.apache.curator.framework.api.UnhandledErrorListener;
-import org.apache.curator.framework.api.VersionPathAndBytesable;
-import org.apache.curator.framework.api.WatchPathable;
-import org.apache.curator.framework.api.Watchable;
-import org.apache.curator.framework.api.transaction.CuratorMultiTransaction;
-import org.apache.curator.framework.api.transaction.CuratorTransaction;
-import org.apache.curator.framework.api.transaction.CuratorTransactionBridge;
-import org.apache.curator.framework.api.transaction.CuratorTransactionFinal;
-import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
-import org.apache.curator.framework.api.transaction.TransactionCheckBuilder;
-import org.apache.curator.framework.api.transaction.TransactionCreateBuilder;
-import org.apache.curator.framework.api.transaction.TransactionCreateBuilder2;
-import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder;
-import org.apache.curator.framework.api.transaction.TransactionOp;
-import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder;
-import org.apache.curator.framework.imps.CuratorFrameworkState;
-import org.apache.curator.framework.listen.Listenable;
-import org.apache.curator.framework.recipes.atomic.AtomicStats;
-import org.apache.curator.framework.recipes.atomic.AtomicValue;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
-import org.apache.curator.framework.recipes.cache.ChildData;
-import org.apache.curator.framework.recipes.cache.NodeCacheListener;
-import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
-import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
-import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
-import org.apache.curator.framework.schema.SchemaSet;
-import org.apache.curator.framework.state.ConnectionStateErrorPolicy;
-import org.apache.curator.framework.state.ConnectionStateListener;
-import org.apache.curator.utils.EnsurePath;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.Watcher;
-import org.apache.zookeeper.data.ACL;
-import org.apache.zookeeper.data.Stat;
-import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
-import java.nio.file.Paths;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-import static com.yahoo.vespa.curator.mock.MemoryFileSystem.Node;
/**
* <p>A <b>non thread safe</b> mock of the curator API.
@@ -121,24 +25,7 @@ import static com.yahoo.vespa.curator.mock.MemoryFileSystem.Node;
*/
public class MockCurator extends Curator {
- public boolean timeoutOnLock = false;
- public boolean throwExceptionOnLock = false;
- private boolean shouldTimeoutOnEnter = false;
- private int monotonicallyIncreasingNumber = 0;
- private final boolean stableOrdering;
private String zooKeeperEnsembleConnectionSpec = "";
- private final Locks<String> locks = new Locks<>(Long.MAX_VALUE, TimeUnit.DAYS);
-
- /** The file system used by this mock to store zookeeper files and directories */
- private final MemoryFileSystem fileSystem = new MemoryFileSystem();
-
- /** Atomic counters. A more accurate mock would store these as files in the file system */
- private final Map<String, MockAtomicCounter> atomicCounters = new ConcurrentHashMap<>();
-
- /** Listeners to changes to a particular path */
- private final ListenerMap listeners = new ListenerMap();
-
- private final CuratorFramework curatorFramework;
/** Creates a mock curator with stable ordering */
@Inject
@@ -153,26 +40,24 @@ public class MockCurator extends Curator {
* This is not what ZooKeeper does.
*/
public MockCurator(boolean stableOrdering) {
- super("", "", (retryPolicy) -> null);
- this.stableOrdering = stableOrdering;
- curatorFramework = new MockCuratorFramework();
- curatorFramework.start();
+ super("host1:2181", "host1:2181", (retryPolicy) -> new MockCuratorFramework(stableOrdering, false));
+ }
+
+ private MockCuratorFramework mockFramework() {
+ return (MockCuratorFramework) super.framework();
}
/**
* Lists the entire content of this curator instance as a multiline string.
* Useful for debugging.
*/
- public String dumpState() { return fileSystem.dumpState(); }
-
- /** Returns a started curator framework */
- public CuratorFramework framework() { return curatorFramework; }
+ public String dumpState() { return mockFramework().fileSystem().dumpState(); }
/** Returns an atomic counter in this, or empty if no such counter is created */
public Optional<DistributedAtomicLong> counter(String path) {
- return Optional.ofNullable(atomicCounters.get(path));
+ return Optional.ofNullable(mockFramework().atomicCounters().get(path));
}
-
+
/**
* Sets the ZooKeeper ensemble connection spec, which must be on the form
* host1:port,host2:port ...
@@ -186,1455 +71,37 @@ public class MockCurator extends Curator {
return zooKeeperEnsembleConnectionSpec;
}
- // ----- Start of adaptor methods from Curator to the mock file system -----
-
- /** Creates a node below the given directory root */
- private String createNode(String pathString, byte[] content, boolean createParents, CreateMode createMode, Node root, Listeners listeners)
- throws KeeperException.NodeExistsException, KeeperException.NoNodeException {
- validatePath(pathString);
- Path path = Path.fromString(pathString);
- if (path.isRoot()) return "/"; // the root already exists
- Node parent = root.getNode(Paths.get(path.getParentPath().toString()), createParents);
- String name = nodeName(path.getName(), createMode);
-
- if (parent == null)
- throw new KeeperException.NoNodeException(path.getParentPath().toString());
- if (parent.children().containsKey(path.getName()))
- throw new KeeperException.NodeExistsException(path.toString());
-
- parent.add(name).setContent(content);
- String nodePath = "/" + path.getParentPath().toString() + "/" + name;
- listeners.notify(Path.fromString(nodePath), content, PathChildrenCacheEvent.Type.CHILD_ADDED);
- return nodePath;
- }
-
- /** Deletes a node below the given directory root */
- private void deleteNode(String pathString, boolean deleteChildren, Node root, Listeners listeners)
- throws KeeperException.NoNodeException, KeeperException.NotEmptyException {
- validatePath(pathString);
- Path path = Path.fromString(pathString);
- Node parent = root.getNode(Paths.get(path.getParentPath().toString()), false);
- if (parent == null) throw new KeeperException.NoNodeException(path.toString());
- Node node = parent.children().get(path.getName());
- if (node == null) throw new KeeperException.NoNodeException(path.getName() + " under " + parent);
- if ( ! node.children().isEmpty() && ! deleteChildren)
- throw new KeeperException.NotEmptyException(path.toString());
- parent.remove(path.getName());
- listeners.notify(path, new byte[0], PathChildrenCacheEvent.Type.CHILD_REMOVED);
- }
-
- /** Returns the data of a node */
- private byte[] getData(String pathString, Node root) throws KeeperException.NoNodeException {
- validatePath(pathString);
- return getNode(pathString, root).getContent();
- }
-
- /** sets the data of an existing node */
- private void setData(String pathString, byte[] content, Node root, Listeners listeners)
- throws KeeperException.NoNodeException {
- validatePath(pathString);
- getNode(pathString, root).setContent(content);
- listeners.notify(Path.fromString(pathString), content, PathChildrenCacheEvent.Type.CHILD_UPDATED);
- }
-
- private List<String> getChildren(String path, Node root) throws KeeperException.NoNodeException {
- validatePath(path);
- Node node = root.getNode(Paths.get(path), false);
- if (node == null) throw new KeeperException.NoNodeException(path);
- List<String> children = new ArrayList<>(node.children().keySet());
- if (! stableOrdering)
- Collections.shuffle(children);
- return children;
- }
-
- private boolean exists(String path, Node root) {
- validatePath(path);
- Node parent = root.getNode(Paths.get(Path.fromString(path).getParentPath().toString()), false);
- if (parent == null) return false;
- Node node = parent.children().get(Path.fromString(path).getName());
- return node != null;
- }
-
- /** Returns a node or throws the appropriate exception if it doesn't exist */
- private Node getNode(String pathString, Node root) throws KeeperException.NoNodeException {
- validatePath(pathString);
- Path path = Path.fromString(pathString);
- Node parent = root.getNode(Paths.get(path.getParentPath().toString()), false);
- if (parent == null) throw new KeeperException.NoNodeException(path.toString());
- Node node = parent.children().get(path.getName());
- if (node == null) throw new KeeperException.NoNodeException(path.toString());
- return node;
- }
-
- private String nodeName(String baseName, CreateMode createMode) {
- switch (createMode) {
- case PERSISTENT: case EPHEMERAL: return baseName;
- case PERSISTENT_SEQUENTIAL: case EPHEMERAL_SEQUENTIAL: return baseName + monotonicallyIncreasingNumber++;
- default: throw new UnsupportedOperationException(createMode + " support not implemented in MockCurator");
- }
- }
-
- /** Validates a path using the same rules as ZooKeeper */
- public static String validatePath(String path) throws IllegalArgumentException {
- if (path == null) throw new IllegalArgumentException("Path cannot be null");
- if (path.length() == 0) throw new IllegalArgumentException("Path length must be > 0");
- if (path.charAt(0) != '/') throw new IllegalArgumentException("Path must start with / character");
- if (path.length() == 1) return path; // done checking - it's the root
- if (path.charAt(path.length() - 1) == '/')
- throw new IllegalArgumentException("Path must not end with / character");
-
- String reason = null;
- char lastc = '/';
- char chars[] = path.toCharArray();
- char c;
- for (int i = 1; i < chars.length; lastc = chars[i], i++) {
- c = chars[i];
-
- if (c == 0) {
- reason = "null character not allowed @" + i;
- break;
- } else if (c == '/' && lastc == '/') {
- reason = "empty node name specified @" + i;
- break;
- } else if (c == '.' && lastc == '.') {
- if (chars[i-2] == '/' && ((i + 1 == chars.length) || chars[i+1] == '/')) {
- reason = "relative paths not allowed @" + i;
- break;
- }
- } else if (c == '.') {
- if (chars[i-1] == '/' && ((i + 1 == chars.length) || chars[i+1] == '/')) {
- reason = "relative paths not allowed @" + i;
- break;
- }
- } else if (c > '\u0000' && c < '\u001f' || c > '\u007f' && c < '\u009F'
- || c > '\ud800' && c < '\uf8ff' || c > '\ufff0' && c < '\uffff') {
- reason = "invalid charater @" + i;
- break;
- }
- }
-
- if (reason != null)
- throw new IllegalArgumentException("Invalid path string \"" + path + "\" caused by " + reason);
- return path;
- }
-
- // ----- Mock of Curator recipes accessed through our Curator interface -----
-
@Override
public DistributedAtomicLong createAtomicCounter(String path) {
- MockAtomicCounter counter = atomicCounters.get(path);
- if (counter == null) {
- counter = new MockAtomicCounter(path);
- atomicCounters.put(path, counter);
- }
- return counter;
+ return mockFramework().createAtomicCounter(path);
}
- /** Create a mutex which ensures exclusive access within this single vm */
@Override
public InterProcessLock createMutex(String path) {
- return new MockLock(path);
- }
-
- public MockCurator timeoutBarrierOnEnter(boolean shouldTimeout) {
- shouldTimeoutOnEnter = shouldTimeout;
- return this;
+ return mockFramework().createMutex(path);
}
@Override
public CompletionWaiter getCompletionWaiter(Path parentPath, int numMembers, String id) {
- return new MockCompletionWaiter();
+ return mockFramework().createCompletionWaiter();
}
@Override
public CompletionWaiter createCompletionWaiter(Path parentPath, String waiterNode, int numMembers, String id) {
- return new MockCompletionWaiter();
+ return mockFramework().createCompletionWaiter();
}
@Override
public DirectoryCache createDirectoryCache(String path, boolean cacheData, boolean dataIsCompressed, ExecutorService executorService) {
- return new MockDirectoryCache(Path.fromString(path));
+ return mockFramework().createDirectoryCache(path);
}
@Override
public FileCache createFileCache(String path, boolean dataIsCompressed) {
- return new MockFileCache(Path.fromString(path));
+ return mockFramework().createFileCache(path);
}
@Override
public int zooKeeperEnsembleCount() { return 1; }
- /**
- * Invocation of changes to the file system state is abstracted through this to allow transactional
- * changes to notify on commit
- */
- private abstract class Listeners {
-
- /** Translating method */
- public final void notify(Path path, byte[] data, PathChildrenCacheEvent.Type type) {
- String pathString = "/" + path.toString(); // this silly path class strips the leading "/" :-/
- PathChildrenCacheEvent event = new PathChildrenCacheEvent(type, new ChildData(pathString, null, data));
- notify(path, event);
- }
-
- public abstract void notify(Path path, PathChildrenCacheEvent event);
-
- }
-
- /** The regular listener implementation which notifies registered file and directory listeners */
- private class ListenerMap extends Listeners {
-
- private final Map<Path, PathChildrenCacheListener> directoryListeners = new ConcurrentHashMap<>();
- private final Map<Path, NodeCacheListener> fileListeners = new ConcurrentHashMap<>();
-
- public void add(Path path, PathChildrenCacheListener listener) {
- directoryListeners.put(path, listener);
- }
-
- public void add(Path path, NodeCacheListener listener) {
- fileListeners.put(path, listener);
- }
-
- @Override
- public void notify(Path path, PathChildrenCacheEvent event) {
- try {
- // Snapshot directoryListeners in case notification leads to new directoryListeners added
- Set<Map.Entry<Path, PathChildrenCacheListener>> directoryListenerSnapshot = new HashSet<>(directoryListeners.entrySet());
- for (Map.Entry<Path, PathChildrenCacheListener> listener : directoryListenerSnapshot) {
- if (path.isChildOf(listener.getKey()))
- listener.getValue().childEvent(curatorFramework, event);
- }
-
- // Snapshot directoryListeners in case notification leads to new directoryListeners added
- Set<Map.Entry<Path, NodeCacheListener>> fileListenerSnapshot = new HashSet<>(fileListeners.entrySet());
- for (Map.Entry<Path, NodeCacheListener> listener : fileListenerSnapshot) {
- if (path.equals(listener.getKey()))
- listener.getValue().nodeChanged();
- }
- }
- catch (Exception e) {
- e.printStackTrace(); // TODO: Remove
- throw new RuntimeException("Exception notifying listeners", e);
- }
- }
-
- }
-
- private class MockCompletionWaiter implements CompletionWaiter {
-
- @Override
- public void awaitCompletion(Duration timeout) {
- if (shouldTimeoutOnEnter) {
- throw new CompletionTimeoutException("");
- }
- }
-
- @Override
- public void notifyCompletion() {
- }
-
- }
-
- /** A lock which works inside a single vm */
- private class MockLock extends InterProcessSemaphoreMutex {
-
- private final String path;
-
- private Lock lock = null;
-
- public MockLock(String path) {
- super(curatorFramework, path);
- this.path = path;
- }
-
- @Override
- public boolean acquire(long timeout, TimeUnit unit) {
- if (throwExceptionOnLock)
- throw new CuratorLockException("Thrown by mock");
- if (timeoutOnLock) return false;
-
- try {
- lock = locks.lock(path, timeout, unit);
- return true;
- }
- catch (UncheckedTimeoutException e) {
- return false;
- }
- }
-
- @Override
- public void acquire() {
- if (throwExceptionOnLock)
- throw new CuratorLockException("Thrown by mock");
-
- lock = locks.lock(path);
- }
-
- @Override
- public void release() {
- if (lock != null)
- lock.close();
- }
-
- }
-
- private class MockAtomicCounter extends DistributedAtomicLong {
-
- private boolean initialized = false;
- private MockLongValue value = new MockLongValue(0); // yes, uninitialized returns 0 :-/
-
- public MockAtomicCounter(String path) {
- super(curatorFramework, path, retryPolicy);
- }
-
- @Override
- public boolean initialize(Long value) {
- if (initialized) return false;
- this.value = new MockLongValue(value);
- initialized = true;
- return true;
- }
-
- @Override
- public AtomicValue<Long> get() {
- if (value == null) return new MockLongValue(0);
- return value;
- }
-
- public AtomicValue<Long> add(Long delta) throws Exception {
- return trySet(value.postValue() + delta);
- }
-
- public AtomicValue<Long> subtract(Long delta) throws Exception {
- return trySet(value.postValue() - delta);
- }
-
- @Override
- public AtomicValue<Long> increment() {
- return trySet(value.postValue() + 1);
- }
-
- public AtomicValue<Long> decrement() throws Exception {
- return trySet(value.postValue() - 1);
- }
-
- @Override
- public AtomicValue<Long> trySet(Long longval) {
- value = new MockLongValue(longval);
- return value;
- }
-
- public void forceSet(Long newValue) throws Exception {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- public AtomicValue<Long> compareAndSet(Long expectedValue, Long newValue) throws Exception {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- }
-
- private class MockLongValue implements AtomicValue<Long> {
-
- private AtomicLong value = new AtomicLong();
-
- public MockLongValue(long value) {
- this.value.set(value);
- }
-
- @Override
- public boolean succeeded() {
- return true;
- }
-
- public void setValue(long value) {
- this.value.set(value);
- }
-
- @Override
- public Long preValue() {
- return value.get();
- }
-
- @Override
- public Long postValue() {
- return value.get();
- }
-
- @Override
- public AtomicStats getStats() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- }
-
- private class MockDirectoryCache implements DirectoryCache {
-
- /** The path this is caching and listening to */
- private Path path;
-
- public MockDirectoryCache(Path path) {
- this.path = path;
- }
-
- @Override
- public void start() {}
-
- @Override
- public void addListener(PathChildrenCacheListener listener) {
- listeners.add(path, listener);
- }
-
- @Override
- public List<ChildData> getCurrentData() {
- List<ChildData> childData = new ArrayList<>();
- for (String childName : getChildren(path)) {
- Path childPath = path.append(childName);
- childData.add(new ChildData(childPath.getAbsolute(), null, getData(childPath).get()));
- }
- return childData;
- }
-
- @Override
- public ChildData getCurrentData(Path fullPath) {
- if (!fullPath.getParentPath().equals(path)) {
- throw new IllegalArgumentException("Path '" + fullPath + "' is not a child path of '" + path + "'");
- }
-
- return getData(fullPath).map(bytes -> new ChildData(fullPath.getAbsolute(), null, bytes)).orElse(null);
- }
-
- private void collectData(Node parent, Path parentPath, List<ChildData> data) {
- for (Node child : parent.children().values()) {
- Path childPath = parentPath.append(child.name());
- data.add(new ChildData("/" + childPath.toString(), null, child.getContent()));
- }
- }
-
- @Override
- public void close() {}
-
- }
-
- private class MockFileCache implements FileCache {
-
- /** The path this is caching and listening to */
- private Path path;
-
- public MockFileCache(Path path) {
- this.path = path;
- }
-
- @Override
- public void start() {}
-
- @Override
- public void addListener(NodeCacheListener listener) {
- listeners.add(path, listener);
- }
-
- @Override
- public ChildData getCurrentData() {
- Node node = fileSystem.root().getNode(Paths.get(path.toString()), false);
- if (node == null) return null;
- return new ChildData("/" + path.toString(), null, node.getContent());
- }
-
- @Override
- public void close() {}
-
- }
-
- // ----- The rest of this file is adapting the Curator (non-recipe) API to the -----
- // ----- file system methods above. -----
- // ----- There's nothing to see unless you are interested in an illustration of -----
- // ----- the folly of fluent API's or, more generally, mankind. -----
- private abstract static class MockProtectACLCreateModeStatPathAndBytesable<String>
- implements ProtectACLCreateModeStatPathAndBytesable<String> {
-
- public BackgroundPathAndBytesable<String> withACL(List<ACL> list) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- public BackgroundPathAndBytesable<String> withACL(List<ACL> list, boolean b) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- public ProtectACLCreateModeStatPathAndBytesable<String> withMode(CreateMode createMode) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ACLCreateModeBackgroundPathAndBytesable<java.lang.String> withProtection() {
- return null;
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground() {
- return null;
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(Object o) {
- return null;
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback) {
- return null;
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o) {
- return null;
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
- return null;
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
- return null;
- }
-
- @Override
- public ACLBackgroundPathAndBytesable<String> storingStatIn(Stat stat) {
- return null;
- }
-
- }
-
- private class MockCreateBuilder implements CreateBuilder {
-
- private boolean createParents = false;
- private CreateMode createMode = CreateMode.PERSISTENT;
-
- @Override
- public ProtectACLCreateModeStatPathAndBytesable<String> creatingParentsIfNeeded() {
- createParents = true;
- return new MockProtectACLCreateModeStatPathAndBytesable<>() {
-
- @Override
- public String forPath(String s, byte[] bytes) throws Exception {
- return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners);
- }
-
- @Override
- public String forPath(String s) throws Exception {
- return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners);
- }
-
- };
- }
-
- @Override
- public ProtectACLCreateModeStatPathAndBytesable<String> creatingParentContainersIfNeeded() {
- return new MockProtectACLCreateModeStatPathAndBytesable<>() {
-
- @Override
- public String forPath(String s, byte[] bytes) throws Exception {
- return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners);
- }
-
- @Override
- public String forPath(String s) throws Exception {
- return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners);
- }
-
- };
- }
-
- @Override
- @Deprecated
- public ACLPathAndBytesable<String> withProtectedEphemeralSequential() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ACLCreateModeStatBackgroundPathAndBytesable<String> withProtection() {
- return null;
- }
-
- public String forPath(String s) throws Exception {
- return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners);
- }
-
- public String forPath(String s, byte[] bytes) throws Exception {
- return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners);
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public CreateBuilderMain withTtl(long l) {
- return null;
- }
-
- @Override
- public CreateBuilder2 orSetData() {
- return null;
- }
-
- @Override
- public CreateBuilder2 orSetData(int i) {
- return null;
- }
-
- @Override
- public CreateBackgroundModeStatACLable compressed() {
- return null;
- }
-
- @Override
- public CreateProtectACLCreateModePathAndBytesable<String> storingStatIn(Stat stat) {
- return null;
- }
-
- @Override
- public BackgroundPathAndBytesable<String> withACL(List<ACL> list) {
- return null;
- }
-
- @Override
- public ACLBackgroundPathAndBytesable<String> withMode(CreateMode createMode) {
- this.createMode = createMode;
- return this;
- }
-
- @Override
- public BackgroundPathAndBytesable<String> withACL(List<ACL> list, boolean b) {
- return null;
- }
- }
-
- private static class MockBackgroundPathableBuilder<T> implements BackgroundPathable<T>, Watchable<BackgroundPathable<T>> {
-
- @Override
- public ErrorListenerPathable<T> inBackground() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<T> inBackground(Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback, Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public T forPath(String s) throws Exception {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public BackgroundPathable<T> watched() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public BackgroundPathable<T> usingWatcher(Watcher watcher) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public BackgroundPathable<T> usingWatcher(CuratorWatcher curatorWatcher) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
- }
-
- private class MockGetChildrenBuilder extends MockBackgroundPathableBuilder<List<String>> implements GetChildrenBuilder {
-
- @Override
- public WatchPathable<List<String>> storingStatIn(Stat stat) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public List<String> forPath(String path) throws Exception {
- return getChildren(path, fileSystem.root());
- }
-
- }
-
- private class MockExistsBuilder extends MockBackgroundPathableBuilder<Stat> implements ExistsBuilder {
-
- @Override
- public Stat forPath(String path) throws Exception {
- try {
- Node node = getNode(path, fileSystem.root());
- Stat stat = new Stat();
- stat.setVersion(node.version());
- return stat;
- }
- catch (KeeperException.NoNodeException e) {
- return null;
- }
- }
-
- @Override
- public ACLableExistBuilderMain creatingParentsIfNeeded() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ACLableExistBuilderMain creatingParentContainersIfNeeded() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
- }
-
- private class MockDeleteBuilder extends MockBackgroundPathableBuilder<Void> implements DeleteBuilder {
-
- private boolean deleteChildren = false;
-
- @Override
- public BackgroundVersionable deletingChildrenIfNeeded() {
- deleteChildren = true;
- return this;
- }
-
- @Override
- public ChildrenDeletable guaranteed() {
- return this;
- }
-
- @Override
- public BackgroundPathable<Void> withVersion(int i) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- public Void forPath(String pathString) throws Exception {
- deleteNode(pathString, deleteChildren, fileSystem.root(), listeners);
- return null;
- }
-
- @Override
- public DeleteBuilderMain quietly() {
- return this;
- }
- }
-
- private class MockGetDataBuilder extends MockBackgroundPathableBuilder<byte[]> implements GetDataBuilder {
-
- @Override
- public GetDataWatchBackgroundStatable decompressed() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- public byte[] forPath(String path) throws Exception {
- return getData(path, fileSystem.root());
- }
-
- @Override
- public ErrorListenerPathable<byte[]> inBackground() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<byte[]> inBackground(Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public WatchPathable<byte[]> storingStatIn(Stat stat) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
- }
-
- // extends MockBackgroundACLPathAndBytesableBuilder<Stat>
- private class MockSetDataBuilder implements SetDataBuilder {
-
- @Override
- public SetDataBackgroundVersionable compressed() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public BackgroundPathAndBytesable<Stat> withVersion(int i) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public Stat forPath(String path, byte[] bytes) throws Exception {
- setData(path, bytes, fileSystem.root(), listeners);
- return null;
- }
-
- @Override
- public Stat forPath(String s) throws Exception {
- return null;
- }
-
- @Override
- public ErrorListenerPathAndBytesable<Stat> inBackground() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<Stat> inBackground(Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback, Object o) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
- }
-
- /** Allows addition of directoryListeners which are never called */
- private class MockListenable<T> implements Listenable<T> {
-
- @Override
- public void addListener(T t) {
- }
-
- @Override
- public void addListener(T t, Executor executor) {
- }
-
- @Override
- public void removeListener(T t) {
- }
-
- }
-
- private class MockCuratorTransactionFinal implements CuratorTransactionFinal {
-
- /** The new directory root in which the transactional changes are made */
- private Node newRoot;
-
- private boolean committed = false;
-
- private final DelayedListener delayedListener = new DelayedListener();
-
- public MockCuratorTransactionFinal() {
- newRoot = fileSystem.root().clone();
- }
-
- @Override
- public Collection<CuratorTransactionResult> commit() throws Exception {
- fileSystem.replaceRoot(newRoot);
- committed = true;
- delayedListener.commit();
- return null; // TODO
- }
-
- @Override
- public TransactionCreateBuilder create() {
- ensureNotCommitted();
- return new MockTransactionCreateBuilder();
- }
-
- @Override
- public TransactionDeleteBuilder delete() {
- ensureNotCommitted();
- return new MockTransactionDeleteBuilder();
- }
-
- @Override
- public TransactionSetDataBuilder setData() {
- ensureNotCommitted();
- return new MockTransactionSetDataBuilder();
- }
-
- @Override
- public TransactionCheckBuilder check() {
- ensureNotCommitted();
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- private void ensureNotCommitted() {
- if (committed) throw new IllegalStateException("transaction already committed");
- }
-
- private class MockTransactionCreateBuilder implements TransactionCreateBuilder {
-
- private CreateMode createMode = CreateMode.PERSISTENT;
-
- @Override
- public ACLCreateModePathAndBytesable<CuratorTransactionBridge> compressed() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ACLPathAndBytesable<CuratorTransactionBridge> withMode(CreateMode createMode) {
- this.createMode = createMode;
- return this;
- }
-
- @Override
- public CuratorTransactionBridge forPath(String s, byte[] bytes) throws Exception {
- createNode(s, bytes, false, createMode, newRoot, delayedListener);
- return new MockCuratorTransactionBridge();
- }
-
- @Override
- public CuratorTransactionBridge forPath(String s) throws Exception {
- createNode(s, new byte[0], false, createMode, newRoot, delayedListener);
- return new MockCuratorTransactionBridge();
- }
-
- @Override
- public TransactionCreateBuilder2 withTtl(long l) {
- return this;
- }
-
- @Override
- public Object withACL(List list, boolean b) {
- return this;
- }
-
- @Override
- public Object withACL(List list) {
- return this;
- }
- }
-
- private class MockTransactionDeleteBuilder implements TransactionDeleteBuilder {
-
- @Override
- public Pathable<CuratorTransactionBridge> withVersion(int i) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public CuratorTransactionBridge forPath(String path) throws Exception {
- deleteNode(path, false, newRoot, delayedListener);
- return new MockCuratorTransactionBridge();
- }
-
- }
-
- private class MockTransactionSetDataBuilder implements TransactionSetDataBuilder {
-
- @Override
- public VersionPathAndBytesable<CuratorTransactionBridge> compressed() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public PathAndBytesable<CuratorTransactionBridge> withVersion(int i) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public CuratorTransactionBridge forPath(String s, byte[] bytes) throws Exception {
- MockCurator.this.setData(s, bytes, newRoot, delayedListener);
- return new MockCuratorTransactionBridge();
- }
-
- @Override
- public CuratorTransactionBridge forPath(String s) throws Exception {
- MockCurator.this.setData(s, new byte[0], newRoot, delayedListener);
- return new MockCuratorTransactionBridge();
- }
-
- }
-
- private class MockCuratorTransactionBridge implements CuratorTransactionBridge {
-
- @Override
- public CuratorTransactionFinal and() {
- return MockCuratorTransactionFinal.this;
- }
-
- }
-
- /** A class which collects listen events and forwards them to the regular directoryListeners on commit */
- private class DelayedListener extends Listeners {
-
- private final List<Pair<Path, PathChildrenCacheEvent>> events = new ArrayList<>();
-
- @Override
- public void notify(Path path, PathChildrenCacheEvent event) {
- events.add(new Pair<>(path, event));
- }
-
- public void commit() {
- for (Pair<Path, PathChildrenCacheEvent> event : events)
- listeners.notify(event.getFirst(), event.getSecond());
- }
-
- }
-
- }
-
- private class MockCuratorFramework implements CuratorFramework {
-
- private CuratorFrameworkState curatorState = CuratorFrameworkState.LATENT;
-
- @Override
- public void start() {
- curatorState = CuratorFrameworkState.STARTED;
- }
-
- @Override
- public void close() {
- curatorState = CuratorFrameworkState.STOPPED;
- }
-
- @Override
- public CuratorFrameworkState getState() {
- return curatorState;
- }
-
- @Override
- @Deprecated
- public boolean isStarted() {
- return curatorState == CuratorFrameworkState.STARTED;
- }
-
- @Override
- public CreateBuilder create() {
- return new MockCreateBuilder();
- }
-
- @Override
- public DeleteBuilder delete() {
- return new MockDeleteBuilder();
- }
-
- @Override
- public ExistsBuilder checkExists() {
- return new MockExistsBuilder();
- }
-
- @Override
- public GetDataBuilder getData() {
- return new MockGetDataBuilder();
- }
-
- @Override
- public SetDataBuilder setData() {
- return new MockSetDataBuilder();
- }
-
- @Override
- public GetChildrenBuilder getChildren() {
- return new MockGetChildrenBuilder();
- }
-
- @Override
- public GetACLBuilder getACL() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public SetACLBuilder setACL() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public ReconfigBuilder reconfig() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public GetConfigBuilder getConfig() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public CuratorTransaction inTransaction() {
- return new MockCuratorTransactionFinal();
- }
-
- @Override
- public CuratorMultiTransaction transaction() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public TransactionOp transactionOp() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- @Deprecated
- public void sync(String path, Object backgroundContextObject) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public void createContainers(String s) throws Exception {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public Listenable<ConnectionStateListener> getConnectionStateListenable() {
- return new MockListenable<>();
- }
-
- @Override
- public Listenable<CuratorListener> getCuratorListenable() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public Listenable<UnhandledErrorListener> getUnhandledErrorListenable() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- @Deprecated
- public CuratorFramework nonNamespaceView() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public CuratorFramework usingNamespace(String newNamespace) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public String getNamespace() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public CuratorZookeeperClient getZookeeperClient() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Deprecated
- @Override
- public EnsurePath newNamespaceAwareEnsurePath(String path) {
- return new EnsurePath(path);
- }
-
- @Override
- public void clearWatcherReferences(Watcher watcher) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public boolean blockUntilConnected(int i, TimeUnit timeUnit) throws InterruptedException {
- return true;
- }
-
- @Override
- public void blockUntilConnected() throws InterruptedException {
-
- }
-
- @Override
- public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework() {
- return new WatcherRemoveCuratorFramework() {
- @Override
- public void removeWatchers() {
-
- }
-
- @Override
- public void start() {
-
- }
-
- @Override
- public void close() {
-
- }
-
- @Override
- public CuratorFrameworkState getState() {
- return null;
- }
-
- @Override
- public boolean isStarted() {
- return false;
- }
-
- @Override
- public CreateBuilder create() {
- return null;
- }
-
- @Override
- public DeleteBuilder delete() {
- return null;
- }
-
- @Override
- public ExistsBuilder checkExists() {
- return null;
- }
-
- @Override
- public GetDataBuilder getData() {
- return null;
- }
-
- @Override
- public SetDataBuilder setData() {
- return null;
- }
-
- @Override
- public GetChildrenBuilder getChildren() {
- return null;
- }
-
- @Override
- public GetACLBuilder getACL() {
- return null;
- }
-
- @Override
- public SetACLBuilder setACL() {
- return null;
- }
-
- @Override
- public ReconfigBuilder reconfig() {
- return null;
- }
-
- @Override
- public GetConfigBuilder getConfig() {
- return null;
- }
-
- @Override
- public CuratorTransaction inTransaction() {
- return null;
- }
-
- @Override
- public CuratorMultiTransaction transaction() {
- return null;
- }
-
- @Override
- public TransactionOp transactionOp() {
- return null;
- }
-
- @Override
- public void sync(String s, Object o) {
-
- }
-
- @Override
- public void createContainers(String s) throws Exception {
-
- }
-
- @Override
- public SyncBuilder sync() {
- return null;
- }
-
- @Override
- public RemoveWatchesBuilder watches() {
- return null;
- }
-
- @Override
- public Listenable<ConnectionStateListener> getConnectionStateListenable() {
- return null;
- }
-
- @Override
- public Listenable<CuratorListener> getCuratorListenable() {
- return null;
- }
-
- @Override
- public Listenable<UnhandledErrorListener> getUnhandledErrorListenable() {
- return null;
- }
-
- @Override
- public CuratorFramework nonNamespaceView() {
- return null;
- }
-
- @Override
- public CuratorFramework usingNamespace(String s) {
- return null;
- }
-
- @Override
- public String getNamespace() {
- return null;
- }
-
- @Override
- public CuratorZookeeperClient getZookeeperClient() {
- return null;
- }
-
- @Override
- public EnsurePath newNamespaceAwareEnsurePath(String s) {
- return null;
- }
-
- @Override
- public void clearWatcherReferences(Watcher watcher) {
-
- }
-
- @Override
- public boolean blockUntilConnected(int i, TimeUnit timeUnit) throws InterruptedException {
- return false;
- }
-
- @Override
- public void blockUntilConnected() throws InterruptedException {
-
- }
-
- @Override
- public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework() {
- return null;
- }
-
- @Override
- public ConnectionStateErrorPolicy getConnectionStateErrorPolicy() {
- return null;
- }
-
- @Override
- public QuorumVerifier getCurrentConfig() {
- return null;
- }
-
- @Override
- public SchemaSet getSchemaSet() {
- return null;
- }
-
- @Override
- public boolean isZk34CompatibilityMode() {
- return false;
- }
-
- @Override
- public CompletableFuture<Void> runSafe(Runnable runnable) {
- return null;
- }
- };
-
- }
-
- @Override
- public ConnectionStateErrorPolicy getConnectionStateErrorPolicy() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public QuorumVerifier getCurrentConfig() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public SchemaSet getSchemaSet() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public boolean isZk34CompatibilityMode() {
- return false;
- }
-
- @Override
- public CompletableFuture<Void> runSafe(Runnable runnable) {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public SyncBuilder sync() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- @Override
- public RemoveWatchesBuilder watches() {
- throw new UnsupportedOperationException("Not implemented in MockCurator");
- }
-
- }
-
}
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCuratorFramework.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCuratorFramework.java
new file mode 100644
index 00000000000..401c6e90cd8
--- /dev/null
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCuratorFramework.java
@@ -0,0 +1,1356 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.curator.mock;
+
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+import com.yahoo.collections.Pair;
+import com.yahoo.concurrent.Lock;
+import com.yahoo.concurrent.Locks;
+import com.yahoo.path.Path;
+import com.yahoo.vespa.curator.CompletionTimeoutException;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.recipes.CuratorLockException;
+import org.apache.curator.CuratorZookeeperClient;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.WatcherRemoveCuratorFramework;
+import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable;
+import org.apache.curator.framework.api.ACLCreateModeBackgroundPathAndBytesable;
+import org.apache.curator.framework.api.ACLCreateModePathAndBytesable;
+import org.apache.curator.framework.api.ACLCreateModeStatBackgroundPathAndBytesable;
+import org.apache.curator.framework.api.ACLPathAndBytesable;
+import org.apache.curator.framework.api.ACLableExistBuilderMain;
+import org.apache.curator.framework.api.BackgroundCallback;
+import org.apache.curator.framework.api.BackgroundPathAndBytesable;
+import org.apache.curator.framework.api.BackgroundPathable;
+import org.apache.curator.framework.api.BackgroundVersionable;
+import org.apache.curator.framework.api.ChildrenDeletable;
+import org.apache.curator.framework.api.CreateBackgroundModeStatACLable;
+import org.apache.curator.framework.api.CreateBuilder;
+import org.apache.curator.framework.api.CreateBuilder2;
+import org.apache.curator.framework.api.CreateBuilderMain;
+import org.apache.curator.framework.api.CreateProtectACLCreateModePathAndBytesable;
+import org.apache.curator.framework.api.CuratorListener;
+import org.apache.curator.framework.api.CuratorWatcher;
+import org.apache.curator.framework.api.DeleteBuilder;
+import org.apache.curator.framework.api.DeleteBuilderMain;
+import org.apache.curator.framework.api.ErrorListenerPathAndBytesable;
+import org.apache.curator.framework.api.ErrorListenerPathable;
+import org.apache.curator.framework.api.ExistsBuilder;
+import org.apache.curator.framework.api.GetACLBuilder;
+import org.apache.curator.framework.api.GetChildrenBuilder;
+import org.apache.curator.framework.api.GetConfigBuilder;
+import org.apache.curator.framework.api.GetDataBuilder;
+import org.apache.curator.framework.api.GetDataWatchBackgroundStatable;
+import org.apache.curator.framework.api.PathAndBytesable;
+import org.apache.curator.framework.api.Pathable;
+import org.apache.curator.framework.api.ProtectACLCreateModeStatPathAndBytesable;
+import org.apache.curator.framework.api.ReconfigBuilder;
+import org.apache.curator.framework.api.RemoveWatchesBuilder;
+import org.apache.curator.framework.api.SetACLBuilder;
+import org.apache.curator.framework.api.SetDataBackgroundVersionable;
+import org.apache.curator.framework.api.SetDataBuilder;
+import org.apache.curator.framework.api.SyncBuilder;
+import org.apache.curator.framework.api.UnhandledErrorListener;
+import org.apache.curator.framework.api.VersionPathAndBytesable;
+import org.apache.curator.framework.api.WatchPathable;
+import org.apache.curator.framework.api.Watchable;
+import org.apache.curator.framework.api.transaction.CuratorMultiTransaction;
+import org.apache.curator.framework.api.transaction.CuratorTransaction;
+import org.apache.curator.framework.api.transaction.CuratorTransactionBridge;
+import org.apache.curator.framework.api.transaction.CuratorTransactionFinal;
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.curator.framework.api.transaction.TransactionCheckBuilder;
+import org.apache.curator.framework.api.transaction.TransactionCreateBuilder;
+import org.apache.curator.framework.api.transaction.TransactionCreateBuilder2;
+import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder;
+import org.apache.curator.framework.api.transaction.TransactionOp;
+import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder;
+import org.apache.curator.framework.imps.CuratorFrameworkState;
+import org.apache.curator.framework.listen.Listenable;
+import org.apache.curator.framework.recipes.atomic.AtomicStats;
+import org.apache.curator.framework.recipes.atomic.AtomicValue;
+import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.NodeCacheListener;
+import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
+import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
+import org.apache.curator.framework.recipes.locks.InterProcessLock;
+import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
+import org.apache.curator.framework.schema.SchemaSet;
+import org.apache.curator.framework.state.ConnectionStateErrorPolicy;
+import org.apache.curator.framework.state.ConnectionStateListener;
+import org.apache.curator.retry.RetryForever;
+import org.apache.curator.utils.EnsurePath;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
+
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A mock implementation of{@link CuratorFramework} for testing purposes.
+ *
+ * @author mpolden
+ */
+public class MockCuratorFramework implements CuratorFramework {
+
+ private final boolean shouldTimeoutOnEnter;
+ private final boolean stableOrdering;
+ private final Locks<String> locks = new Locks<>(Long.MAX_VALUE, TimeUnit.DAYS);
+
+ /** The file system used by this mock to store zookeeper files and directories */
+ private final MemoryFileSystem fileSystem = new MemoryFileSystem();
+
+ /** Atomic counters. A more accurate mock would store these as files in the file system */
+ private final Map<String, MockAtomicCounter> atomicCounters = new ConcurrentHashMap<>();
+
+ /** Listeners to changes to a particular path */
+ private final ListenerMap listeners = new ListenerMap();
+
+ private CuratorFrameworkState curatorState = CuratorFrameworkState.LATENT;
+ private int monotonicallyIncreasingNumber = 0;
+
+ public MockCuratorFramework(boolean stableOrdering, boolean shouldTimeoutOnEnter) {
+ this.stableOrdering = stableOrdering;
+ this.shouldTimeoutOnEnter = shouldTimeoutOnEnter;
+ }
+
+ public Map<String, MockAtomicCounter> atomicCounters() {
+ return Collections.unmodifiableMap(atomicCounters);
+ }
+
+ public MemoryFileSystem fileSystem() {
+ return fileSystem;
+ }
+
+ @Override
+ public void start() {
+ curatorState = CuratorFrameworkState.STARTED;
+ }
+
+ @Override
+ public void close() {
+ curatorState = CuratorFrameworkState.STOPPED;
+ }
+
+ @Override
+ public CuratorFrameworkState getState() {
+ return curatorState;
+ }
+
+ @Override
+ @Deprecated
+ public boolean isStarted() {
+ return curatorState == CuratorFrameworkState.STARTED;
+ }
+
+ @Override
+ public CreateBuilder create() {
+ return new MockCreateBuilder();
+ }
+
+ @Override
+ public DeleteBuilder delete() {
+ return new MockDeleteBuilder();
+ }
+
+ @Override
+ public ExistsBuilder checkExists() {
+ return new MockExistsBuilder();
+ }
+
+ @Override
+ public GetDataBuilder getData() {
+ return new MockGetDataBuilder();
+ }
+
+ @Override
+ public SetDataBuilder setData() {
+ return new MockSetDataBuilder();
+ }
+
+ @Override
+ public GetChildrenBuilder getChildren() {
+ return new MockGetChildrenBuilder();
+ }
+
+ @Override
+ public GetACLBuilder getACL() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public SetACLBuilder setACL() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+
+ @Override
+ public ReconfigBuilder reconfig() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public GetConfigBuilder getConfig() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public CuratorTransaction inTransaction() {
+ return new MockCuratorTransactionFinal();
+ }
+
+ @Override
+ public CuratorMultiTransaction transaction() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public TransactionOp transactionOp() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public RemoveWatchesBuilder watches() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public ConnectionStateErrorPolicy getConnectionStateErrorPolicy() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public QuorumVerifier getCurrentConfig() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public SchemaSet getSchemaSet() { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ public boolean isZk34CompatibilityMode() { return false; }
+
+ @Override
+ public CompletableFuture<Void> runSafe(Runnable runnable) { throw new UnsupportedOperationException("Not implemented in MockCurator"); }
+
+ @Override
+ @Deprecated
+ public void sync(String path, Object backgroundContextObject) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public void createContainers(String s) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public Listenable<ConnectionStateListener> getConnectionStateListenable() {
+ return new MockListenable<>();
+ }
+
+ @Override
+ public Listenable<CuratorListener> getCuratorListenable() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public Listenable<UnhandledErrorListener> getUnhandledErrorListenable() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ @Deprecated
+ public CuratorFramework nonNamespaceView() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public CuratorFramework usingNamespace(String newNamespace) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public String getNamespace() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public CuratorZookeeperClient getZookeeperClient() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Deprecated
+ @Override
+ public EnsurePath newNamespaceAwareEnsurePath(String path) {
+ return new EnsurePath(path);
+ }
+
+ @Override
+ public void clearWatcherReferences(Watcher watcher) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public boolean blockUntilConnected(int i, TimeUnit timeUnit) {
+ return true;
+ }
+
+ @Override
+ public void blockUntilConnected() {
+ }
+
+ @Override
+ public SyncBuilder sync() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ // ----- Factory methods for mocks */
+
+ public InterProcessLock createMutex(String path) {
+ return new MockLock(path);
+ }
+
+ public MockAtomicCounter createAtomicCounter(String path) {
+ return atomicCounters.computeIfAbsent(path, (k) -> new MockAtomicCounter(path));
+ }
+
+ public Curator.CompletionWaiter createCompletionWaiter() {
+ return new MockCompletionWaiter();
+ }
+
+ public Curator.DirectoryCache createDirectoryCache(String path) {
+ return new MockDirectoryCache(Path.fromString(path));
+ }
+
+ public Curator.FileCache createFileCache(String path) {
+ return new MockFileCache(Path.fromString(path));
+ }
+
+ // ----- Start of adaptor methods from Curator to the mock file system -----
+
+ /** Creates a node below the given directory root */
+ private String createNode(String pathString, byte[] content, boolean createParents, CreateMode createMode, MemoryFileSystem.Node root, Listeners listeners)
+ throws KeeperException.NodeExistsException, KeeperException.NoNodeException {
+ validatePath(pathString);
+ Path path = Path.fromString(pathString);
+ if (path.isRoot()) return "/"; // the root already exists
+ MemoryFileSystem.Node parent = root.getNode(Paths.get(path.getParentPath().toString()), createParents);
+ String name = nodeName(path.getName(), createMode);
+
+ if (parent == null)
+ throw new KeeperException.NoNodeException(path.getParentPath().toString());
+ if (parent.children().containsKey(path.getName()))
+ throw new KeeperException.NodeExistsException(path.toString());
+
+ parent.add(name).setContent(content);
+ String nodePath = "/" + path.getParentPath().toString() + "/" + name;
+ listeners.notify(Path.fromString(nodePath), content, PathChildrenCacheEvent.Type.CHILD_ADDED);
+ return nodePath;
+ }
+
+ /** Deletes a node below the given directory root */
+ private void deleteNode(String pathString, boolean deleteChildren, MemoryFileSystem.Node root, Listeners listeners)
+ throws KeeperException.NoNodeException, KeeperException.NotEmptyException {
+ validatePath(pathString);
+ Path path = Path.fromString(pathString);
+ MemoryFileSystem.Node parent = root.getNode(Paths.get(path.getParentPath().toString()), false);
+ if (parent == null) throw new KeeperException.NoNodeException(path.toString());
+ MemoryFileSystem.Node node = parent.children().get(path.getName());
+ if (node == null) throw new KeeperException.NoNodeException(path.getName() + " under " + parent);
+ if ( ! node.children().isEmpty() && ! deleteChildren)
+ throw new KeeperException.NotEmptyException(path.toString());
+ parent.remove(path.getName());
+ listeners.notify(path, new byte[0], PathChildrenCacheEvent.Type.CHILD_REMOVED);
+ }
+
+ /** Returns the data of a node */
+ private byte[] getData(String pathString, MemoryFileSystem.Node root) throws KeeperException.NoNodeException {
+ validatePath(pathString);
+ return getNode(pathString, root).getContent();
+ }
+
+ /** sets the data of an existing node */
+ private void setData(String pathString, byte[] content, MemoryFileSystem.Node root, Listeners listeners)
+ throws KeeperException.NoNodeException {
+ validatePath(pathString);
+ getNode(pathString, root).setContent(content);
+ listeners.notify(Path.fromString(pathString), content, PathChildrenCacheEvent.Type.CHILD_UPDATED);
+ }
+
+ private List<String> getChildren(String path, MemoryFileSystem.Node root) throws KeeperException.NoNodeException {
+ validatePath(path);
+ MemoryFileSystem.Node node = root.getNode(Paths.get(path), false);
+ if (node == null) throw new KeeperException.NoNodeException(path);
+ List<String> children = new ArrayList<>(node.children().keySet());
+ if (! stableOrdering)
+ Collections.shuffle(children);
+ return children;
+ }
+
+ /** Returns a node or throws the appropriate exception if it doesn't exist */
+ private MemoryFileSystem.Node getNode(String pathString, MemoryFileSystem.Node root) throws KeeperException.NoNodeException {
+ validatePath(pathString);
+ Path path = Path.fromString(pathString);
+ MemoryFileSystem.Node parent = root.getNode(Paths.get(path.getParentPath().toString()), false);
+ if (parent == null) throw new KeeperException.NoNodeException(path.toString());
+ MemoryFileSystem.Node node = parent.children().get(path.getName());
+ if (node == null) throw new KeeperException.NoNodeException(path.toString());
+ return node;
+ }
+
+ private String nodeName(String baseName, CreateMode createMode) {
+ switch (createMode) {
+ case PERSISTENT: case EPHEMERAL: return baseName;
+ case PERSISTENT_SEQUENTIAL: case EPHEMERAL_SEQUENTIAL: return baseName + monotonicallyIncreasingNumber++;
+ default: throw new UnsupportedOperationException(createMode + " support not implemented in MockCurator");
+ }
+ }
+
+ /** Validates a path using the same rules as ZooKeeper */
+ public static String validatePath(String path) throws IllegalArgumentException {
+ if (path == null) throw new IllegalArgumentException("Path cannot be null");
+ if (path.length() == 0) throw new IllegalArgumentException("Path length must be > 0");
+ if (path.charAt(0) != '/') throw new IllegalArgumentException("Path must start with / character");
+ if (path.length() == 1) return path; // done checking - it's the root
+ if (path.charAt(path.length() - 1) == '/')
+ throw new IllegalArgumentException("Path must not end with / character");
+
+ String reason = null;
+ char lastc = '/';
+ char[] chars = path.toCharArray();
+ char c;
+ for (int i = 1; i < chars.length; lastc = chars[i], i++) {
+ c = chars[i];
+
+ if (c == 0) {
+ reason = "null character not allowed @" + i;
+ break;
+ } else if (c == '/' && lastc == '/') {
+ reason = "empty node name specified @" + i;
+ break;
+ } else if (c == '.' && lastc == '.') {
+ if (chars[i-2] == '/' && ((i + 1 == chars.length) || chars[i+1] == '/')) {
+ reason = "relative paths not allowed @" + i;
+ break;
+ }
+ } else if (c == '.') {
+ if (chars[i-1] == '/' && ((i + 1 == chars.length) || chars[i+1] == '/')) {
+ reason = "relative paths not allowed @" + i;
+ break;
+ }
+ } else if (c > '\u0000' && c < '\u001f' || c > '\u007f' && c < '\u009F'
+ || c > '\ud800' && c < '\uf8ff' || c > '\ufff0' && c < '\uffff') {
+ reason = "invalid charater @" + i;
+ break;
+ }
+ }
+
+ if (reason != null)
+ throw new IllegalArgumentException("Invalid path string \"" + path + "\" caused by " + reason);
+ return path;
+ }
+
+ /**
+ * Invocation of changes to the file system state is abstracted through this to allow transactional
+ * changes to notify on commit
+ */
+ private abstract static class Listeners {
+
+ /** Translating method */
+ public final void notify(Path path, byte[] data, PathChildrenCacheEvent.Type type) {
+ String pathString = "/" + path.toString(); // this silly path class strips the leading "/" :-/
+ PathChildrenCacheEvent event = new PathChildrenCacheEvent(type, new ChildData(pathString, null, data));
+ notify(path, event);
+ }
+
+ public abstract void notify(Path path, PathChildrenCacheEvent event);
+
+ }
+
+ /** The regular listener implementation which notifies registered file and directory listeners */
+ private class ListenerMap extends Listeners {
+
+ private final Map<Path, PathChildrenCacheListener> directoryListeners = new ConcurrentHashMap<>();
+ private final Map<Path, NodeCacheListener> fileListeners = new ConcurrentHashMap<>();
+
+ public void add(Path path, PathChildrenCacheListener listener) {
+ directoryListeners.put(path, listener);
+ }
+
+ public void add(Path path, NodeCacheListener listener) {
+ fileListeners.put(path, listener);
+ }
+
+ @Override
+ public void notify(Path path, PathChildrenCacheEvent event) {
+ try {
+ // Snapshot directoryListeners in case notification leads to new directoryListeners added
+ Set<Map.Entry<Path, PathChildrenCacheListener>> directoryListenerSnapshot = new HashSet<>(directoryListeners.entrySet());
+ for (Map.Entry<Path, PathChildrenCacheListener> listener : directoryListenerSnapshot) {
+ if (path.isChildOf(listener.getKey()))
+ listener.getValue().childEvent(MockCuratorFramework.this, event);
+ }
+
+ // Snapshot directoryListeners in case notification leads to new directoryListeners added
+ Set<Map.Entry<Path, NodeCacheListener>> fileListenerSnapshot = new HashSet<>(fileListeners.entrySet());
+ for (Map.Entry<Path, NodeCacheListener> listener : fileListenerSnapshot) {
+ if (path.equals(listener.getKey()))
+ listener.getValue().nodeChanged();
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace(); // TODO: Remove
+ throw new RuntimeException("Exception notifying listeners", e);
+ }
+ }
+
+ }
+
+ private class MockCompletionWaiter implements Curator.CompletionWaiter {
+
+ @Override
+ public void awaitCompletion(Duration timeout) {
+ if (shouldTimeoutOnEnter) {
+ throw new CompletionTimeoutException("");
+ }
+ }
+
+ @Override
+ public void notifyCompletion() {
+ }
+
+ }
+
+ /** A lock which works inside a single vm */
+ private class MockLock extends InterProcessSemaphoreMutex {
+
+ public boolean timeoutOnLock = false;
+ public boolean throwExceptionOnLock = false;
+
+ private final String path;
+
+ private Lock lock = null;
+
+ public MockLock(String path) {
+ super(MockCuratorFramework.this, path);
+ this.path = path;
+ }
+
+ @Override
+ public boolean acquire(long timeout, TimeUnit unit) {
+ if (throwExceptionOnLock)
+ throw new CuratorLockException("Thrown by mock");
+ if (timeoutOnLock) return false;
+
+ try {
+ lock = locks.lock(path, timeout, unit);
+ return true;
+ }
+ catch (UncheckedTimeoutException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void acquire() {
+ if (throwExceptionOnLock)
+ throw new CuratorLockException("Thrown by mock");
+
+ lock = locks.lock(path);
+ }
+
+ @Override
+ public void release() {
+ if (lock != null)
+ lock.close();
+ }
+
+ }
+
+ private class MockAtomicCounter extends DistributedAtomicLong {
+
+ private boolean initialized = false;
+ private MockLongValue value = new MockLongValue(0); // yes, uninitialized returns 0 :-/
+
+ public MockAtomicCounter(String path) {
+ super(MockCuratorFramework.this, path, new RetryForever(1_000));
+ }
+
+ @Override
+ public boolean initialize(Long value) {
+ if (initialized) return false;
+ this.value = new MockLongValue(value);
+ initialized = true;
+ return true;
+ }
+
+ @Override
+ public AtomicValue<Long> get() {
+ if (value == null) return new MockLongValue(0);
+ return value;
+ }
+
+ public AtomicValue<Long> add(Long delta) {
+ return trySet(value.postValue() + delta);
+ }
+
+ public AtomicValue<Long> subtract(Long delta) {
+ return trySet(value.postValue() - delta);
+ }
+
+ @Override
+ public AtomicValue<Long> increment() {
+ return trySet(value.postValue() + 1);
+ }
+
+ public AtomicValue<Long> decrement() {
+ return trySet(value.postValue() - 1);
+ }
+
+ @Override
+ public AtomicValue<Long> trySet(Long longval) {
+ value = new MockLongValue(longval);
+ return value;
+ }
+
+ public void forceSet(Long newValue) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ public AtomicValue<Long> compareAndSet(Long expectedValue, Long newValue) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ }
+
+ private static class MockLongValue implements AtomicValue<Long> {
+
+ private final AtomicLong value = new AtomicLong();
+
+ public MockLongValue(long value) {
+ this.value.set(value);
+ }
+
+ @Override
+ public boolean succeeded() {
+ return true;
+ }
+
+ public void setValue(long value) {
+ this.value.set(value);
+ }
+
+ @Override
+ public Long preValue() {
+ return value.get();
+ }
+
+ @Override
+ public Long postValue() {
+ return value.get();
+ }
+
+ @Override
+ public AtomicStats getStats() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ }
+
+ private class MockDirectoryCache implements Curator.DirectoryCache {
+
+ /** The path this is caching and listening to */
+ private final Path path;
+
+ public MockDirectoryCache(Path path) {
+ this.path = path;
+ }
+
+ @Override
+ public void start() {}
+
+ @Override
+ public void addListener(PathChildrenCacheListener listener) {
+ listeners.add(path, listener);
+ }
+
+ @Override
+ public List<ChildData> getCurrentData() {
+ List<ChildData> childData = new ArrayList<>();
+ for (String childName : getChildren(path)) {
+ Path childPath = path.append(childName);
+ childData.add(new ChildData(childPath.getAbsolute(), null, getData(childPath).get()));
+ }
+ return childData;
+ }
+
+ @Override
+ public ChildData getCurrentData(Path fullPath) {
+ if (!fullPath.getParentPath().equals(path)) {
+ throw new IllegalArgumentException("Path '" + fullPath + "' is not a child path of '" + path + "'");
+ }
+
+ return getData(fullPath).map(bytes -> new ChildData(fullPath.getAbsolute(), null, bytes)).orElse(null);
+ }
+
+ @Override
+ public void close() {}
+
+ private List<String> getChildren(Path path) {
+ try {
+ return MockCuratorFramework.this.getChildren().forPath(path.getAbsolute());
+ } catch (KeeperException.NoNodeException e) {
+ return List.of();
+ } catch (Exception e) {
+ throw new RuntimeException("Could not get children of " + path.getAbsolute(), e);
+ }
+ }
+
+ private Optional<byte[]> getData(Path path) {
+ try {
+ return Optional.of(MockCuratorFramework.this.getData().forPath(path.getAbsolute()));
+ }
+ catch (KeeperException.NoNodeException e) {
+ return Optional.empty();
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Could not get data at " + path.getAbsolute(), e);
+ }
+ }
+
+ }
+
+ private class MockFileCache implements Curator.FileCache {
+
+ /** The path this is caching and listening to */
+ private final Path path;
+
+ public MockFileCache(Path path) {
+ this.path = path;
+ }
+
+ @Override
+ public void start() {}
+
+ @Override
+ public void addListener(NodeCacheListener listener) {
+ listeners.add(path, listener);
+ }
+
+ @Override
+ public ChildData getCurrentData() {
+ MemoryFileSystem.Node node = fileSystem.root().getNode(Paths.get(path.toString()), false);
+ if (node == null) return null;
+ return new ChildData("/" + path.toString(), null, node.getContent());
+ }
+
+ @Override
+ public void close() {}
+
+ }
+
+
+ // ----- The rest of this file is adapting the Curator (non-recipe) API to the -----
+ // ----- file system methods above. -----
+ // ----- There's nothing to see unless you are interested in an illustration of -----
+ // ----- the folly of fluent API's or, more generally, mankind. -----
+ private abstract static class MockProtectACLCreateModeStatPathAndBytesable<String>
+ implements ProtectACLCreateModeStatPathAndBytesable<String> {
+
+ public BackgroundPathAndBytesable<String> withACL(List<ACL> list) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ public BackgroundPathAndBytesable<String> withACL(List<ACL> list, boolean b) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ public ProtectACLCreateModeStatPathAndBytesable<String> withMode(CreateMode createMode) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ACLCreateModeBackgroundPathAndBytesable<java.lang.String> withProtection() {
+ return null;
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground() {
+ return null;
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(Object o) {
+ return null;
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback) {
+ return null;
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o) {
+ return null;
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
+ return null;
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
+ return null;
+ }
+
+ @Override
+ public ACLBackgroundPathAndBytesable<String> storingStatIn(Stat stat) {
+ return null;
+ }
+
+ }
+
+ private class MockCreateBuilder implements CreateBuilder {
+
+ private boolean createParents = false;
+ private CreateMode createMode = CreateMode.PERSISTENT;
+
+ @Override
+ public ProtectACLCreateModeStatPathAndBytesable<String> creatingParentsIfNeeded() {
+ createParents = true;
+ return new MockProtectACLCreateModeStatPathAndBytesable<>() {
+
+ @Override
+ public String forPath(String s, byte[] bytes) throws Exception {
+ return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners);
+ }
+
+ @Override
+ public String forPath(String s) throws Exception {
+ return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners);
+ }
+
+ };
+ }
+
+ @Override
+ public ProtectACLCreateModeStatPathAndBytesable<String> creatingParentContainersIfNeeded() {
+ return new MockProtectACLCreateModeStatPathAndBytesable<>() {
+
+ @Override
+ public String forPath(String s, byte[] bytes) throws Exception {
+ return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners);
+ }
+
+ @Override
+ public String forPath(String s) throws Exception {
+ return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners);
+ }
+
+ };
+ }
+
+ @Override
+ @Deprecated
+ public ACLPathAndBytesable<String> withProtectedEphemeralSequential() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ACLCreateModeStatBackgroundPathAndBytesable<String> withProtection() {
+ return null;
+ }
+
+ public String forPath(String s) throws Exception {
+ return createNode(s, new byte[0], createParents, createMode, fileSystem.root(), listeners);
+ }
+
+ public String forPath(String s, byte[] bytes) throws Exception {
+ return createNode(s, bytes, createParents, createMode, fileSystem.root(), listeners);
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<String> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public CreateBuilderMain withTtl(long l) {
+ return null;
+ }
+
+ @Override
+ public CreateBuilder2 orSetData() {
+ return null;
+ }
+
+ @Override
+ public CreateBuilder2 orSetData(int i) {
+ return null;
+ }
+
+ @Override
+ public CreateBackgroundModeStatACLable compressed() {
+ return null;
+ }
+
+ @Override
+ public CreateProtectACLCreateModePathAndBytesable<String> storingStatIn(Stat stat) {
+ return null;
+ }
+
+ @Override
+ public BackgroundPathAndBytesable<String> withACL(List<ACL> list) {
+ return null;
+ }
+
+ @Override
+ public ACLBackgroundPathAndBytesable<String> withMode(CreateMode createMode) {
+ this.createMode = createMode;
+ return this;
+ }
+
+ @Override
+ public BackgroundPathAndBytesable<String> withACL(List<ACL> list, boolean b) {
+ return null;
+ }
+ }
+
+ private static class MockBackgroundPathableBuilder<T> implements BackgroundPathable<T>, Watchable<BackgroundPathable<T>> {
+
+ @Override
+ public ErrorListenerPathable<T> inBackground() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<T> inBackground(Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback, Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<T> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public T forPath(String s) throws Exception {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public BackgroundPathable<T> watched() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public BackgroundPathable<T> usingWatcher(Watcher watcher) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public BackgroundPathable<T> usingWatcher(CuratorWatcher curatorWatcher) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+ }
+
+ private class MockGetChildrenBuilder extends MockBackgroundPathableBuilder<List<String>> implements GetChildrenBuilder {
+
+ @Override
+ public WatchPathable<List<String>> storingStatIn(Stat stat) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public List<String> forPath(String path) throws Exception {
+ return getChildren(path, fileSystem.root());
+ }
+
+ }
+
+ private class MockExistsBuilder extends MockBackgroundPathableBuilder<Stat> implements ExistsBuilder {
+
+ @Override
+ public Stat forPath(String path) throws Exception {
+ try {
+ MemoryFileSystem.Node node = getNode(path, fileSystem.root());
+ Stat stat = new Stat();
+ stat.setVersion(node.version());
+ return stat;
+ }
+ catch (KeeperException.NoNodeException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public ACLableExistBuilderMain creatingParentsIfNeeded() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ACLableExistBuilderMain creatingParentContainersIfNeeded() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+ }
+
+ private class MockDeleteBuilder extends MockBackgroundPathableBuilder<Void> implements DeleteBuilder {
+
+ private boolean deleteChildren = false;
+
+ @Override
+ public BackgroundVersionable deletingChildrenIfNeeded() {
+ deleteChildren = true;
+ return this;
+ }
+
+ @Override
+ public ChildrenDeletable guaranteed() {
+ return this;
+ }
+
+ @Override
+ public BackgroundPathable<Void> withVersion(int i) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ public Void forPath(String pathString) throws Exception {
+ deleteNode(pathString, deleteChildren, fileSystem.root(), listeners);
+ return null;
+ }
+
+ @Override
+ public DeleteBuilderMain quietly() {
+ return this;
+ }
+ }
+
+ private class MockGetDataBuilder extends MockBackgroundPathableBuilder<byte[]> implements GetDataBuilder {
+
+ @Override
+ public GetDataWatchBackgroundStatable decompressed() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ public byte[] forPath(String path) throws Exception {
+ return getData(path, fileSystem.root());
+ }
+
+ @Override
+ public ErrorListenerPathable<byte[]> inBackground() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<byte[]> inBackground(Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathable<byte[]> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public WatchPathable<byte[]> storingStatIn(Stat stat) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+ }
+
+ // extends MockBackgroundACLPathAndBytesableBuilder<Stat>
+ private class MockSetDataBuilder implements SetDataBuilder {
+
+ @Override
+ public SetDataBackgroundVersionable compressed() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public BackgroundPathAndBytesable<Stat> withVersion(int i) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public Stat forPath(String path, byte[] bytes) throws Exception {
+ setData(path, bytes, fileSystem.root(), listeners);
+ return null;
+ }
+
+ @Override
+ public Stat forPath(String s) throws Exception {
+ return null;
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<Stat> inBackground() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<Stat> inBackground(Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback, Object o) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ErrorListenerPathAndBytesable<Stat> inBackground(BackgroundCallback backgroundCallback, Object o, Executor executor) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+ }
+
+ /** Allows addition of directoryListeners which are never called */
+ private class MockListenable<T> implements Listenable<T> {
+
+ @Override
+ public void addListener(T t) {
+ }
+
+ @Override
+ public void addListener(T t, Executor executor) {
+ }
+
+ @Override
+ public void removeListener(T t) {
+ }
+
+ }
+
+ private class MockCuratorTransactionFinal implements CuratorTransactionFinal {
+
+ /** The new directory root in which the transactional changes are made */
+ private MemoryFileSystem.Node newRoot;
+
+ private boolean committed = false;
+
+ private final DelayedListener delayedListener = new DelayedListener();
+
+ public MockCuratorTransactionFinal() {
+ newRoot = fileSystem.root().clone();
+ }
+
+ @Override
+ public Collection<CuratorTransactionResult> commit() throws Exception {
+ fileSystem.replaceRoot(newRoot);
+ committed = true;
+ delayedListener.commit();
+ return null; // TODO
+ }
+
+ @Override
+ public TransactionCreateBuilder create() {
+ ensureNotCommitted();
+ return new MockTransactionCreateBuilder();
+ }
+
+ @Override
+ public TransactionDeleteBuilder delete() {
+ ensureNotCommitted();
+ return new MockTransactionDeleteBuilder();
+ }
+
+ @Override
+ public TransactionSetDataBuilder setData() {
+ ensureNotCommitted();
+ return new MockTransactionSetDataBuilder();
+ }
+
+ @Override
+ public TransactionCheckBuilder check() {
+ ensureNotCommitted();
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ private void ensureNotCommitted() {
+ if (committed) throw new IllegalStateException("transaction already committed");
+ }
+
+ private class MockTransactionCreateBuilder implements TransactionCreateBuilder {
+
+ private CreateMode createMode = CreateMode.PERSISTENT;
+
+ @Override
+ public ACLCreateModePathAndBytesable<CuratorTransactionBridge> compressed() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public ACLPathAndBytesable<CuratorTransactionBridge> withMode(CreateMode createMode) {
+ this.createMode = createMode;
+ return this;
+ }
+
+ @Override
+ public CuratorTransactionBridge forPath(String s, byte[] bytes) throws Exception {
+ createNode(s, bytes, false, createMode, newRoot, delayedListener);
+ return new MockCuratorTransactionBridge();
+ }
+
+ @Override
+ public CuratorTransactionBridge forPath(String s) throws Exception {
+ createNode(s, new byte[0], false, createMode, newRoot, delayedListener);
+ return new MockCuratorTransactionBridge();
+ }
+
+ @Override
+ public TransactionCreateBuilder2 withTtl(long l) {
+ return this;
+ }
+
+ @Override
+ public Object withACL(List list, boolean b) {
+ return this;
+ }
+
+ @Override
+ public Object withACL(List list) {
+ return this;
+ }
+ }
+
+ private class MockTransactionDeleteBuilder implements TransactionDeleteBuilder {
+
+ @Override
+ public Pathable<CuratorTransactionBridge> withVersion(int i) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public CuratorTransactionBridge forPath(String path) throws Exception {
+ deleteNode(path, false, newRoot, delayedListener);
+ return new MockCuratorTransactionBridge();
+ }
+
+ }
+
+ private class MockTransactionSetDataBuilder implements TransactionSetDataBuilder {
+
+ @Override
+ public VersionPathAndBytesable<CuratorTransactionBridge> compressed() {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public PathAndBytesable<CuratorTransactionBridge> withVersion(int i) {
+ throw new UnsupportedOperationException("Not implemented in MockCurator");
+ }
+
+ @Override
+ public CuratorTransactionBridge forPath(String s, byte[] bytes) throws Exception {
+ MockCuratorFramework.this.setData(s, bytes, newRoot, delayedListener);
+ return new MockCuratorTransactionBridge();
+ }
+
+ @Override
+ public CuratorTransactionBridge forPath(String s) throws Exception {
+ MockCuratorFramework.this.setData(s, new byte[0], newRoot, delayedListener);
+ return new MockCuratorTransactionBridge();
+ }
+
+ }
+
+ private class MockCuratorTransactionBridge implements CuratorTransactionBridge {
+
+ @Override
+ public CuratorTransactionFinal and() {
+ return MockCuratorTransactionFinal.this;
+ }
+
+ }
+
+ /** A class which collects listen events and forwards them to the regular directoryListeners on commit */
+ private class DelayedListener extends Listeners {
+
+ private final List<Pair<Path, PathChildrenCacheEvent>> events = new ArrayList<>();
+
+ @Override
+ public void notify(Path path, PathChildrenCacheEvent event) {
+ events.add(new Pair<>(path, event));
+ }
+
+ public void commit() {
+ for (Pair<Path, PathChildrenCacheEvent> event : events)
+ listeners.notify(event.getFirst(), event.getSecond());
+ }
+
+ }
+
+ }
+
+}
diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/package-info.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/package-info.java
index 07f0924cb31..777da5988eb 100644
--- a/zkfacade/src/main/java/com/yahoo/vespa/curator/package-info.java
+++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/package-info.java
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-@PublicApi
+@PublicApi // TODO: Revoke this on Vespa 8.
package com.yahoo.vespa.curator;
import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/zkfacade/src/test/java/com/yahoo/vespa/curator/ConnectionSpecTest.java b/zkfacade/src/test/java/com/yahoo/vespa/curator/ConnectionSpecTest.java
new file mode 100644
index 00000000000..a518d8df843
--- /dev/null
+++ b/zkfacade/src/test/java/com/yahoo/vespa/curator/ConnectionSpecTest.java
@@ -0,0 +1,74 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.curator;
+
+import com.yahoo.net.HostName;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mpolden
+ */
+public class ConnectionSpecTest {
+
+ @Test
+ public void create() {
+ HostName.setHostNameForTestingOnly("host2");
+ Config config = new Config(List.of(new Config.Server("host1", 10001),
+ new Config.Server("host2", 10002),
+ new Config.Server("host3", 10003)));
+
+ {
+ ConnectionSpec spec = ConnectionSpec.create(config.servers, Config.Server::hostname, Config.Server::port, false);
+ assertEquals("host1:10001,host2:10002,host3:10003", spec.local());
+ assertEquals("host1:10001,host2:10002,host3:10003", spec.ensemble());
+ assertEquals(3, spec.ensembleSize());
+ }
+
+ {
+ ConnectionSpec specLocalAffinity = ConnectionSpec.create(config.servers, Config.Server::hostname, Config.Server::port, true);
+ assertEquals("host2:10002", specLocalAffinity.local());
+ assertEquals("host1:10001,host2:10002,host3:10003", specLocalAffinity.ensemble());
+ assertEquals(3, specLocalAffinity.ensembleSize());
+ }
+
+ {
+ ConnectionSpec specFromString = ConnectionSpec.create("host1:10001", "host1:10001,host2:10002");
+ assertEquals("host1:10001", specFromString.local());
+ assertEquals("host1:10001,host2:10002", specFromString.ensemble());
+ assertEquals(2, specFromString.ensembleSize());
+ }
+ }
+
+ private static class Config {
+
+ private final List<Server> servers;
+
+ public Config(List<Server> servers) {
+ this.servers = servers;
+ }
+
+ private static class Server {
+
+ private final String hostname;
+ private final int port;
+
+ public Server(String hostname, int port) {
+ this.hostname = hostname;
+ this.port = port;
+ }
+
+ public String hostname() {
+ return hostname;
+ }
+
+ public int port() {
+ return port;
+ }
+ }
+
+ }
+
+}
diff --git a/zkfacade/src/test/java/com/yahoo/vespa/curator/CuratorTest.java b/zkfacade/src/test/java/com/yahoo/vespa/curator/CuratorTest.java
index 2bf40c4e2bb..5341efaefe5 100644
--- a/zkfacade/src/test/java/com/yahoo/vespa/curator/CuratorTest.java
+++ b/zkfacade/src/test/java/com/yahoo/vespa/curator/CuratorTest.java
@@ -1,7 +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.vespa.curator;
-import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.cloud.config.CuratorConfig;
import com.yahoo.net.HostName;
import org.apache.curator.test.TestingServer;
import org.junit.After;
@@ -61,45 +61,33 @@ public class CuratorTest {
@Test
public void require_that_server_count_is_correct() {
- ConfigserverConfig.Builder builder = new ConfigserverConfig.Builder();
- builder.zookeeperserver(createZKBuilder(localhost, port1));
- try (Curator curator = createCurator(new ConfigserverConfig(builder))) {
+ CuratorConfig.Builder builder = new CuratorConfig.Builder();
+ builder.server(createZKBuilder(localhost, port1));
+ try (Curator curator = createCurator(new CuratorConfig(builder))) {
assertEquals(1, curator.zooKeeperEnsembleCount());
}
}
- @Test
- public void localhost_affinity() {
- String localhostHostName = "myhost";
- int localhostPort = 123;
-
- ConfigserverConfig.Builder builder = new ConfigserverConfig.Builder();
- builder.zookeeperserver(createZKBuilder(localhostHostName, localhostPort));
- builder.zookeeperserver(createZKBuilder("otherhost", 345));
- ConfigserverConfig config = new ConfigserverConfig(builder);
-
- HostName.setHostNameForTestingOnly(localhostHostName);
-
- String localhostSpec = localhostHostName + ":" + localhostPort;
- assertEquals(localhostSpec, Curator.createConnectionSpecForLocalhost(config));
- }
-
- private ConfigserverConfig createTestConfig() {
- ConfigserverConfig.Builder builder = new ConfigserverConfig.Builder();
- builder.zookeeperserver(createZKBuilder(localhost, port1));
- builder.zookeeperserver(createZKBuilder(localhost, port2));
- return new ConfigserverConfig(builder);
+ private CuratorConfig createTestConfig() {
+ CuratorConfig.Builder builder = new CuratorConfig.Builder();
+ builder.server(createZKBuilder(localhost, port1));
+ builder.server(createZKBuilder(localhost, port2));
+ return new CuratorConfig(builder);
}
- private ConfigserverConfig.Zookeeperserver.Builder createZKBuilder(String hostname, int port) {
- ConfigserverConfig.Zookeeperserver.Builder zkBuilder = new ConfigserverConfig.Zookeeperserver.Builder();
+ private CuratorConfig.Server.Builder createZKBuilder(String hostname, int port) {
+ CuratorConfig.Server.Builder zkBuilder = new CuratorConfig.Server.Builder();
zkBuilder.hostname(hostname);
zkBuilder.port(port);
return zkBuilder;
}
- private Curator createCurator(ConfigserverConfig configserverConfig) {
- return new Curator(configserverConfig, Optional.empty());
+ private Curator createCurator(CuratorConfig curatorConfig) {
+ return new Curator(ConnectionSpec.create(curatorConfig.server(),
+ CuratorConfig.Server::hostname,
+ CuratorConfig.Server::port,
+ curatorConfig.zookeeperLocalhostAffinity()),
+ Optional.empty());
}
private static class PortAllocator {
diff --git a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java b/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java
index ee3695b02f8..3c42d881ecc 100644
--- a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java
+++ b/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java
@@ -1,55 +1,28 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.zookeeper;
import com.google.inject.Inject;
import com.yahoo.cloud.config.ZookeeperServerConfig;
import com.yahoo.component.AbstractComponent;
-import com.yahoo.security.tls.TransportSecurityUtils;
-
-import java.util.logging.Level;
-
-import static com.yahoo.vespa.defaults.Defaults.getDefaults;
-import static com.yahoo.vespa.zookeeper.Configurator.zookeeperServerHostnames;
/**
- * Writes zookeeper config and starts zookeeper server.
+ * Main component controlling startup and stop of zookeeper server
*
* @author Ulf Lilleengen
* @author Harald Musum
*/
-public class VespaZooKeeperServerImpl extends AbstractComponent implements Runnable, VespaZooKeeperServer {
- private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(VespaZooKeeperServerImpl.class.getName());
- private final Thread zkServerThread;
- private final ZookeeperServerConfig zookeeperServerConfig;
+public class VespaZooKeeperServerImpl extends AbstractComponent implements VespaZooKeeperServer {
+
+ private final ZooKeeperRunner zooKeeperRunner;
@Inject
public VespaZooKeeperServerImpl(ZookeeperServerConfig zookeeperServerConfig) {
- this.zookeeperServerConfig = zookeeperServerConfig;
- new Configurator(zookeeperServerConfig).writeConfigToDisk(TransportSecurityUtils.getOptions());
- zkServerThread = new Thread(this, "zookeeper server");
- zkServerThread.start();
- }
-
- private void shutdown() {
- zkServerThread.interrupt();
- try {
- zkServerThread.join();
- } catch (InterruptedException e) {
- log.log(Level.WARNING, "Error joining server thread on shutdown", e);
- }
- }
-
- @Override
- public void run() {
- String[] args = new String[]{getDefaults().underVespaHome(zookeeperServerConfig.zooKeeperConfigFile())};
- log.log(Level.INFO, "Starting ZooKeeper server with config file " + args[0] +
- ". Trying to establish ZooKeeper quorum (members: " + zookeeperServerHostnames(zookeeperServerConfig) + ")");
- org.apache.zookeeper.server.quorum.QuorumPeerMain.main(args);
+ this.zooKeeperRunner = new ZooKeeperRunner(zookeeperServerConfig);
}
@Override
public void deconstruct() {
- shutdown();
+ zooKeeperRunner.shutdown();
super.deconstruct();
}
diff --git a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java b/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java
new file mode 100644
index 00000000000..492417cef96
--- /dev/null
+++ b/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ZooKeeperRunner.java
@@ -0,0 +1,49 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.zookeeper;
+
+import com.yahoo.cloud.config.ZookeeperServerConfig;
+import com.yahoo.security.tls.TransportSecurityUtils;
+
+import java.util.logging.Level;
+
+import static com.yahoo.vespa.defaults.Defaults.getDefaults;
+import static com.yahoo.vespa.zookeeper.Configurator.zookeeperServerHostnames;
+
+/**
+ * Writes zookeeper config and starts zookeeper server.
+ *
+ * @author Harald Musum
+ */
+public class ZooKeeperRunner implements Runnable {
+ private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(ZooKeeperRunner.class.getName());
+ private final Thread zkServerThread;
+ private final ZookeeperServerConfig zookeeperServerConfig;
+
+ public ZooKeeperRunner(ZookeeperServerConfig zookeeperServerConfig) {
+ this.zookeeperServerConfig = zookeeperServerConfig;
+ new Configurator(zookeeperServerConfig).writeConfigToDisk(TransportSecurityUtils.getOptions());
+ zkServerThread = new Thread(this, "zookeeper server");
+ zkServerThread.start();
+ }
+
+ void shutdown() {
+ zkServerThread.interrupt();
+ try {
+ zkServerThread.join();
+ } catch (InterruptedException e) {
+ log.log(Level.WARNING, "Error joining server thread on shutdown", e);
+ }
+ }
+
+ @Override
+ public void run() {
+ String[] args = new String[]{getDefaults().underVespaHome(zookeeperServerConfig.zooKeeperConfigFile())};
+ log.log(Level.INFO, "Starting ZooKeeper server with config file " + args[0] +
+ ". Trying to establish ZooKeeper quorum (members: " + zookeeperServerHostnames(zookeeperServerConfig) + ")");
+ org.apache.zookeeper.server.quorum.QuorumPeerMain.main(args);
+ }
+
+ public ZookeeperServerConfig zookeeperServerConfig() {
+ return zookeeperServerConfig;
+ }
+}