summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOla Aunrønning <olaa@verizonmedia.com>2021-02-03 11:08:25 +0100
committerGitHub <noreply@github.com>2021-02-03 11:08:25 +0100
commit4b45a7684bdcbb1c1c044755c5d81a9a3b3e6326 (patch)
tree25eb84905bee80a47e06a231342041e296d21c52
parent960022afa54672c004f37539452b6f68301a31a4 (diff)
parent986dc62765dc2264ab07c2f4744d86d48e707193 (diff)
Merge branch 'master' into olaa/create-cloud-role
-rw-r--r--README.md4
-rw-r--r--bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java1
-rw-r--r--bundle-plugin-test/test-bundles/main/pom.xml5
-rw-r--r--bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java8
-rw-r--r--cloud-tenant-base-dependencies-enforcer/pom.xml2
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundle.java36
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java7
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/NodeResourceExhaustion.java10
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculator.java53
-rw-r--r--clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/hostinfo/ResourceUsage.java18
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFeedBlockTest.java48
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFixture.java6
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundleTest.java34
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FeedBlockUtil.java58
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculatorTest.java45
-rw-r--r--clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/hostinfo/HostInfoTest.java1
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java6
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java10
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java8
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java21
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java51
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java103
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java33
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java12
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java8
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/FileStorProducer.java8
-rw-r--r--config-model/src/main/python/ES_Vespa_parser.py2
-rw-r--r--config-model/src/main/resources/schema/content.rnc3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java6
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java7
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java101
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java43
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java46
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java13
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java45
-rw-r--r--config/src/tests/configfetcher/configfetcher.cpp1
-rw-r--r--config/src/tests/configholder/configholder.cpp1
-rw-r--r--config/src/tests/configretriever/configretriever.cpp7
-rw-r--r--config/src/tests/frt/frt.cpp6
-rw-r--r--config/src/tests/subscriber/subscriber.cpp7
-rw-r--r--config/src/tests/subscription/subscription.cpp1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java1
-rw-r--r--container-core/pom.xml1
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java23
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java25
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java31
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java87
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java15
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java6
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java73
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java141
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java50
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java106
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/package-info.java12
-rw-r--r--container-core/src/main/java/org/json/package-info.java2
-rw-r--r--container-core/src/main/resources/configdefinitions/metrics.metrics-presentation.def3
-rw-r--r--container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java49
-rw-r--r--container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java27
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java13
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java21
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java21
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java61
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/MockSnapshotProvider.java23
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java338
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java109
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java77
-rw-r--r--container-dependency-versions/pom.xml2
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java3
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java17
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java6
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java24
-rw-r--r--container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java81
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java6
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java10
-rw-r--r--container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java198
-rw-r--r--container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java82
-rw-r--r--container-search/pom.xml5
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java4
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java10
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java23
-rw-r--r--container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java189
-rw-r--r--container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java42
-rw-r--r--container-search/src/test/java/com/yahoo/select/SelectTestCase.java196
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java23
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java93
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java30
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java21
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-job-accepted-2.json11
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json45
-rw-r--r--dist/vespa.spec1
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java197
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java42
-rw-r--r--document/abi-spec.json4
-rw-r--r--document/src/main/java/com/yahoo/document/datatypes/TensorFieldValue.java55
-rw-r--r--document/src/main/java/com/yahoo/document/json/SingleDocumentParser.java53
-rw-r--r--document/src/main/java/com/yahoo/document/serialization/VespaDocumentDeserializer6.java11
-rw-r--r--document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java8
-rw-r--r--document/src/tests/documentselectparsertest.cpp4
-rw-r--r--document/src/vespa/document/bucket/bucket.h2
-rw-r--r--document/src/vespa/document/bucket/bucketid.cpp8
-rw-r--r--document/src/vespa/document/bucket/bucketid.h42
-rw-r--r--documentapi/src/tests/policies/policies_test.cpp2
-rw-r--r--documentapi/src/vespa/documentapi/messagebus/policies/CMakeLists.txt2
-rw-r--r--eval/CMakeLists.txt2
-rw-r--r--eval/src/tests/eval/compile_cache/compile_cache_test.cpp2
-rw-r--r--eval/src/tests/eval/fast_value/fast_value_test.cpp40
-rw-r--r--eval/src/tests/eval/gen_spec/CMakeLists.txt9
-rw-r--r--eval/src/tests/eval/gen_spec/gen_spec_test.cpp196
-rw-r--r--eval/src/tests/eval/reference_operations/reference_operations_test.cpp2
-rw-r--r--eval/src/tests/eval/simple_value/simple_value_test.cpp115
-rw-r--r--eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp20
-rw-r--r--eval/src/tests/eval/value_codec/value_codec_test.cpp54
-rw-r--r--eval/src/tests/instruction/add_trivial_dimension_optimizer/add_trivial_dimension_optimizer_test.cpp12
-rw-r--r--eval/src/tests/instruction/dense_replace_type_function/dense_replace_type_function_test.cpp4
-rw-r--r--eval/src/tests/instruction/fast_rename_optimizer/fast_rename_optimizer_test.cpp16
-rw-r--r--eval/src/tests/instruction/generic_concat/generic_concat_test.cpp111
-rw-r--r--eval/src/tests/instruction/generic_create/generic_create_test.cpp46
-rw-r--r--eval/src/tests/instruction/generic_join/generic_join_test.cpp74
-rw-r--r--eval/src/tests/instruction/generic_map/generic_map_test.cpp46
-rw-r--r--eval/src/tests/instruction/generic_merge/generic_merge_test.cpp54
-rw-r--r--eval/src/tests/instruction/generic_peek/generic_peek_test.cpp38
-rw-r--r--eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp61
-rw-r--r--eval/src/tests/instruction/generic_rename/generic_rename_test.cpp51
-rw-r--r--eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp28
-rw-r--r--eval/src/tests/instruction/mixed_inner_product_function/mixed_inner_product_function_test.cpp71
-rw-r--r--eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp18
-rw-r--r--eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp44
-rw-r--r--eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp20
-rw-r--r--eval/src/tests/instruction/remove_trivial_dimension_optimizer/remove_trivial_dimension_optimizer_test.cpp10
-rw-r--r--eval/src/tests/instruction/sparse_dot_product_function/CMakeLists.txt9
-rw-r--r--eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp85
-rw-r--r--eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp51
-rw-r--r--eval/src/tests/streamed/value/streamed_value_test.cpp115
-rw-r--r--eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp229
-rw-r--r--eval/src/vespa/eval/eval/optimize_tensor_function.cpp8
-rw-r--r--eval/src/vespa/eval/eval/test/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/eval/test/eval_fixture.cpp22
-rw-r--r--eval/src/vespa/eval/eval/test/eval_fixture.h5
-rw-r--r--eval/src/vespa/eval/eval/test/gen_spec.cpp63
-rw-r--r--eval/src/vespa/eval/eval/test/gen_spec.h109
-rw-r--r--eval/src/vespa/eval/eval/test/param_variants.h23
-rw-r--r--eval/src/vespa/eval/instruction/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/instruction/generic_join.cpp11
-rw-r--r--eval/src/vespa/eval/instruction/generic_join.h10
-rw-r--r--eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp111
-rw-r--r--eval/src/vespa/eval/instruction/sparse_dot_product_function.h23
-rw-r--r--fastos/src/vespa/fastos/ringbuffer.h17
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java89
-rw-r--r--fnet/src/tests/connect/connect_test.cpp3
-rw-r--r--fnet/src/tests/frt/rpc/detach_return_invoke.cpp1
-rw-r--r--fnet/src/tests/sync_execute/sync_execute.cpp1
-rw-r--r--jrt/src/com/yahoo/jrt/CryptoEngine.java6
-rw-r--r--messagebus/src/tests/context/context.cpp1
-rw-r--r--messagebus/src/tests/messagebus/messagebus.cpp1
-rw-r--r--messagebus/src/tests/serviceaddress/serviceaddress.cpp1
-rw-r--r--messagebus/src/tests/slobrok/slobrok.cpp2
-rw-r--r--messagebus/src/tests/sourcesession/sourcesession.cpp1
-rw-r--r--messagebus/src/tests/throttling/throttling.cpp1
-rw-r--r--messagebus_test/src/tests/trace/trace.cpp1
-rw-r--r--metrics-proxy/pom.xml5
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java36
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java16
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java55
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java25
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java18
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java17
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java19
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java79
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java22
-rw-r--r--persistence/src/vespa/persistence/spi/attribute_resource_usage.h1
-rw-r--r--persistence/src/vespa/persistence/spi/bucket.h8
-rw-r--r--protocols/getnodestate/host_info.json3
-rw-r--r--searchcommon/src/vespa/searchcommon/common/CMakeLists.txt2
-rw-r--r--searchcommon/src/vespa/searchcommon/common/compaction_strategy.cpp15
-rw-r--r--searchcommon/src/vespa/searchcommon/common/compaction_strategy.h3
-rw-r--r--searchcommon/src/vespa/searchcommon/common/growstrategy.cpp18
-rw-r--r--searchcommon/src/vespa/searchcommon/common/growstrategy.h7
-rw-r--r--searchcore/CMakeLists.txt1
-rw-r--r--searchcore/src/apps/tests/persistenceconformance_test.cpp2
-rw-r--r--searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp2
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp8
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_test.cpp2
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_usage_filter/attribute_usage_filter_test.cpp47
-rw-r--r--searchcore/src/tests/proton/attribute/attributeflush_test.cpp1
-rw-r--r--searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp2
-rw-r--r--searchcore/src/tests/proton/common/alloc_config/CMakeLists.txt9
-rw-r--r--searchcore/src/tests/proton/common/alloc_config/alloc_config_test.cpp35
-rw-r--r--searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp7
-rw-r--r--searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp3
-rw-r--r--searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp25
-rw-r--r--searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp40
-rw-r--r--searchcore/src/tests/proton/documentdb/job_tracked_maintenance_job/job_tracked_maintenance_job_test.cpp8
-rw-r--r--searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp45
-rw-r--r--searchcore/src/tests/proton/flushengine/flushengine_test.cpp134
-rw-r--r--searchcore/src/tests/proton/matching/docid_range_scheduler/docid_range_scheduler_test.cpp3
-rw-r--r--searchcore/src/tests/proton/metrics/documentdb_job_trackers/documentdb_job_trackers_test.cpp1
-rw-r--r--searchcore/src/tests/proton/metrics/job_tracked_flush/job_tracked_flush_test.cpp24
-rw-r--r--searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp30
-rw-r--r--searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp2
-rw-r--r--searchcore/src/tests/proton/reference/gid_to_lid_change_listener/gid_to_lid_change_listener_test.cpp1
-rw-r--r--searchcore/src/vespa/searchcore/config/proton.def6
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.cpp10
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.h12
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.cpp11
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.h8
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_initializer.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.cpp9
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.h8
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.cpp14
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.h6
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.h15
-rw-r--r--searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp6
-rw-r--r--searchcore/src/vespa/searchcore/proton/bucketdb/CMakeLists.txt1
-rw-r--r--searchcore/src/vespa/searchcore/proton/bucketdb/bucketdb.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.cpp29
-rw-r--r--searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.h49
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt2
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/alloc_config.cpp56
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/alloc_config.h34
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/alloc_strategy.cpp41
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/alloc_strategy.h41
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/eventlogger.cpp33
-rw-r--r--searchcore/src/vespa/searchcore/proton/common/eventlogger.h13
-rw-r--r--searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreflushtarget.cpp18
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreinitializer.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.h4
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/cachedflushtarget.h22
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushcontext.cpp17
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp25
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h6
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushtargetproxy.h39
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushtask.cpp7
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/flushtask.h12
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/iflushstrategy.h14
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.h6
-rw-r--r--searchcore/src/vespa/searchcore/proton/reprocessing/reprocess_documents_task.cpp7
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.h8
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp55
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/bucketmovejob.h85
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp13
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentbucketmover.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.cpp45
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdb.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp17
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h7
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp21
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp15
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.h15
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp15
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/i_move_operation_limiter.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/ibucketmodifiedhandler.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/imaintenancejobrunner.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h3
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/move_operation_limiter.h2
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/proton.cpp9
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp6
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp34
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.h16
-rw-r--r--searchcore/src/vespa/searchcore/proton/server/transactionlogmanager.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp4
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h1
-rw-r--r--searchcore/src/vespa/searchcore/proton/test/simple_job_tracker.h2
-rw-r--r--searchlib/src/tests/attribute/compaction/attribute_compaction_test.cpp20
-rw-r--r--searchlib/src/tests/transactionlog/translogclient_test.cpp1
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributesaver.h5
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributevector.cpp26
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attributevector.h3
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java4
-rw-r--r--slobrok/src/tests/configure/configure.cpp1
-rw-r--r--slobrok/src/tests/mirrorapi/mirrorapi.cpp1
-rw-r--r--slobrok/src/tests/registerapi/registerapi.cpp1
-rw-r--r--slobrok/src/tests/standalone/standalone.cpp1
-rw-r--r--staging_vespalib/src/tests/clock/clock_test.cpp1
-rw-r--r--staging_vespalib/src/tests/shutdownguard/shutdownguard_test.cpp1
-rw-r--r--staging_vespalib/src/tests/timer/timer_test.cpp12
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/adaptive_sequenced_executor.h1
-rw-r--r--storage/src/tests/persistence/filestorage/service_layer_host_info_reporter_test.cpp22
-rw-r--r--storage/src/vespa/storage/bucketdb/bucketcopy.h44
-rw-r--r--storage/src/vespa/storage/bucketdb/bucketmanager.cpp8
-rw-r--r--storage/src/vespa/storage/distributor/pending_bucket_space_db_transition_entry.h2
-rw-r--r--storage/src/vespa/storage/distributor/pendingmessagetracker.h12
-rw-r--r--storage/src/vespa/storage/persistence/asynchandler.cpp2
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp14
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestormanager.h1
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp42
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.h2
-rw-r--r--storage/src/vespa/storage/persistence/messages.cpp12
-rw-r--r--storage/src/vespa/storage/persistence/messages.h5
-rw-r--r--storageapi/src/vespa/storageapi/buckets/bucketinfo.cpp5
-rw-r--r--storageapi/src/vespa/storageapi/buckets/bucketinfo.h40
-rw-r--r--vbench/src/apps/vbench/vbench.cpp4
-rw-r--r--vbench/src/tests/time_queue/time_queue_test.cpp6
-rw-r--r--vespalib/src/tests/barrier/barrier_test.cpp12
-rw-r--r--vespalib/src/tests/benchmark_timer/benchmark_timer_test.cpp1
-rw-r--r--vespalib/src/tests/executor/blockingthreadstackexecutor_test.cpp14
-rw-r--r--vespalib/src/tests/executor/stress_test.cpp1
-rw-r--r--vespalib/src/tests/executor/threadstackexecutor_test.cpp4
-rw-r--r--vespalib/src/tests/latch/latch_test.cpp9
-rw-r--r--vespalib/src/tests/net/async_resolver/async_resolver_test.cpp1
-rw-r--r--vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp1
-rw-r--r--vespalib/src/tests/net/selector/selector_test.cpp1
-rw-r--r--vespalib/src/tests/net/send_fd/send_fd_test.cpp2
-rw-r--r--vespalib/src/tests/net/socket/socket_test.cpp2
-rw-r--r--vespalib/src/tests/net/sync_crypto_socket/sync_crypto_socket_test.cpp1
-rw-r--r--vespalib/src/tests/net/tls/auto_reloading_tls_crypto_engine/auto_reloading_tls_crypto_engine_test.cpp4
-rw-r--r--vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp9
-rw-r--r--vespalib/src/tests/portal/portal_test.cpp11
-rw-r--r--vespalib/src/tests/portal/reactor/reactor_test.cpp9
-rw-r--r--vespalib/src/tests/rendezvous/rendezvous_test.cpp6
-rw-r--r--vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp5
-rw-r--r--vespalib/src/tests/simple_thread_bundle/threading_speed_test.cpp1
-rw-r--r--vespalib/src/tests/sync/sync_test.cpp8
-rw-r--r--vespalib/src/tests/testkit-time_bomb/testkit-time_bomb_test.cpp1
-rw-r--r--vespalib/src/tests/thread/thread_test.cpp1
-rw-r--r--vespalib/src/tests/time/time_box_test.cpp1
-rw-r--r--vespalib/src/tests/websocket/websocket_test.cpp5
-rw-r--r--vespalib/src/vespa/vespalib/gtest/gtest.h13
-rw-r--r--vespalib/src/vespa/vespalib/testkit/test_kit.h1
-rw-r--r--vespalib/src/vespa/vespalib/testkit/time_bomb.cpp4
-rw-r--r--vespalib/src/vespa/vespalib/util/address_space.cpp2
-rw-r--r--vespalib/src/vespa/vespalib/util/count_down_latch.h7
-rw-r--r--vespalib/src/vespa/vespalib/util/executor.h4
-rw-r--r--vespalib/src/vespa/vespalib/util/growstrategy.h2
-rw-r--r--vespamalloc/src/tests/allocfree/allocfree.cpp1
-rw-r--r--vespamalloc/src/tests/allocfree/linklist.cpp2
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/chain/Chain.java2
-rw-r--r--zookeeper-server/CMakeLists.txt1
-rw-r--r--zookeeper-server/pom.xml1
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/CMakeLists.txt2
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/pom.xml78
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java39
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java50
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java47
-rw-r--r--zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java46
366 files changed, 4666 insertions, 3874 deletions
diff --git a/README.md b/README.md
index a2b64f20fc3..3056f619982 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-<!-- 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. -->
-[![#Vespa](https://vespa.ai/img/VespaLogoBlack.png)](https://vespa.ai)
+[![#Vespa](https://vespa.ai/assets/vespa-logo-color.png)](https://vespa.ai)
The big data serving engine - Store, search, rank and organize big data at user serving time.
Vespa is an engine for low-latency computation over large data sets.
diff --git a/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java b/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java
index a46abce1dff..35a95ed3d89 100644
--- a/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java
+++ b/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java
@@ -91,7 +91,6 @@ public class BundleTest {
// From SimpleSearcher
assertThat(importPackage, containsString("com.yahoo.prelude.hitfield"));
- assertThat(importPackage, containsString("org.json"));
// From SimpleSearcher2
assertThat(importPackage, containsString("com.yahoo.processing"));
diff --git a/bundle-plugin-test/test-bundles/main/pom.xml b/bundle-plugin-test/test-bundles/main/pom.xml
index c9c9ea270eb..190e1c9d90f 100644
--- a/bundle-plugin-test/test-bundles/main/pom.xml
+++ b/bundle-plugin-test/test-bundles/main/pom.xml
@@ -16,11 +16,6 @@
<packaging>container-plugin</packaging>
<dependencies>
<dependency>
- <groupId>org.json</groupId>
- <artifactId>json</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>jrt</artifactId>
<version>${project.version}</version>
diff --git a/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java b/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java
index dddca3f4d59..ae9644aa010 100644
--- a/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java
+++ b/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java
@@ -8,8 +8,6 @@ import com.yahoo.search.Searcher;
import com.yahoo.search.result.Hit;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.text.BooleanParser;
-import org.json.JSONException;
-import org.json.JSONObject;
/**
* A searcher adding a new hit.
@@ -19,19 +17,13 @@ import org.json.JSONObject;
public class SimpleSearcher extends Searcher {
public Result search(Query query,Execution execution) {
- try {
BooleanParser.parseBoolean("true");
XMLString xmlString = new XMLString("<sampleXmlString/>");
Hit hit = new Hit("Hello world!");
- hit.setField("json", new JSONObject().put("price", 42).toString());
Result result = execution.search(query);
result.hits().add(hit);
return result;
-
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
}
}
diff --git a/cloud-tenant-base-dependencies-enforcer/pom.xml b/cloud-tenant-base-dependencies-enforcer/pom.xml
index a8655a82860..8bb7b75be89 100644
--- a/cloud-tenant-base-dependencies-enforcer/pom.xml
+++ b/cloud-tenant-base-dependencies-enforcer/pom.xml
@@ -34,7 +34,7 @@
<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>
- <org.json.version>20090211</org.json.version>
+ <org.json.version>20090211</org.json.version><!-- TODO Vespa 8: remove as provided dependency -->
<slf4j.version>1.7.5</slf4j.version>
<tensorflow.version>1.12.0</tensorflow.version>
<xml-apis.version>1.4.01</xml-apis.version>
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundle.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundle.java
index 0ca4f5632a8..c4e61b1d3d0 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundle.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundle.java
@@ -44,16 +44,30 @@ public class ClusterStateBundle {
public static class FeedBlock {
private final boolean blockFeedInCluster;
private final String description;
+ private final Set<NodeResourceExhaustion> concreteExhaustions;
public FeedBlock(boolean blockFeedInCluster, String description) {
this.blockFeedInCluster = blockFeedInCluster;
this.description = description;
+ this.concreteExhaustions = Collections.emptySet();
+ }
+
+ public FeedBlock(boolean blockFeedInCluster, String description,
+ Set<NodeResourceExhaustion> concreteExhaustions)
+ {
+ this.blockFeedInCluster = blockFeedInCluster;
+ this.description = description;
+ this.concreteExhaustions = concreteExhaustions;
}
public static FeedBlock blockedWithDescription(String desc) {
return new FeedBlock(true, desc);
}
+ public static FeedBlock blockedWith(String description, Set<NodeResourceExhaustion> concreteExhaustions) {
+ return new FeedBlock(true, description, concreteExhaustions);
+ }
+
public boolean blockFeedInCluster() {
return blockFeedInCluster;
}
@@ -62,18 +76,31 @@ public class ClusterStateBundle {
return description;
}
+ public Set<NodeResourceExhaustion> getConcreteExhaustions() {
+ return concreteExhaustions;
+ }
+
+ public boolean similarTo(FeedBlock other) {
+ // We check everything _but_ the description, as that includes current usage
+ // as floating point and we don't care about reporting changes in that. We do
+ // however care about reporting changes to the actual set of exhaustions.
+ return (blockFeedInCluster == other.blockFeedInCluster &&
+ Objects.equals(concreteExhaustions, other.concreteExhaustions));
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FeedBlock feedBlock = (FeedBlock) o;
- return (blockFeedInCluster == feedBlock.blockFeedInCluster &&
- Objects.equals(description, feedBlock.description));
+ return blockFeedInCluster == feedBlock.blockFeedInCluster &&
+ Objects.equals(description, feedBlock.description) &&
+ Objects.equals(concreteExhaustions, feedBlock.concreteExhaustions);
}
@Override
public int hashCode() {
- return Objects.hash(blockFeedInCluster, description);
+ return Objects.hash(blockFeedInCluster, description, concreteExhaustions);
}
}
@@ -229,6 +256,9 @@ public class ClusterStateBundle {
if (clusterFeedIsBlocked() != other.clusterFeedIsBlocked()) {
return false;
}
+ if (clusterFeedIsBlocked() && !feedBlock.similarTo(other.feedBlock)) {
+ return false;
+ }
// FIXME we currently treat mismatching bucket space sets as unchanged to avoid breaking some tests
return derivedBucketSpaceStates.entrySet().stream()
.allMatch(entry -> other.derivedBucketSpaceStates.getOrDefault(entry.getKey(), entry.getValue())
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java
index 6dc4b1e8015..e238303b58b 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/FleetController.java
@@ -346,12 +346,11 @@ public class FleetController implements NodeStateOrHostInfoChangeHandler, NodeAd
return;
}
// TODO hysteresis to prevent oscillations!
- // TODO also ensure we trigger if CC options have changed
var calc = createResourceExhaustionCalculator();
// Important: nodeInfo contains the _current_ host info _prior_ to newHostInfo being applied.
- boolean previouslyExhausted = !calc.enumerateNodeResourceExhaustions(nodeInfo).isEmpty();
- boolean nowExhausted = !calc.resourceExhaustionsFromHostInfo(nodeInfo.getNode(), newHostInfo).isEmpty();
- if (previouslyExhausted != nowExhausted) {
+ var previouslyExhausted = calc.enumerateNodeResourceExhaustions(nodeInfo);
+ var nowExhausted = calc.resourceExhaustionsFromHostInfo(nodeInfo, newHostInfo);
+ if (!previouslyExhausted.equals(nowExhausted)) {
log.fine(() -> String.format("Triggering state recomputation due to change in cluster feed block: %s -> %s",
previouslyExhausted, nowExhausted));
stateChangeHandler.setStateChangedFlag();
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/NodeResourceExhaustion.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/NodeResourceExhaustion.java
index 609fea2b91e..79f04627073 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/NodeResourceExhaustion.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/NodeResourceExhaustion.java
@@ -15,13 +15,16 @@ public class NodeResourceExhaustion {
public final String resourceType;
public final ResourceUsage resourceUsage;
public final double limit;
+ public final String rpcAddress;
public NodeResourceExhaustion(Node node, String resourceType,
- ResourceUsage resourceUsage, double limit) {
+ ResourceUsage resourceUsage, double limit,
+ String rpcAddress) {
this.node = node;
this.resourceType = resourceType;
this.resourceUsage = resourceUsage;
this.limit = limit;
+ this.rpcAddress = rpcAddress;
}
@Override
@@ -32,11 +35,12 @@ public class NodeResourceExhaustion {
return Double.compare(that.limit, limit) == 0 &&
Objects.equals(node, that.node) &&
Objects.equals(resourceType, that.resourceType) &&
- Objects.equals(resourceUsage, that.resourceUsage);
+ Objects.equals(resourceUsage, that.resourceUsage) &&
+ Objects.equals(rpcAddress, that.rpcAddress);
}
@Override
public int hashCode() {
- return Objects.hash(node, resourceType, resourceUsage, limit);
+ return Objects.hash(node, resourceType, resourceUsage, limit, rpcAddress);
}
}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculator.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculator.java
index 80b8a6110f1..e2e61eb8ed0 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculator.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculator.java
@@ -1,14 +1,17 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.core;
+import com.yahoo.jrt.Spec;
import com.yahoo.vdslib.state.Node;
import com.yahoo.vespa.clustercontroller.core.hostinfo.HostInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -37,43 +40,65 @@ public class ResourceExhaustionCalculator {
int maxDescriptions = 3;
String description = exhaustions.stream()
.limit(maxDescriptions)
- .map(n -> String.format("%s on node %s (%.3g > %.3g)",
- n.resourceType, n.node.getIndex(),
- n.resourceUsage.getUsage(), n.limit))
+ .map(ResourceExhaustionCalculator::formatNodeResourceExhaustion)
.collect(Collectors.joining(", "));
if (exhaustions.size() > maxDescriptions) {
description += String.format(" (... and %d more)", exhaustions.size() - maxDescriptions);
}
- return ClusterStateBundle.FeedBlock.blockedWithDescription(description);
+ // FIXME we currently will trigger a cluster state recomputation even if the number of
+ // exhaustions is greater than what is returned as part of the description. Though at
+ // that point, cluster state recomputations will be the least of your worries...!
+ return ClusterStateBundle.FeedBlock.blockedWith(description, exhaustions);
}
- public List<NodeResourceExhaustion> resourceExhaustionsFromHostInfo(Node node, HostInfo hostInfo) {
- List<NodeResourceExhaustion> exceedingLimit = null;
+ private static String formatNodeResourceExhaustion(NodeResourceExhaustion n) {
+ return String.format("%s%s on node %d [%s] (%.3g > %.3g)",
+ n.resourceType,
+ (n.resourceUsage.getName() != null ? ":" + n.resourceUsage.getName() : ""),
+ n.node.getIndex(),
+ inferHostnameFromRpcAddress(n.rpcAddress),
+ n.resourceUsage.getUsage(), n.limit);
+ }
+
+ private static String inferHostnameFromRpcAddress(String rpcAddress) {
+ if (rpcAddress == null) {
+ return "unknown hostname";
+ }
+ var spec = new Spec(rpcAddress);
+ if (spec.malformed()) {
+ return "unknown hostname";
+ }
+ return spec.host();
+ }
+
+ public Set<NodeResourceExhaustion> resourceExhaustionsFromHostInfo(NodeInfo nodeInfo, HostInfo hostInfo) {
+ Set<NodeResourceExhaustion> exceedingLimit = null;
for (var usage : hostInfo.getContentNode().getResourceUsage().entrySet()) {
double limit = feedBlockLimits.getOrDefault(usage.getKey(), 1.0);
if (usage.getValue().getUsage() > limit) {
if (exceedingLimit == null) {
- exceedingLimit = new ArrayList<>();
+ exceedingLimit = new LinkedHashSet<>();
}
- exceedingLimit.add(new NodeResourceExhaustion(node, usage.getKey(), usage.getValue(), limit));
+ exceedingLimit.add(new NodeResourceExhaustion(nodeInfo.getNode(), usage.getKey(), usage.getValue(),
+ limit, nodeInfo.getRpcAddress()));
}
}
- return (exceedingLimit != null) ? exceedingLimit : Collections.emptyList();
+ return (exceedingLimit != null) ? exceedingLimit : Collections.emptySet();
}
- public List<NodeResourceExhaustion> enumerateNodeResourceExhaustions(NodeInfo nodeInfo) {
+ public Set<NodeResourceExhaustion> enumerateNodeResourceExhaustions(NodeInfo nodeInfo) {
if (!nodeInfo.isStorage()) {
- return Collections.emptyList();
+ return Collections.emptySet();
}
- return resourceExhaustionsFromHostInfo(nodeInfo.getNode(), nodeInfo.getHostInfo());
+ return resourceExhaustionsFromHostInfo(nodeInfo, nodeInfo.getHostInfo());
}
// Returns 0-n entries per content node in the cluster, where n is the number of exhausted
// resource types on any given node.
- public List<NodeResourceExhaustion> enumerateNodeResourceExhaustionsAcrossAllNodes(Collection<NodeInfo> nodeInfos) {
+ public Set<NodeResourceExhaustion> enumerateNodeResourceExhaustionsAcrossAllNodes(Collection<NodeInfo> nodeInfos) {
return nodeInfos.stream()
.flatMap(info -> enumerateNodeResourceExhaustions(info).stream())
- .collect(Collectors.toList());
+ .collect(Collectors.toCollection(LinkedHashSet::new));
}
}
diff --git a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/hostinfo/ResourceUsage.java b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/hostinfo/ResourceUsage.java
index e47ec5452a4..da0862d7de9 100644
--- a/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/hostinfo/ResourceUsage.java
+++ b/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/hostinfo/ResourceUsage.java
@@ -8,12 +8,20 @@ import java.util.Objects;
/**
* Encapsulation of the usage levels for a particular resource type. The resource type
* itself is not tracked in this class; this must be done on a higher level.
+ *
+ * Note: equality checks and hash code computations do NOT include the actual floating
+ * point usage! This is so sets of ResourceUsages are de-duplicated at the resource level
+ * regardless of the relative usage (all cases where these are compared is assumed to
+ * be when feed is blocked anyway, so just varying levels over the feed block limit).
*/
public class ResourceUsage {
private final Double usage;
+ private final String name;
- public ResourceUsage(@JsonProperty("usage") Double usage) {
+ public ResourceUsage(@JsonProperty("usage") Double usage,
+ @JsonProperty("name") String name) {
this.usage = usage;
+ this.name = name;
}
/** Resource usage in [0, 1] */
@@ -21,16 +29,20 @@ public class ResourceUsage {
return usage;
}
+ public String getName() {
+ return name;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ResourceUsage that = (ResourceUsage) o;
- return Objects.equals(usage, that.usage);
+ return Objects.equals(name, that.name);
}
@Override
public int hashCode() {
- return Objects.hash(usage);
+ return Objects.hash(name);
}
}
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFeedBlockTest.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFeedBlockTest.java
index 2ac7113741b..8dd2a9ca55c 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFeedBlockTest.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFeedBlockTest.java
@@ -17,10 +17,13 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.mapOf;
+import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.setOf;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.usage;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.createResourceUsageJson;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -89,7 +92,7 @@ public class ClusterFeedBlockTest extends FleetControllerTest {
return options;
}
- private void reportResourceUsageFromNode(int nodeIndex, Map<String, Double> resourceUsages) throws Exception {
+ private void reportResourceUsageFromNode(int nodeIndex, Set<FeedBlockUtil.UsageDetails> resourceUsages) throws Exception {
String hostInfo = createResourceUsageJson(resourceUsages);
communicator.setNodeState(new Node(NodeType.STORAGE, nodeIndex), new NodeState(NodeType.STORAGE, State.UP), hostInfo);
ctrl.tick();
@@ -102,17 +105,17 @@ public class ClusterFeedBlockTest extends FleetControllerTest {
assertFalse(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
// Too much cheese in use, must block feed!
- reportResourceUsageFromNode(1, mapOf(usage("cheese", 0.8), usage("wine", 0.3)));
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.8), usage("wine", 0.3)));
assertTrue(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
// TODO check desc?
// Wine usage has gone up too, we should remain blocked
- reportResourceUsageFromNode(1, mapOf(usage("cheese", 0.8), usage("wine", 0.5)));
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.8), usage("wine", 0.5)));
assertTrue(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
// TODO check desc?
// Back to normal wine and cheese levels
- reportResourceUsageFromNode(1, mapOf(usage("cheese", 0.6), usage("wine", 0.3)));
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.6), usage("wine", 0.3)));
assertFalse(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
}
@@ -121,7 +124,7 @@ public class ClusterFeedBlockTest extends FleetControllerTest {
initialize(createOptions(mapOf(usage("cheese", 0.7), usage("wine", 0.4))));
assertFalse(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
- reportResourceUsageFromNode(1, mapOf(usage("cheese", 0.8), usage("wine", 0.3)));
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.8), usage("wine", 0.3)));
assertTrue(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
// Increase cheese allowance. Should now automatically unblock since reported usage is lower.
@@ -131,4 +134,39 @@ public class ClusterFeedBlockTest extends FleetControllerTest {
assertFalse(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
}
+ @Test
+ public void cluster_feed_block_state_is_recomputed_when_resource_block_set_differs() throws Exception {
+ initialize(createOptions(mapOf(usage("cheese", 0.7), usage("wine", 0.4))));
+ assertFalse(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
+
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.8), usage("wine", 0.3)));
+ var bundle = ctrl.getClusterStateBundle();
+ assertTrue(bundle.clusterFeedIsBlocked());
+ assertEquals("cheese on node 1 [unknown hostname] (0.800 > 0.700)", bundle.getFeedBlock().get().getDescription());
+
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.8), usage("wine", 0.5)));
+ bundle = ctrl.getClusterStateBundle();
+ assertTrue(bundle.clusterFeedIsBlocked());
+ assertEquals("cheese on node 1 [unknown hostname] (0.800 > 0.700), " +
+ "wine on node 1 [unknown hostname] (0.500 > 0.400)",
+ bundle.getFeedBlock().get().getDescription());
+ }
+
+ @Test
+ public void cluster_feed_block_state_is_not_recomputed_when_only_resource_usage_levels_differ() throws Exception {
+ initialize(createOptions(mapOf(usage("cheese", 0.7), usage("wine", 0.4))));
+ assertFalse(ctrl.getClusterStateBundle().clusterFeedIsBlocked());
+
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.8), usage("wine", 0.3)));
+ var bundle = ctrl.getClusterStateBundle();
+ assertTrue(bundle.clusterFeedIsBlocked());
+ assertEquals("cheese on node 1 [unknown hostname] (0.800 > 0.700)", bundle.getFeedBlock().get().getDescription());
+
+ // 80% -> 90%, should not trigger new state.
+ reportResourceUsageFromNode(1, setOf(usage("cheese", 0.9), usage("wine", 0.4)));
+ bundle = ctrl.getClusterStateBundle();
+ assertTrue(bundle.clusterFeedIsBlocked());
+ assertEquals("cheese on node 1 [unknown hostname] (0.800 > 0.700)", bundle.getFeedBlock().get().getDescription());
+ }
+
}
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFixture.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFixture.java
index a6cf10d4022..35cfd82f367 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFixture.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterFixture.java
@@ -150,7 +150,11 @@ public class ClusterFixture {
}
public ClusterFixture assignDummyRpcAddresses() {
- cluster.getNodeInfo().forEach(ni -> ni.setRpcAddress("tcp/localhost:0"));
+ cluster.getNodeInfo().forEach(ni -> {
+ ni.setRpcAddress(String.format("tcp/%s.%d.local:0",
+ ni.isStorage() ? "storage" : "distributor",
+ ni.getNodeIndex()));
+ });
return this;
}
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundleTest.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundleTest.java
index d2db47131bd..ceddf7cdcf3 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundleTest.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ClusterStateBundleTest.java
@@ -6,9 +6,14 @@ import com.yahoo.vdslib.state.Node;
import com.yahoo.vdslib.state.NodeState;
import com.yahoo.vdslib.state.NodeType;
import com.yahoo.vdslib.state.State;
+import com.yahoo.vespa.clustercontroller.core.hostinfo.ResourceUsage;
import org.junit.Test;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
import java.util.function.Function;
+import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -53,6 +58,12 @@ public class ClusterStateBundleTest {
.deriveAndBuild();
}
+ private static ClusterStateBundle createTestBundleWithFeedBlock(String description, Set<NodeResourceExhaustion> concreteExhaustions) {
+ return createTestBundleBuilder(false)
+ .feedBlock(ClusterStateBundle.FeedBlock.blockedWith(description, concreteExhaustions))
+ .deriveAndBuild();
+ }
+
private static ClusterStateBundle createTestBundle() {
return createTestBundle(true);
}
@@ -109,6 +120,29 @@ public class ClusterStateBundleTest {
assertTrue(blockingBundle.similarTo(blockingBundleWithOtherDesc));
}
+ static NodeResourceExhaustion createDummyExhaustion(String type) {
+ return new NodeResourceExhaustion(new Node(NodeType.STORAGE, 1), type, new ResourceUsage(0.8, null), 0.7, "foo");
+ }
+
+ static Set<NodeResourceExhaustion> exhaustionsOf(String... types) {
+ return Arrays.stream(types)
+ .map(t -> createDummyExhaustion(t))
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ }
+
+ @Test
+ public void similarity_test_considers_cluster_feed_block_concrete_exhaustion_set() {
+ var blockingBundleNoSet = createTestBundleWithFeedBlock("foo");
+ var blockingBundleWithSet = createTestBundleWithFeedBlock("bar", exhaustionsOf("beer", "wine"));
+ var blockingBundleWithOtherSet = createTestBundleWithFeedBlock("bar", exhaustionsOf("beer", "soda"));
+
+ assertTrue(blockingBundleNoSet.similarTo(blockingBundleNoSet));
+ assertTrue(blockingBundleWithSet.similarTo(blockingBundleWithSet));
+ assertFalse(blockingBundleWithSet.similarTo(blockingBundleWithOtherSet));
+ assertFalse(blockingBundleNoSet.similarTo(blockingBundleWithSet));
+ assertFalse(blockingBundleNoSet.similarTo(blockingBundleWithOtherSet));
+ }
+
@Test
public void feed_block_state_is_available() {
var nonBlockingBundle = createTestBundle(false);
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FeedBlockUtil.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FeedBlockUtil.java
index e2894705352..a26f8791cda 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FeedBlockUtil.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FeedBlockUtil.java
@@ -2,46 +2,78 @@
package com.yahoo.vespa.clustercontroller.core;
import java.util.Arrays;
+import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
public class FeedBlockUtil {
static class NodeAndUsages {
public final int index;
- public final Map<String, Double> usages;
+ public final Set<UsageDetails> usages;
- public NodeAndUsages(int index, Map<String, Double> usages) {
+ public NodeAndUsages(int index, Set<UsageDetails> usages) {
this.index = index;
this.usages = usages;
}
}
- static class NameAndUsage {
+ static class UsageDetails {
+ public final String type;
public final String name;
public final double usage;
- public NameAndUsage(String name, double usage) {
+ public UsageDetails(String type, String name, double usage) {
+ this.type = type;
this.name = name;
this.usage = usage;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ UsageDetails that = (UsageDetails) o;
+ return Double.compare(that.usage, usage) == 0 &&
+ Objects.equals(type, that.type) &&
+ Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, name, usage);
+ }
+ }
+
+ static UsageDetails usage(String type, double usage) {
+ return new UsageDetails(type, null, usage);
+ }
+
+ static UsageDetails usage(String type, String name, double usage) {
+ return new UsageDetails(type, name, usage);
}
- static NameAndUsage usage(String name, double usage) {
- return new NameAndUsage(name, usage);
+ static Map<String, Double> mapOf(UsageDetails... usages) {
+ return Arrays.stream(usages).collect(Collectors.toMap(u -> u.type, u -> u.usage));
}
- static Map<String, Double> mapOf(NameAndUsage... usages) {
- return Arrays.stream(usages).collect(Collectors.toMap(u -> u.name, u -> u.usage));
+ static Set<UsageDetails> setOf(UsageDetails... usages) {
+ // Preserve input order to make stringification tests deterministic
+ return Arrays.stream(usages).collect(Collectors.toCollection(LinkedHashSet::new));
}
- static NodeAndUsages forNode(int index, NameAndUsage... usages) {
- return new NodeAndUsages(index, mapOf(usages));
+ static NodeAndUsages forNode(int index, UsageDetails... usages) {
+ return new NodeAndUsages(index, setOf(usages));
}
- static String createResourceUsageJson(Map<String, Double> usages) {
- String usageInnerJson = usages.entrySet().stream()
- .map(kv -> String.format("\"%s\":{\"usage\": %.3g}", kv.getKey(), kv.getValue()))
+ static String createResourceUsageJson(Set<UsageDetails> usages) {
+ // We deal only in the finest of manual JSON string building technologies(tm).
+ String usageInnerJson = usages.stream()
+ .map(u -> String.format("\"%s\":{\"usage\": %.3g%s}",
+ u.type, u.usage,
+ (u.name != null ? String.format(",\"name\":\"%s\"", u.name) : "")))
.collect(Collectors.joining(","));
return String.format("{\"content-node\":{\"resource-usage\":{%s}}}", usageInnerJson);
}
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculatorTest.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculatorTest.java
index 5a5cda1f4ed..54686919a7b 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculatorTest.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/ResourceExhaustionCalculatorTest.java
@@ -10,6 +10,7 @@ import static com.yahoo.vespa.clustercontroller.core.ClusterFixture.storageNode;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.NodeAndUsages;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.forNode;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.mapOf;
+import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.setOf;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.usage;
import static com.yahoo.vespa.clustercontroller.core.FeedBlockUtil.createResourceUsageJson;
@@ -25,7 +26,10 @@ public class ResourceExhaustionCalculatorTest {
if (highestIndex.isEmpty()) {
throw new IllegalArgumentException("Can't have an empty cluster");
}
- var cf = ClusterFixture.forFlatCluster(highestIndex.getAsInt() + 1).bringEntireClusterUp();
+ var cf = ClusterFixture
+ .forFlatCluster(highestIndex.getAsInt() + 1)
+ .assignDummyRpcAddresses()
+ .bringEntireClusterUp();
for (var nu : nodeAndUsages) {
cf.cluster().getNodeInfo(storageNode(nu.index))
.setHostInfo(HostInfo.createHostInfo(createResourceUsageJson(nu.usages)));
@@ -50,7 +54,32 @@ public class ResourceExhaustionCalculatorTest {
var feedBlock = calc.inferContentClusterFeedBlockOrNull(cf.cluster().getNodeInfo());
assertNotNull(feedBlock);
assertTrue(feedBlock.blockFeedInCluster());
- assertEquals("disk on node 1 (0.510 > 0.500)", feedBlock.getDescription());
+ assertEquals("disk on node 1 [storage.1.local] (0.510 > 0.500)", feedBlock.getDescription());
+ }
+
+ @Test
+ public void feed_block_description_can_contain_optional_name_component() {
+ var calc = new ResourceExhaustionCalculator(true, mapOf(usage("disk", 0.5), usage("memory", 0.8)));
+ var cf = createFixtureWithReportedUsages(forNode(1, usage("disk", "a-fancy-disk", 0.51), usage("memory", 0.79)),
+ forNode(2, usage("disk", 0.4), usage("memory", 0.6)));
+ var feedBlock = calc.inferContentClusterFeedBlockOrNull(cf.cluster().getNodeInfo());
+ assertNotNull(feedBlock);
+ assertTrue(feedBlock.blockFeedInCluster());
+ assertEquals("disk:a-fancy-disk on node 1 [storage.1.local] (0.510 > 0.500)", feedBlock.getDescription());
+ }
+
+ @Test
+ public void missing_or_malformed_rpc_addresses_are_emitted_as_unknown_hostnames() {
+ var calc = new ResourceExhaustionCalculator(true, mapOf(usage("disk", 0.5), usage("memory", 0.8)));
+ var cf = createFixtureWithReportedUsages(forNode(1, usage("disk", 0.51), usage("memory", 0.79)),
+ forNode(2, usage("disk", 0.4), usage("memory", 0.85)));
+ cf.cluster().getNodeInfo(storageNode(1)).setRpcAddress(null);
+ cf.cluster().getNodeInfo(storageNode(2)).setRpcAddress("max mekker");
+ var feedBlock = calc.inferContentClusterFeedBlockOrNull(cf.cluster().getNodeInfo());
+ assertNotNull(feedBlock);
+ assertTrue(feedBlock.blockFeedInCluster());
+ assertEquals("disk on node 1 [unknown hostname] (0.510 > 0.500), " +
+ "memory on node 2 [unknown hostname] (0.850 > 0.800)", feedBlock.getDescription());
}
@Test
@@ -61,9 +90,9 @@ public class ResourceExhaustionCalculatorTest {
var feedBlock = calc.inferContentClusterFeedBlockOrNull(cf.cluster().getNodeInfo());
assertNotNull(feedBlock);
assertTrue(feedBlock.blockFeedInCluster());
- assertEquals("disk on node 1 (0.510 > 0.400), " +
- "memory on node 1 (0.850 > 0.800), " +
- "disk on node 2 (0.450 > 0.400)",
+ assertEquals("disk on node 1 [storage.1.local] (0.510 > 0.400), " +
+ "memory on node 1 [storage.1.local] (0.850 > 0.800), " +
+ "disk on node 2 [storage.2.local] (0.450 > 0.400)",
feedBlock.getDescription());
}
@@ -76,9 +105,9 @@ public class ResourceExhaustionCalculatorTest {
var feedBlock = calc.inferContentClusterFeedBlockOrNull(cf.cluster().getNodeInfo());
assertNotNull(feedBlock);
assertTrue(feedBlock.blockFeedInCluster());
- assertEquals("disk on node 1 (0.510 > 0.400), " +
- "memory on node 1 (0.850 > 0.800), " +
- "disk on node 2 (0.450 > 0.400) (... and 2 more)",
+ assertEquals("disk on node 1 [storage.1.local] (0.510 > 0.400), " +
+ "memory on node 1 [storage.1.local] (0.850 > 0.800), " +
+ "disk on node 2 [storage.2.local] (0.450 > 0.400) (... and 2 more)",
feedBlock.getDescription());
}
diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/hostinfo/HostInfoTest.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/hostinfo/HostInfoTest.java
index f9b0a4ca36f..b2d3eb54f78 100644
--- a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/hostinfo/HostInfoTest.java
+++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/hostinfo/HostInfoTest.java
@@ -75,6 +75,7 @@ public class HostInfoTest {
assertEquals(resourceUsage.size(), 2);
assertEquals(Optional.ofNullable(resourceUsage.get("memory")).map(ResourceUsage::getUsage).orElse(0.0), 0.85, 0.00001);
assertEquals(Optional.ofNullable(resourceUsage.get("disk")).map(ResourceUsage::getUsage).orElse(0.0), 0.6, 0.00001);
+ assertEquals(Optional.ofNullable(resourceUsage.get("disk")).map(ResourceUsage::getName).orElse("missing"), "a cool disk");
assertNull(resourceUsage.get("flux-capacitor"));
}
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
index a9a83386573..6517c3e57fc 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
@@ -64,8 +64,8 @@ public interface ModelContext {
* - Remove below method once all config-model versions in hosted production include changes from 1)
*/
interface FeatureFlags {
- @ModelFeatureFlag(owners = {"bjorncs", "jonmv"}) default boolean enableAutomaticReindexing() { return false; }
- @ModelFeatureFlag(owners = {"bjorncs", "jonmv"}) default double reindexerWindowSizeIncrement() { return 0.2; }
+ @ModelFeatureFlag(owners = {"bjorncs", "jonmv"}, removeAfter = "7.352") default boolean enableAutomaticReindexing() { return true; }
+ @ModelFeatureFlag(owners = {"jonmv"}) default double reindexerWindowSizeIncrement() { return 0.2; }
@ModelFeatureFlag(owners = {"baldersheim"}, comment = "Revisit in May or June 2021") default double defaultTermwiseLimit() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"vekterli"}) default boolean useThreePhaseUpdates() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}, comment = "Select sequencer type use while feeding") default String feedSequencerType() { throw new UnsupportedOperationException("TODO specify default value"); }
@@ -77,7 +77,7 @@ public interface ModelContext {
@ModelFeatureFlag(owners = {"tokle"}) default boolean useAccessControlTlsHandshakeClientAuth() { return false; }
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean useAsyncMessageHandlingOnSchedule() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}, removeAfter = "7.348") default int contentNodeBucketDBStripeBits() { return 4; }
- @ModelFeatureFlag(owners = {"baldersheim"}) default int mergeChunkSize() { throw new UnsupportedOperationException("TODO specify default value"); }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter = "7.350") default int mergeChunkSize() { return 0x2000000; }
@ModelFeatureFlag(owners = {"baldersheim"}) default double feedConcurrency() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean useBucketExecutorForLidSpaceCompact() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"musum", "mpolden"}, comment = "Revisit in February 2021") default boolean reconfigurableZookeeperServer() { return false; }
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
index 686cb7ce7c6..6ca1c8f2b79 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
@@ -47,7 +47,6 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
private Quota quota = Quota.unlimited();
private boolean useAccessControlTlsHandshakeClientAuth;
private boolean useAsyncMessageHandlingOnSchedule = false;
- private int mergeChunkSize = 0x400000 - 0x1000; // 4M -4k
private double feedConcurrency = 0.5;
private boolean enableAutomaticReindexing = false;
private boolean reconfigurableZookeeperServer = false;
@@ -82,7 +81,6 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
@Override public Quota quota() { return quota; }
@Override public boolean useAccessControlTlsHandshakeClientAuth() { return useAccessControlTlsHandshakeClientAuth; }
@Override public boolean useAsyncMessageHandlingOnSchedule() { return useAsyncMessageHandlingOnSchedule; }
- @Override public int mergeChunkSize() { return mergeChunkSize; }
@Override public double feedConcurrency() { return feedConcurrency; }
@Override public boolean enableAutomaticReindexing() { return enableAutomaticReindexing; }
@Override public boolean reconfigurableZookeeperServer() { return reconfigurableZookeeperServer; }
@@ -94,11 +92,6 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
return this;
}
- public TestProperties setMergeChunkSize(int size) {
- mergeChunkSize = size;
- return this;
- }
-
public TestProperties setAsyncMessageHandlingOnSchedule(boolean value) {
useAsyncMessageHandlingOnSchedule = value;
return this;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
index 3fe6ce3ff27..8423f7d723a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainerCluster.java
@@ -32,9 +32,7 @@ public class ClusterControllerContainerCluster extends ContainerCluster<ClusterC
public ReindexingContext reindexingContext() { return reindexingContext; }
private static ReindexingContext createReindexingContext(DeployState deployState) {
- Reindexing reindexing = deployState.featureFlags().enableAutomaticReindexing()
- ? deployState.reindexing().orElse(Reindexing.DISABLED_INSTANCE)
- : Reindexing.DISABLED_INSTANCE;
+ Reindexing reindexing = deployState.reindexing().orElse(Reindexing.DISABLED_INSTANCE);
return new ReindexingContext(reindexing, deployState.featureFlags().reindexerWindowSizeIncrement());
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java
index 0add9f243fe..a462cb4fdb3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidator.java
@@ -39,7 +39,7 @@ public class NodeResourceChangeValidator implements ChangeValidator {
}
private boolean changeRequiresRestart(NodeResources currentResources, NodeResources nextResources) {
- return currentResources.memoryGb() != nextResources.memoryGb();
+ return !currentResources.equals(nextResources);
}
private Optional<NodeResources> resourcesOf(ClusterSpec.Id clusterId, VespaModel model) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java
index 5074f1ecbbe..911e5e2e9bd 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerThreadpool.java
@@ -22,8 +22,6 @@ public class ContainerThreadpool extends SimpleComponent implements ContainerThr
private final String name;
private final UserOptions userOptions;
- public ContainerThreadpool(String name) { this(name, null); }
-
public ContainerThreadpool(String name, UserOptions userOptions) {
super(new ComponentModel(
BundleInstantiationSpecification.getFromStrings(
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java
index 2ca97b297b0..3dae3160c51 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/DefaultThreadpoolProvider.java
@@ -5,6 +5,7 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.handler.ThreadPoolProvider;
import com.yahoo.container.handler.ThreadpoolConfig;
import com.yahoo.osgi.provider.model.ComponentModel;
+import com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster;
import com.yahoo.vespa.model.container.component.SimpleComponent;
/**
@@ -25,11 +26,18 @@ class DefaultThreadpoolProvider extends SimpleComponent implements ThreadpoolCon
this.cluster = cluster;
}
+ private int defaultThreadsByClusterType() {
+ if (cluster instanceof MetricsProxyContainerCluster) {
+ return 4;
+ }
+ return 10;
+ }
+
@Override
public void getConfig(ThreadpoolConfig.Builder builder) {
if (!(cluster instanceof ApplicationContainerCluster)) {
// Container clusters such as logserver, metricsproxy and clustercontroller
- int defaultWorkerThreads = 10;
+ int defaultWorkerThreads = defaultThreadsByClusterType();
builder.maxthreads(defaultWorkerThreads);
builder.corePoolSize(defaultWorkerThreads);
builder.queueSize(50);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
index 6a858bd2e02..a66d98ad52c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java
@@ -82,6 +82,12 @@ public class AccessLogBuilder {
}
private static CompressionType compressionType(Element spec, DeployState deployState, boolean isHostedVespa) {
+ CompressionType fallback;
+ if (isHostedVespa && deployState.featureFlags().enableZstdCompressionAccessLog()) {
+ fallback = CompressionType.ZSTD;
+ } else {
+ fallback = CompressionType.GZIP;
+ }
return Optional.ofNullable(spec.getAttribute("compressionType"))
.filter(value -> !value.isBlank())
.map(value -> {
@@ -94,7 +100,7 @@ public class AccessLogBuilder {
throw new IllegalArgumentException("Unknown compression type: " + value);
}
})
- .orElse(isHostedVespa && deployState.featureFlags().enableZstdCompressionAccessLog() ? CompressionType.ZSTD : CompressionType.GZIP);
+ .orElse(fallback);
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java
index ab734c506c1..8be57ff3bde 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ConfigServerContainerModelBuilder.java
@@ -2,8 +2,12 @@
package com.yahoo.vespa.model.container.xml;
import com.yahoo.config.model.ConfigModelContext;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.container.logging.FileConnectionLog;
import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import com.yahoo.vespa.model.container.ContainerModel;
+import com.yahoo.vespa.model.container.component.AccessLogComponent;
+import com.yahoo.vespa.model.container.component.ConnectionLogComponent;
import com.yahoo.vespa.model.container.configserver.ConfigserverCluster;
import com.yahoo.vespa.model.container.configserver.option.CloudConfigOptions;
import org.w3c.dom.Element;
@@ -34,7 +38,22 @@ public class ConfigServerContainerModelBuilder extends ContainerModelBuilder {
// in ConfigModelContext.DeployState.properties are not set)
@Override
protected void addStatusHandlers(ApplicationContainerCluster cluster, boolean isHostedVespa) {
- super.addStatusHandlers(cluster, options.hostedVespa().orElse(Boolean.FALSE));
+ super.addStatusHandlers(cluster, isHosted());
}
+ // Override access log configuration for hosted configserver/controller
+ @Override
+ protected void addAccessLogs(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
+ if (isHosted()){
+ cluster.addComponent(
+ new AccessLogComponent(
+ AccessLogComponent.AccessLogType.jsonAccessLog, AccessLogComponent.CompressionType.ZSTD,
+ "logs/vespa/configserver/access-json.log.%Y%m%d%H%M%S", null, true, true, "access-json.log"));
+ cluster.addComponent(new ConnectionLogComponent(FileConnectionLog.class, cluster.getName()));
+ } else {
+ super.addAccessLogs(deployState, cluster, spec);
+ }
+ }
+
+ private boolean isHosted() { return options.hostedVespa().orElse(Boolean.FALSE); }
}
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 d650b10a910..7477ccdd970 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
@@ -338,7 +338,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
addConfiguredComponents(deployState, cluster, spec, "server");
}
- private void addAccessLogs(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
+ protected void addAccessLogs(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
List<Element> accessLogElements = getAccessLogElements(spec);
for (Element accessLog : accessLogElements) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java
index 8d6127970c8..66ec0d81947 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterControllerConfig.java
@@ -19,12 +19,14 @@ import org.w3c.dom.Element;
public class ClusterControllerConfig extends AbstractConfigProducer<ClusterControllerConfig> implements FleetcontrollerConfig.Producer {
public static class Builder extends VespaDomBuilder.DomConfigProducerBuilder<ClusterControllerConfig> {
- String clusterName;
- ModelElement clusterElement;
+ private final String clusterName;
+ private final ModelElement clusterElement;
+ private final ResourceLimits resourceLimits;
- public Builder(String clusterName, ModelElement clusterElement) {
+ public Builder(String clusterName, ModelElement clusterElement, ResourceLimits resourceLimits) {
this.clusterName = clusterName;
this.clusterElement = clusterElement;
+ this.resourceLimits = resourceLimits;
}
@Override
@@ -51,27 +53,29 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
tuning.childAsDouble("min-storage-up-ratio"),
bucketSplittingMinimumBits,
minNodeRatioPerGroup,
- enableClusterFeedBlock);
+ enableClusterFeedBlock,
+ resourceLimits);
} else {
return new ClusterControllerConfig(ancestor, clusterName,
null, null, null, null, null, null,
bucketSplittingMinimumBits,
minNodeRatioPerGroup,
- enableClusterFeedBlock);
+ enableClusterFeedBlock, resourceLimits);
}
}
}
- String clusterName;
- Duration initProgressTime;
- Duration transitionTime;
- Long maxPrematureCrashes;
- Duration stableStateTimePeriod;
- Double minDistributorUpRatio;
- Double minStorageUpRatio;
- Integer minSplitBits;
- private Double minNodeRatioPerGroup;
- private boolean enableClusterFeedBlock = false;
+ private final String clusterName;
+ private final Duration initProgressTime;
+ private final Duration transitionTime;
+ private final Long maxPrematureCrashes;
+ private final Duration stableStateTimePeriod;
+ private final Double minDistributorUpRatio;
+ private final Double minStorageUpRatio;
+ private final Integer minSplitBits;
+ private final Double minNodeRatioPerGroup;
+ private final boolean enableClusterFeedBlock;
+ private final ResourceLimits resourceLimits;
// TODO refactor; too many args
private ClusterControllerConfig(AbstractConfigProducer parent,
@@ -84,7 +88,8 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
Double minStorageUpRatio,
Integer minSplitBits,
Double minNodeRatioPerGroup,
- boolean enableClusterFeedBlock) {
+ boolean enableClusterFeedBlock,
+ ResourceLimits resourceLimits) {
super(parent, "fleetcontroller");
this.clusterName = clusterName;
@@ -97,6 +102,7 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
this.minSplitBits = minSplitBits;
this.minNodeRatioPerGroup = minNodeRatioPerGroup;
this.enableClusterFeedBlock = enableClusterFeedBlock;
+ this.resourceLimits = resourceLimits;
}
@Override
@@ -139,18 +145,7 @@ public class ClusterControllerConfig extends AbstractConfigProducer<ClusterContr
builder.min_node_ratio_per_group(minNodeRatioPerGroup);
}
builder.enable_cluster_feed_block(enableClusterFeedBlock);
- setDefaultClusterFeedBlockLimits(builder);
+ resourceLimits.getConfig(builder);
}
- private static void setDefaultClusterFeedBlockLimits(FleetcontrollerConfig.Builder builder) {
- // TODO: Override these based on resource-limits in services.xml (if they are specified).
- // TODO: Choose other defaults when this is default enabled.
- // Note: The resource categories must match the ones used in host info reporting
- // between content nodes and cluster controller:
- // storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp
- builder.cluster_feed_block_limit.put("memory", 0.79);
- builder.cluster_feed_block_limit.put("disk", 0.79);
- builder.cluster_feed_block_limit.put("attribute-enum-store", 0.89);
- builder.cluster_feed_block_limit.put("attribute-multi-value", 0.89);
- }
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java
new file mode 100644
index 00000000000..5324ee171ec
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ClusterResourceLimits.java
@@ -0,0 +1,103 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.content;
+
+import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
+import com.yahoo.vespa.model.content.cluster.DomResourceLimitsBuilder;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Class tracking the feed block resource limits for a content cluster.
+ *
+ * This includes the limits used by the cluster controller and the content nodes (proton).
+ *
+ * @author geirst
+ */
+public class ClusterResourceLimits {
+
+ private final ResourceLimits clusterControllerLimits;
+ private final ResourceLimits contentNodeLimits;
+
+ private ClusterResourceLimits(Builder builder) {
+ clusterControllerLimits = builder.ctrlBuilder.build();
+ contentNodeLimits = builder.nodeBuilder.build();
+ }
+
+ public ResourceLimits getClusterControllerLimits() {
+ return clusterControllerLimits;
+ }
+
+ public ResourceLimits getContentNodeLimits() {
+ return contentNodeLimits;
+ }
+
+ public static class Builder {
+
+ private ResourceLimits.Builder ctrlBuilder = new ResourceLimits.Builder();
+ private ResourceLimits.Builder nodeBuilder = new ResourceLimits.Builder();
+
+ public ClusterResourceLimits build(ModelElement clusterElem) {
+
+ ModelElement tuningElem = clusterElem.childByPath("tuning");
+ if (tuningElem != null) {
+ ctrlBuilder = DomResourceLimitsBuilder.createBuilder(tuningElem);
+ }
+
+ ModelElement protonElem = clusterElem.childByPath("engine.proton");
+ if (protonElem != null) {
+ nodeBuilder = DomResourceLimitsBuilder.createBuilder(protonElem);
+ }
+
+ deriveLimits();
+ return new ClusterResourceLimits(this);
+ }
+
+ public void setClusterControllerBuilder(ResourceLimits.Builder builder) {
+ ctrlBuilder = builder;
+ }
+
+ public void setContentNodeBuilder(ResourceLimits.Builder builder) {
+ nodeBuilder = builder;
+ }
+
+ public ClusterResourceLimits build() {
+ deriveLimits();
+ return new ClusterResourceLimits(this);
+ }
+
+ private void deriveLimits() {
+ deriveClusterControllerLimit(ctrlBuilder.getDiskLimit(), nodeBuilder.getDiskLimit(), ctrlBuilder::setDiskLimit);
+ deriveClusterControllerLimit(ctrlBuilder.getMemoryLimit(), nodeBuilder.getMemoryLimit(), ctrlBuilder::setMemoryLimit);
+
+ deriveContentNodeLimit(nodeBuilder.getDiskLimit(), ctrlBuilder.getDiskLimit(), nodeBuilder::setDiskLimit);
+ deriveContentNodeLimit(nodeBuilder.getMemoryLimit(), ctrlBuilder.getMemoryLimit(), nodeBuilder::setMemoryLimit);
+ }
+
+ private void deriveClusterControllerLimit(Optional<Double> clusterControllerLimit,
+ Optional<Double> contentNodeLimit,
+ Consumer<Double> setter) {
+ if (!clusterControllerLimit.isPresent()) {
+ contentNodeLimit.ifPresent(limit ->
+ // TODO: emit warning when using cluster controller resource limits are default enabled.
+ setter.accept(limit));
+ }
+ }
+
+ private void deriveContentNodeLimit(Optional<Double> contentNodeLimit,
+ Optional<Double> clusterControllerLimit,
+ Consumer<Double> setter) {
+ if (!contentNodeLimit.isPresent()) {
+ clusterControllerLimit.ifPresent(limit ->
+ setter.accept(calcContentNodeLimit(limit)));
+ }
+ }
+
+ private double calcContentNodeLimit(double clusterControllerLimit) {
+ // Note that validation in the range [0.0-1.0] is handled by the rnc schema.
+ return clusterControllerLimit + ((1.0 - clusterControllerLimit) / 2);
+ }
+
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
index dd29df61f35..d7df62d56cf 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ContentSearchCluster.java
@@ -79,13 +79,15 @@ public class ContentSearchCluster extends AbstractConfigProducer<SearchCluster>
private final Map<String, NewDocumentType> documentDefinitions;
private final Set<NewDocumentType> globallyDistributedDocuments;
private final boolean combined;
+ private final ResourceLimits resourceLimits;
public Builder(Map<String, NewDocumentType> documentDefinitions,
Set<NewDocumentType> globallyDistributedDocuments,
- boolean combined) {
+ boolean combined, ResourceLimits resourceLimits) {
this.documentDefinitions = documentDefinitions;
this.globallyDistributedDocuments = globallyDistributedDocuments;
this.combined = combined;
+ this.resourceLimits = resourceLimits;
}
@Override
@@ -106,10 +108,7 @@ public class ContentSearchCluster extends AbstractConfigProducer<SearchCluster>
if (tuning != null) {
search.setTuning(new DomSearchTuningBuilder().build(deployState, search, tuning.getXml()));
}
- ModelElement protonElem = clusterElem.childByPath("engine.proton");
- if (protonElem != null) {
- search.setResourceLimits(DomResourceLimitsBuilder.build(protonElem));
- }
+ search.setResourceLimits(resourceLimits);
buildAllStreamingSearchClusters(deployState, clusterElem, clusterName, search);
buildIndexedSearchCluster(deployState, clusterElem, clusterName, search);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
index 28e8c36d202..e96ba47c6b3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ResourceLimits.java
@@ -1,16 +1,17 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.content;
+import com.yahoo.vespa.config.content.FleetcontrollerConfig;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import java.util.Optional;
/**
- * Class tracking resource limits for a content cluster with engine proton.
+ * Class tracking feed block resource limits used by a component in a content cluster (e.g. cluster controller or content node).
*
* @author geirst
*/
-public class ResourceLimits implements ProtonConfig.Producer {
+public class ResourceLimits implements FleetcontrollerConfig.Producer, ProtonConfig.Producer {
private final Optional<Double> diskLimit;
private final Optional<Double> memoryLimit;
@@ -20,6 +21,26 @@ public class ResourceLimits implements ProtonConfig.Producer {
this.memoryLimit = builder.memoryLimit;
}
+ public Optional<Double> getDiskLimit() {
+ return diskLimit;
+ }
+
+ public Optional<Double> getMemoryLimit() {
+ return memoryLimit;
+ }
+
+ @Override
+ public void getConfig(FleetcontrollerConfig.Builder builder) {
+ // TODO: Choose other defaults when this is default enabled.
+ // Note: The resource categories must match the ones used in host info reporting
+ // between content nodes and cluster controller:
+ // storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp
+ builder.cluster_feed_block_limit.put("memory", memoryLimit.orElse(0.79));
+ builder.cluster_feed_block_limit.put("disk", diskLimit.orElse(0.79));
+ builder.cluster_feed_block_limit.put("attribute-enum-store", 0.89);
+ builder.cluster_feed_block_limit.put("attribute-multi-value", 0.89);
+ }
+
@Override
public void getConfig(ProtonConfig.Builder builder) {
if (diskLimit.isPresent()) {
@@ -39,11 +60,19 @@ public class ResourceLimits implements ProtonConfig.Producer {
return new ResourceLimits(this);
}
+ public Optional<Double> getDiskLimit() {
+ return diskLimit;
+ }
+
public Builder setDiskLimit(double diskLimit) {
this.diskLimit = Optional.of(diskLimit);
return this;
}
+ public Optional<Double> getMemoryLimit() {
+ return memoryLimit;
+ }
+
public Builder setMemoryLimit(double memoryLimit) {
this.memoryLimit = Optional.of(memoryLimit);
return this;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
index a627e030156..44de4a1abec 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java
@@ -38,6 +38,7 @@ import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.ContainerModel;
import com.yahoo.vespa.model.content.ClusterControllerConfig;
+import com.yahoo.vespa.model.content.ClusterResourceLimits;
import com.yahoo.vespa.model.content.ContentSearch;
import com.yahoo.vespa.model.content.ContentSearchCluster;
import com.yahoo.vespa.model.content.DistributionBitCalculator;
@@ -134,11 +135,14 @@ public class ContentCluster extends AbstractConfigProducer implements
ContentCluster c = new ContentCluster(context.getParentProducer(), getClusterId(contentElement), documentDefinitions,
globallyDistributedDocuments, routingSelection,
deployState.zone(), deployState.isHosted());
- c.clusterControllerConfig = new ClusterControllerConfig.Builder(getClusterId(contentElement), contentElement).build(deployState, c, contentElement.getXml());
+ var resourceLimits = new ClusterResourceLimits.Builder().build(contentElement);
+ c.clusterControllerConfig = new ClusterControllerConfig.Builder(getClusterId(contentElement),
+ contentElement,
+ resourceLimits.getClusterControllerLimits()).build(deployState, c, contentElement.getXml());
c.search = new ContentSearchCluster.Builder(documentDefinitions,
- globallyDistributedDocuments,
- isCombined(getClusterId(contentElement), containers))
- .build(deployState, c, contentElement.getXml());
+ globallyDistributedDocuments,
+ isCombined(getClusterId(contentElement), containers),
+ resourceLimits.getContentNodeLimits()).build(deployState, c, contentElement.getXml());
c.persistenceFactory = new EngineFactoryBuilder().build(contentElement, c);
c.storageNodes = new StorageCluster.Builder().build(deployState, c, w3cContentElement);
c.distributorNodes = new DistributorCluster.Builder(c).build(deployState, c, w3cContentElement);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
index 8e91f14238e..210f062f9b2 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/DomResourceLimitsBuilder.java
@@ -5,17 +5,17 @@ import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import com.yahoo.vespa.model.content.ResourceLimits;
/**
- * Builder for resource limits for a content cluster with engine proton.
+ * Builder for feed block resource limits.
*
* @author geirst
*/
public class DomResourceLimitsBuilder {
- public static ResourceLimits build(ModelElement contentXml) {
+ public static ResourceLimits.Builder createBuilder(ModelElement contentXml) {
ResourceLimits.Builder builder = new ResourceLimits.Builder();
ModelElement resourceLimits = contentXml.child("resource-limits");
if (resourceLimits == null) {
- return builder.build();
+ return builder;
}
if (resourceLimits.child("disk") != null) {
builder.setDiskLimit(resourceLimits.childAsDouble("disk"));
@@ -23,7 +23,7 @@ public class DomResourceLimitsBuilder {
if (resourceLimits.child("memory") != null) {
builder.setMemoryLimit(resourceLimits.childAsDouble("memory"));
}
- return builder.build();
+ return builder;
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/FileStorProducer.java b/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/FileStorProducer.java
index ab5dab4fbb9..57292b32a35 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/FileStorProducer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/storagecluster/FileStorProducer.java
@@ -47,7 +47,6 @@ public class FileStorProducer implements StorFilestorConfig.Producer {
private final int reponseNumThreads;
private final StorFilestorConfig.Response_sequencer_type.Enum responseSequencerType;
private final boolean useAsyncMessageHandlingOnSchedule;
- private final int mergeChunkSize;
private static StorFilestorConfig.Response_sequencer_type.Enum convertResponseSequencerType(String sequencerType) {
try {
@@ -56,17 +55,13 @@ public class FileStorProducer implements StorFilestorConfig.Producer {
return StorFilestorConfig.Response_sequencer_type.Enum.ADAPTIVE;
}
}
- private static int alignUp2MiB(int value) {
- final int twoMB = 0x200000;
- return ((value + twoMB - 1)/twoMB) * twoMB;
- }
+
public FileStorProducer(ModelContext.FeatureFlags featureFlags, ContentCluster parent, Integer numThreads) {
this.numThreads = numThreads;
this.cluster = parent;
this.reponseNumThreads = featureFlags.defaultNumResponseThreads();
this.responseSequencerType = convertResponseSequencerType(featureFlags.responseSequencerType());
useAsyncMessageHandlingOnSchedule = featureFlags.useAsyncMessageHandlingOnSchedule();
- mergeChunkSize = alignUp2MiB(featureFlags.mergeChunkSize()); // Align up to default huge page size.
}
@Override
@@ -78,7 +73,6 @@ public class FileStorProducer implements StorFilestorConfig.Producer {
builder.num_response_threads(reponseNumThreads);
builder.response_sequencer_type(responseSequencerType);
builder.use_async_message_handling_on_schedule(useAsyncMessageHandlingOnSchedule);
- builder.bucket_merge_chunk_size(mergeChunkSize);
}
}
diff --git a/config-model/src/main/python/ES_Vespa_parser.py b/config-model/src/main/python/ES_Vespa_parser.py
index b3398fd0403..86df16981f0 100644
--- a/config-model/src/main/python/ES_Vespa_parser.py
+++ b/config-model/src/main/python/ES_Vespa_parser.py
@@ -32,7 +32,7 @@ class ElasticSearchParser:
self.application_name = args.application_name
def main(self):
- self.path = os.getcwd() + "/application/"
+ self.path = os.getcwd() + "/" + self.application_name + "/"
try:
os.mkdir(self.path, 0o777)
print(" > Created folder '" + self.path + "'")
diff --git a/config-model/src/main/resources/schema/content.rnc b/config-model/src/main/resources/schema/content.rnc
index 5646bc72056..a48d38b9f2c 100644
--- a/config-model/src/main/resources/schema/content.rnc
+++ b/config-model/src/main/resources/schema/content.rnc
@@ -98,7 +98,8 @@ ClusterTuning = element tuning {
ClusterControllerTuning? &
Maintenance? &
PersistenceThreads? &
- MinNodeRatioPerGroup?
+ MinNodeRatioPerGroup? &
+ ResourceLimits?
}
Content = element content {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java
index ecf026e7d88..180e4913d5c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/NodeResourceChangeValidatorTest.java
@@ -61,7 +61,7 @@ public class NodeResourceChangeValidatorTest {
Clock.systemUTC().instant());
}
- private static VespaModel model(int mem1, int mem2, int mem3, int mem4) {
+ private static VespaModel model(int mem1, int mem2, int cpu1, int cpu2) {
var properties = new TestProperties();
properties.setHostedVespa(true);
var deployState = new DeployState.Builder().properties(properties)
@@ -82,7 +82,7 @@ public class NodeResourceChangeValidatorTest {
" </container>\n" +
" <content id='content1' version='1.0'>\n" +
" <nodes count='3'>\n" +
- " <resources vcpu='1' memory='" + mem3 + "Gb' disk='100Gb'/>" +
+ " <resources vcpu='" + cpu1 + "' memory='8Gb' disk='100Gb'/>" +
" </nodes>\n" +
" <documents>\n" +
" <document mode='index' type='test'/>\n" +
@@ -91,7 +91,7 @@ public class NodeResourceChangeValidatorTest {
" </content>\n" +
" <content id='content2' version='1.0'>\n" +
" <nodes count='4'>\n" +
- " <resources vcpu='1' memory='" + mem4 + "Gb' disk='100Gb'/>" +
+ " <resources vcpu='" + cpu2 + "' memory='8Gb' disk='100Gb'/>" +
" </nodes>\n" +
" <documents>\n" +
" <document mode='streaming' type='test'/>\n" +
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java
index 294df42bd77..60af25a3087 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JvmOptionsTest.java
@@ -78,11 +78,11 @@ public class JvmOptionsTest extends ContainerModelBuilderTestBase {
}
private static void verifyIgnoreJvmGCOptions(boolean isHosted) throws IOException, SAXException {
- verifyIgnoreJvmGCOptionsIfJvmArgs("jvmargs", ContainerCluster.G1GC);
- verifyIgnoreJvmGCOptionsIfJvmArgs( "jvm-options", "-XX:+UseG1GC");
+ verifyIgnoreJvmGCOptionsIfJvmArgs("jvmargs", ContainerCluster.G1GC, isHosted);
+ verifyIgnoreJvmGCOptionsIfJvmArgs( "jvm-options", "-XX:+UseG1GC", isHosted);
}
- private static void verifyIgnoreJvmGCOptionsIfJvmArgs(String jvmOptionsName, String expectedGC) throws IOException, SAXException {
+ private static void verifyIgnoreJvmGCOptionsIfJvmArgs(String jvmOptionsName, String expectedGC, boolean isHosted) throws IOException, SAXException {
String servicesXml =
"<container version='1.0'>" +
" <nodes jvm-gc-options='-XX:+UseG1GC' " + jvmOptionsName + "='-XX:+UseParNewGC'>" +
@@ -95,6 +95,7 @@ public class JvmOptionsTest extends ContainerModelBuilderTestBase {
VespaModel model = new VespaModel(new NullConfigModelRegistry(), new DeployState.Builder()
.applicationPackage(applicationPackage)
.deployLogger(logger)
+ .properties(new TestProperties().setHostedVespa(isHosted))
.build());
QrStartConfig.Builder qrStartBuilder = new QrStartConfig.Builder();
model.getConfig(qrStartBuilder, "container/container.0");
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java
new file mode 100644
index 00000000000..bc830c079d0
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterResourceLimitsTest.java
@@ -0,0 +1,101 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.content;
+
+import org.junit.Test;
+
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * @author geirst
+ */
+public class ClusterResourceLimitsTest {
+
+ private static class Fixture {
+ ResourceLimits.Builder ctrlBuilder = new ResourceLimits.Builder();
+ ResourceLimits.Builder nodeBuilder = new ResourceLimits.Builder();
+
+ public Fixture ctrlDisk(double limit) {
+ ctrlBuilder.setDiskLimit(limit);
+ return this;
+ }
+ public Fixture ctrlMemory(double limit) {
+ ctrlBuilder.setMemoryLimit(limit);
+ return this;
+ }
+ public Fixture nodeDisk(double limit) {
+ nodeBuilder.setDiskLimit(limit);
+ return this;
+ }
+ public Fixture nodeMemory(double limit) {
+ nodeBuilder.setMemoryLimit(limit);
+ return this;
+ }
+ public ClusterResourceLimits build() {
+ var builder = new ClusterResourceLimits.Builder();
+ builder.setClusterControllerBuilder(ctrlBuilder);
+ builder.setContentNodeBuilder(nodeBuilder);
+ return builder.build();
+ }
+ }
+
+ @Test
+ public void content_node_limits_are_derived_from_cluster_controller_limits_if_not_set() {
+ assertLimits(0.6, 0.7, 0.8, 0.85,
+ new Fixture().ctrlDisk(0.6).ctrlMemory(0.7));
+ assertLimits(0.6, null, 0.8, null,
+ new Fixture().ctrlDisk(0.6));
+ assertLimits(null, 0.7, null, 0.85,
+ new Fixture().ctrlMemory(0.7));
+ }
+
+ @Test
+ public void content_node_limits_can_be_set_explicit() {
+ assertLimits(0.6, 0.7, 0.9, 0.95,
+ new Fixture().ctrlDisk(0.6).ctrlMemory(0.7).nodeDisk(0.9).nodeMemory(0.95));
+ assertLimits(0.6, null, 0.9, null,
+ new Fixture().ctrlDisk(0.6).nodeDisk(0.9));
+ assertLimits(null, 0.7, null, 0.95,
+ new Fixture().ctrlMemory(0.7).nodeMemory(0.95));
+ }
+
+ @Test
+ public void cluster_controller_limits_are_equal_to_content_node_limits_if_not_set() {
+ assertLimits(0.9, 0.95, 0.9, 0.95,
+ new Fixture().nodeDisk(0.9).nodeMemory(0.95));
+ assertLimits(0.9, null, 0.9, null,
+ new Fixture().nodeDisk(0.9));
+ assertLimits(null, 0.95, null, 0.95,
+ new Fixture().nodeMemory(0.95));
+ }
+
+ @Test
+ public void limits_are_derived_from_the_other_if_not_set() {
+ assertLimits(0.6, 0.95, 0.8, 0.95,
+ new Fixture().ctrlDisk(0.6).nodeMemory(0.95));
+ assertLimits(0.9, 0.7, 0.9, 0.85,
+ new Fixture().ctrlMemory(0.7).nodeDisk(0.9));
+ }
+
+ private void assertLimits(Double expCtrlDisk, Double expCtrlMemory, Double expNodeDisk, Double expNodeMemory, Fixture f) {
+ var limits = f.build();
+ assertLimits(expCtrlDisk, expCtrlMemory, limits.getClusterControllerLimits());
+ assertLimits(expNodeDisk, expNodeMemory, limits.getContentNodeLimits());
+ }
+
+ private void assertLimits(Double expDisk, Double expMemory, ResourceLimits limits) {
+ assertLimit(expDisk, limits.getDiskLimit());
+ assertLimit(expMemory, limits.getMemoryLimit());
+ }
+
+ private void assertLimit(Double expLimit, Optional<Double> actLimit) {
+ if (expLimit == null) {
+ assertFalse(actLimit.isPresent());
+ } else {
+ assertEquals(expLimit, actLimit.get(), 0.00001);
+ }
+ }
+
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
index 3415044b088..bc60908e268 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentSearchClusterTest.java
@@ -69,15 +69,30 @@ public class ContentSearchClusterTest {
}
private static ProtonConfig getProtonConfig(ContentCluster cluster) {
- ProtonConfig.Builder protonCfgBuilder = new ProtonConfig.Builder();
- cluster.getSearch().getConfig(protonCfgBuilder);
- return new ProtonConfig(protonCfgBuilder);
+ var builder = new ProtonConfig.Builder();
+ cluster.getSearch().getConfig(builder);
+ return new ProtonConfig(builder);
}
- private static void assertProtonResourceLimits(double expDiskLimit, double expMemoryLimits, String clusterXml) throws Exception {
- ProtonConfig cfg = getProtonConfig(createCluster(clusterXml));
+ private static void assertProtonResourceLimits(double expDiskLimit, double expMemoryLimit, String clusterXml) throws Exception {
+ assertProtonResourceLimits(expDiskLimit, expMemoryLimit, createCluster(clusterXml));
+ }
+
+ private static void assertProtonResourceLimits(double expDiskLimit, double expMemoryLimit, ContentCluster cluster) {
+ var cfg = getProtonConfig(cluster);
assertEquals(expDiskLimit, cfg.writefilter().disklimit(), EPSILON);
- assertEquals(expMemoryLimits, cfg.writefilter().memorylimit(), EPSILON);
+ assertEquals(expMemoryLimit, cfg.writefilter().memorylimit(), EPSILON);
+ }
+
+ private static void assertClusterControllerResourceLimits(double expDiskLimit, double expMemoryLimit, String clusterXml) throws Exception {
+ assertClusterControllerResourceLimits(expDiskLimit, expMemoryLimit, createCluster(clusterXml));
+ }
+
+ private static void assertClusterControllerResourceLimits(double expDiskLimit, double expMemoryLimit, ContentCluster cluster) {
+ var limits = getFleetcontrollerConfig(cluster).cluster_feed_block_limit();
+ assertEquals(4, limits.size());
+ assertEquals(expDiskLimit, limits.get("disk"), EPSILON);
+ assertEquals(expMemoryLimit, limits.get("memory"), EPSILON);
}
@Test
@@ -105,6 +120,19 @@ public class ContentSearchClusterTest {
}
@Test
+ public void cluster_controller_resource_limits_can_be_set() throws Exception {
+ assertClusterControllerResourceLimits(0.92, 0.93,
+ new ContentClusterBuilder().clusterControllerDiskLimit(0.92).clusterControllerMemoryLimit(0.93).getXml());
+ }
+
+ @Test
+ public void resource_limits_are_derived_from_the_other_if_not_specified() throws Exception {
+ var cluster = createCluster(new ContentClusterBuilder().clusterControllerDiskLimit(0.5).protonMemoryLimit(0.95).getXml());
+ assertProtonResourceLimits(0.75, 0.95, cluster);
+ assertClusterControllerResourceLimits(0.5, 0.95, cluster);
+ }
+
+ @Test
public void requireThatGloballyDistributedDocumentTypeIsTaggedAsSuch() throws Exception {
ProtonConfig cfg = getProtonConfig(createClusterWithGlobalType());
assertEquals(2, cfg.documentdb().size());
@@ -149,8 +177,9 @@ public class ContentSearchClusterTest {
}
private static FleetcontrollerConfig getFleetcontrollerConfig(ContentCluster cluster) {
- FleetcontrollerConfig.Builder builder = new FleetcontrollerConfig.Builder();
+ var builder = new FleetcontrollerConfig.Builder();
cluster.getConfig(builder);
+ cluster.getClusterControllerConfig().getConfig(builder);
builder.cluster_name("unknown");
builder.index(0);
builder.zookeeper_server("unknown");
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
index 01bbffce360..3a59f35ce2e 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/FleetControllerClusterTest.java
@@ -10,6 +10,7 @@ import com.yahoo.vespa.model.builder.xml.dom.ModelElement;
import org.junit.Test;
import org.w3c.dom.Document;
+import static com.yahoo.config.model.test.TestUtil.joinLines;
import static org.junit.Assert.assertEquals;
public class FleetControllerClusterTest {
@@ -19,8 +20,11 @@ public class FleetControllerClusterTest {
var deployState = new DeployState.Builder().properties(
new TestProperties().enableFeedBlockInDistributor(enableFeedBlockInDistributor)).build();
MockRoot root = new MockRoot("", deployState);
- return new ClusterControllerConfig.Builder("storage", new ModelElement(doc.getDocumentElement())).build(root.getDeployState(), root,
- new ModelElement(doc.getDocumentElement()).getXml());
+ var clusterElement = new ModelElement(doc.getDocumentElement());
+ return new ClusterControllerConfig.Builder("storage",
+ clusterElement,
+ new ClusterResourceLimits.Builder().build(clusterElement).getClusterControllerLimits()).
+ build(root.getDeployState(), root, clusterElement.getXml());
}
private ClusterControllerConfig parse(String xml) {
@@ -94,15 +98,43 @@ public class FleetControllerClusterTest {
assertEquals(0.0, config.min_node_ratio_per_group(), 0.01);
}
+
@Test
public void default_cluster_feed_block_limits_are_set() {
- var config = getConfigForBasicCluster();
+ assertLimits(0.79, 0.79, getConfigForBasicCluster());
+ }
+
+ @Test
+ public void resource_limits_can_be_set_in_tuning() {
+ assertLimits(0.6, 0.7, getConfigForResourceLimitsTuning(0.6, 0.7));
+ assertLimits(0.6, 0.79, getConfigForResourceLimitsTuning(0.6, null));
+ assertLimits(0.79, 0.7, getConfigForResourceLimitsTuning(null, 0.7));
+ }
+
+ private static double DELTA = 0.00001;
+
+ private void assertLimits(double expDisk, double expMemory, FleetcontrollerConfig config) {
var limits = config.cluster_feed_block_limit();
assertEquals(4, limits.size());
- assertEquals(0.79, limits.get("memory"), 0.0001);
- assertEquals(0.79, limits.get("disk"), 0.0001);
- assertEquals(0.89, limits.get("attribute-enum-store"), 0.0001);
- assertEquals(0.89, limits.get("attribute-multi-value"), 0.0001);
+ assertEquals(expDisk, limits.get("disk"), DELTA);
+ assertEquals(expMemory, limits.get("memory"), DELTA);
+ assertEquals(0.89, limits.get("attribute-enum-store"), DELTA);
+ assertEquals(0.89, limits.get("attribute-multi-value"), DELTA);
+ }
+
+ private FleetcontrollerConfig getConfigForResourceLimitsTuning(Double diskLimit, Double memoryLimit) {
+ FleetcontrollerConfig.Builder builder = new FleetcontrollerConfig.Builder();
+ parse(joinLines("<cluster id=\"test\">",
+ "<documents/>",
+ "<tuning>",
+ " <resource-limits>",
+ (diskLimit != null ? (" <disk>" + diskLimit + "</disk>") : ""),
+ (memoryLimit != null ? (" <memory>" + memoryLimit + "</memory>") : ""),
+ " </resource-limits>",
+ "</tuning>" +
+ "</cluster>")).
+ getConfig(builder);
+ return new FleetcontrollerConfig(builder);
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
index 9c857414717..5cf57430f91 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/StorageClusterTest.java
@@ -287,19 +287,6 @@ public class StorageClusterTest {
assertEquals(StorFilestorConfig.Response_sequencer_type.THROUGHPUT, config.response_sequencer_type());
}
- private void verifyMergeChunkSize(int expected, int value) {
- StorFilestorConfig.Builder builder = new StorFilestorConfig.Builder();
- simpleCluster(new TestProperties().setMergeChunkSize(value)).getConfig(builder);
- StorFilestorConfig config = new StorFilestorConfig(builder);
- assertEquals(expected, config.bucket_merge_chunk_size());
- }
-
- @Test
- public void testFeatureFlagControlOfMergeChunkSize() {
- verifyMergeChunkSize(0x200000, 13);
- verifyMergeChunkSize(0x1600000, 0x1500000);
- }
-
private void verifyAsyncMessageHandlingOnSchedule(boolean expected, boolean value) {
StorFilestorConfig.Builder builder = new StorFilestorConfig.Builder();
simpleCluster(new TestProperties().setAsyncMessageHandlingOnSchedule(value)).getConfig(builder);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
index 866c03d82f0..491326fdc9c 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/utils/ContentClusterBuilder.java
@@ -26,6 +26,8 @@ public class ContentClusterBuilder {
private Optional<String> dispatchXml = Optional.empty();
private Optional<Double> protonDiskLimit = Optional.empty();
private Optional<Double> protonMemoryLimit = Optional.empty();
+ private Optional<Double> clusterControllerDiskLimit = Optional.empty();
+ private Optional<Double> clusterControllerMemoryLimit = Optional.empty();
public ContentClusterBuilder() {
}
@@ -67,13 +69,23 @@ public class ContentClusterBuilder {
return this;
}
- public ContentClusterBuilder protonDiskLimit(double diskLimit) {
- protonDiskLimit = Optional.of(diskLimit);
+ public ContentClusterBuilder protonDiskLimit(double limit) {
+ protonDiskLimit = Optional.of(limit);
return this;
}
- public ContentClusterBuilder protonMemoryLimit(double memoryLimit) {
- protonMemoryLimit = Optional.of(memoryLimit);
+ public ContentClusterBuilder protonMemoryLimit(double limit) {
+ protonMemoryLimit = Optional.of(limit);
+ return this;
+ }
+
+ public ContentClusterBuilder clusterControllerDiskLimit(double limit) {
+ clusterControllerDiskLimit = Optional.of(limit);
+ return this;
+ }
+
+ public ContentClusterBuilder clusterControllerMemoryLimit(double limit) {
+ clusterControllerMemoryLimit = Optional.of(limit);
return this;
}
@@ -88,14 +100,17 @@ public class ContentClusterBuilder {
" <engine>",
" <proton>",
" <searchable-copies>" + searchableCopies + "</searchable-copies>",
- getResourceLimitsXml(" "),
+ getProtonResourceLimitsXml(" "),
" </proton>",
" </engine>");
if (dispatchXml.isPresent()) {
xml += dispatchXml.get();
}
- return xml + groupXml +
- "</content>";
+ xml += groupXml;
+ xml += joinLines(" <tuning>",
+ getTuningResourceLimitsXml(" "),
+ " </tuning>");
+ return xml + "</content>";
}
private static String getSimpleGroupXml() {
@@ -104,11 +119,19 @@ public class ContentClusterBuilder {
" </group>");
}
- private String getResourceLimitsXml(String indent) {
- if (protonDiskLimit.isPresent() || protonMemoryLimit.isPresent()) {
+ private String getProtonResourceLimitsXml(String indent) {
+ return getResourceLimitsXml(indent, protonDiskLimit, protonMemoryLimit);
+ }
+
+ private String getTuningResourceLimitsXml(String indent) {
+ return getResourceLimitsXml(indent, clusterControllerDiskLimit, clusterControllerMemoryLimit);
+ }
+
+ private String getResourceLimitsXml(String indent, Optional<Double> diskLimit, Optional<Double> memoryLimit) {
+ if (diskLimit.isPresent() || memoryLimit.isPresent()) {
String xml = joinLines(indent + "<resource-limits>",
- getXmlLine("disk", protonDiskLimit, indent + " "),
- getXmlLine("memory", protonMemoryLimit, indent + " "),
+ getXmlLine("disk", diskLimit, indent + " "),
+ getXmlLine("memory", memoryLimit, indent + " "),
indent + "</resource-limits>");
return xml;
}
diff --git a/config/src/tests/configfetcher/configfetcher.cpp b/config/src/tests/configfetcher/configfetcher.cpp
index 508322bb74e..0cff6208873 100644
--- a/config/src/tests/configfetcher/configfetcher.cpp
+++ b/config/src/tests/configfetcher/configfetcher.cpp
@@ -5,6 +5,7 @@
#include <vespa/vespalib/util/exception.h>
#include "config-my.h"
#include <atomic>
+#include <thread>
using namespace config;
diff --git a/config/src/tests/configholder/configholder.cpp b/config/src/tests/configholder/configholder.cpp
index d69ad8022f9..1f2c1f27df6 100644
--- a/config/src/tests/configholder/configholder.cpp
+++ b/config/src/tests/configholder/configholder.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/vespalib/testkit/test_kit.h>
#include <vespa/config/common/configholder.h>
+#include <thread>
using namespace config;
diff --git a/config/src/tests/configretriever/configretriever.cpp b/config/src/tests/configretriever/configretriever.cpp
index d4ebe2da994..c8157ff7c16 100644
--- a/config/src/tests/configretriever/configretriever.cpp
+++ b/config/src/tests/configretriever/configretriever.cpp
@@ -1,5 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "config-bootstrap.h"
+#include "config-foo.h"
+#include "config-bar.h"
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/config/print.h>
@@ -11,9 +14,7 @@
#include <vespa/config/subscription/configsubscription.h>
#include <vespa/config/subscription/sourcespec.h>
#include <vespa/config/common/exceptions.h>
-#include "config-bootstrap.h"
-#include "config-foo.h"
-#include "config-bar.h"
+#include <thread>
#include <atomic>
using namespace config;
diff --git a/config/src/tests/frt/frt.cpp b/config/src/tests/frt/frt.cpp
index 6cffe079dae..cb09b8f7254 100644
--- a/config/src/tests/frt/frt.cpp
+++ b/config/src/tests/frt/frt.cpp
@@ -1,5 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "config-my.h"
+#include "config-bar.h"
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/config/common/iconfigholder.h>
#include <vespa/config/common/trace.h>
@@ -15,9 +17,7 @@
#include <vespa/fnet/frt/supervisor.h>
#include <vespa/config/frt/protocol.h>
#include <lz4.h>
-#include "config-my.h"
-#include "config-bar.h"
-
+#include <thread>
using namespace config;
using namespace vespalib;
diff --git a/config/src/tests/subscriber/subscriber.cpp b/config/src/tests/subscriber/subscriber.cpp
index 9d5aeddaf37..d58699f26e0 100644
--- a/config/src/tests/subscriber/subscriber.cpp
+++ b/config/src/tests/subscriber/subscriber.cpp
@@ -1,13 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "config-foo.h"
+#include "config-bar.h"
+#include "config-baz.h"
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/config/config.h>
#include <vespa/config/common/misc.h>
#include <vespa/config/common/configholder.h>
#include <vespa/config/subscription/configsubscription.h>
#include <vespa/config/common/exceptions.h>
-#include "config-foo.h"
-#include "config-bar.h"
-#include "config-baz.h"
+#include <thread>
using namespace config;
using namespace vespalib;
diff --git a/config/src/tests/subscription/subscription.cpp b/config/src/tests/subscription/subscription.cpp
index 9583111a2f7..7a5fccec019 100644
--- a/config/src/tests/subscription/subscription.cpp
+++ b/config/src/tests/subscription/subscription.cpp
@@ -4,6 +4,7 @@
#include <vespa/config/common/configholder.h>
#include <vespa/config/subscription/configsubscription.h>
#include <config-my.h>
+#include <thread>
using namespace config;
using namespace std::chrono_literals;
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 f2f5eb3afa1..a72381d4e84 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
@@ -140,7 +140,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
private final TesterClient testerClient;
private final Metric metric;
private final ClusterReindexingStatusClient clusterReindexingStatusClient;
- private final BooleanFlag waitForResourcesInPrepareFlag;
@Inject
public ApplicationRepository(TenantRepository tenantRepository,
@@ -193,7 +192,6 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
this.testerClient = Objects.requireNonNull(testerClient);
this.metric = Objects.requireNonNull(metric);
this.clusterReindexingStatusClient = clusterReindexingStatusClient;
- this.waitForResourcesInPrepareFlag = Flags.WAIT_FOR_RESOURCES_IN_PREPARE.bindTo(flagSource);
}
public static class Builder {
@@ -401,10 +399,9 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
SessionRepository sessionRepository = tenant.getSessionRepository();
DeployLogger logger = new SilentDeployLogger();
Session newSession = sessionRepository.createSessionFromExisting(activeSession, true, timeoutBudget);
- boolean waitForResourcesInPrepare = waitForResourcesInPrepareFlag.value();
return Optional.of(Deployment.unprepared(newSession, this, hostProvisioner, tenant, logger, timeout, clock,
- false /* don't validate as this is already deployed */, bootstrap, waitForResourcesInPrepare));
+ false /* don't validate as this is already deployed */, bootstrap));
}
@Override
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
index ff2bbc12e29..980134f8884 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java
@@ -81,8 +81,8 @@ public class Deployment implements com.yahoo.config.provision.Deployment {
public static Deployment unprepared(Session session, ApplicationRepository applicationRepository,
Optional<Provisioner> provisioner, Tenant tenant, DeployLogger logger,
- Duration timeout, Clock clock, boolean validate, boolean isBootstrap, boolean waitForResourcesInPrepare) {
- Supplier<PrepareParams> params = createPrepareParams(clock, timeout, session, isBootstrap, !validate, false, waitForResourcesInPrepare);
+ Duration timeout, Clock clock, boolean validate, boolean isBootstrap) {
+ Supplier<PrepareParams> params = createPrepareParams(clock, timeout, session, isBootstrap, !validate, false, true);
return new Deployment(session, applicationRepository, params, provisioner, tenant, logger, clock, true, false);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index fa30cb895c0..aa9ae65394f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -147,7 +147,6 @@ public class ModelContextImpl implements ModelContext {
public static class FeatureFlags implements ModelContext.FeatureFlags {
- private final boolean enableAutomaticReindexing;
private final double reindexerWindowSizeIncrement;
private final double defaultTermwiseLimit;
private final boolean useThreePhaseUpdates;
@@ -159,7 +158,6 @@ public class ModelContextImpl implements ModelContext {
private final boolean skipMbusReplyThread;
private final boolean useAccessControlTlsHandshakeClientAuth;
private final boolean useAsyncMessageHandlingOnSchedule;
- private final int mergeChunkSize;
private final double feedConcurrency;
private final boolean reconfigurableZookeeperServer;
private final boolean enableJdiscConnectionLog;
@@ -168,7 +166,6 @@ public class ModelContextImpl implements ModelContext {
private final boolean enableFeedBlockInDistributor;
public FeatureFlags(FlagSource source, ApplicationId appId) {
- this.enableAutomaticReindexing = flagValue(source, appId, Flags.ENABLE_AUTOMATIC_REINDEXING);
this.reindexerWindowSizeIncrement = flagValue(source, appId, Flags.REINDEXER_WINDOW_SIZE_INCREMENT);
this.defaultTermwiseLimit = flagValue(source, appId, Flags.DEFAULT_TERM_WISE_LIMIT);
this.useThreePhaseUpdates = flagValue(source, appId, Flags.USE_THREE_PHASE_UPDATES);
@@ -180,7 +177,6 @@ public class ModelContextImpl implements ModelContext {
this.skipMbusReplyThread = flagValue(source, appId, Flags.SKIP_MBUS_REPLY_THREAD);
this.useAccessControlTlsHandshakeClientAuth = flagValue(source, appId, Flags.USE_ACCESS_CONTROL_CLIENT_AUTHENTICATION);
this.useAsyncMessageHandlingOnSchedule = flagValue(source, appId, Flags.USE_ASYNC_MESSAGE_HANDLING_ON_SCHEDULE);
- this.mergeChunkSize = flagValue(source, appId, Flags.MERGE_CHUNK_SIZE);
this.feedConcurrency = flagValue(source, appId, Flags.FEED_CONCURRENCY);
this.reconfigurableZookeeperServer = flagValue(source, appId, Flags.RECONFIGURABLE_ZOOKEEPER_SERVER_FOR_CLUSTER_CONTROLLER);
this.enableJdiscConnectionLog = flagValue(source, appId, Flags.ENABLE_JDISC_CONNECTION_LOG);
@@ -189,7 +185,6 @@ public class ModelContextImpl implements ModelContext {
this.enableFeedBlockInDistributor = flagValue(source, appId, Flags.ENABLE_FEED_BLOCK_IN_DISTRIBUTOR);
}
- @Override public boolean enableAutomaticReindexing() { return enableAutomaticReindexing; }
@Override public double reindexerWindowSizeIncrement() { return reindexerWindowSizeIncrement; }
@Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; }
@Override public boolean useThreePhaseUpdates() { return useThreePhaseUpdates; }
@@ -201,7 +196,6 @@ public class ModelContextImpl implements ModelContext {
@Override public boolean skipMbusReplyThread() { return skipMbusReplyThread; }
@Override public boolean useAccessControlTlsHandshakeClientAuth() { return useAccessControlTlsHandshakeClientAuth; }
@Override public boolean useAsyncMessageHandlingOnSchedule() { return useAsyncMessageHandlingOnSchedule; }
- @Override public int mergeChunkSize() { return mergeChunkSize; }
@Override public double feedConcurrency() { return feedConcurrency; }
@Override public boolean reconfigurableZookeeperServer() { return reconfigurableZookeeperServer; }
@Override public boolean enableJdiscConnectionLog() { return enableJdiscConnectionLog; }
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
index 582ecd13ce5..b1edc031e0b 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
@@ -95,7 +95,6 @@ public class ModelContextImplTest {
assertEquals(new Version(8), context.wantedNodeVespaVersion());
assertEquals(1.0, context.properties().featureFlags().defaultTermwiseLimit(), 0.0);
assertFalse(context.properties().featureFlags().useAsyncMessageHandlingOnSchedule());
- assertEquals(0x2000000, context.properties().featureFlags().mergeChunkSize());
assertEquals(0.5, context.properties().featureFlags().feedConcurrency(), 0.0);
}
diff --git a/container-core/pom.xml b/container-core/pom.xml
index 7c98b524c73..051b572b28f 100644
--- a/container-core/pom.xml
+++ b/container-core/pom.xml
@@ -42,6 +42,7 @@
<scope>test</scope>
</dependency>
<dependency>
+ <!-- TODO Vespa 8: stop providing org.json:json -->
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</dependency>
diff --git a/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java b/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java
index 92840cee48f..8c902f88e38 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java
@@ -1,13 +1,14 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.handler.metrics;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
import com.yahoo.restapi.Path;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.net.URI;
import java.util.List;
@@ -26,6 +27,8 @@ import static java.util.logging.Level.WARNING;
*/
public abstract class HttpHandlerBase extends ThreadedHttpRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
protected HttpHandlerBase(Executor executor) {
super(executor);
}
@@ -49,15 +52,14 @@ public abstract class HttpHandlerBase extends ThreadedHttpRequestHandler {
protected JsonResponse resourceListResponse(URI requestUri, List<String> resources) {
try {
return new JsonResponse(OK, resourceList(requestUri, resources));
- } catch (JSONException e) {
+ } catch (JsonProcessingException e) {
log.log(WARNING, "Bad JSON construction in generated resource list for " + requestUri.getPath(), e);
return new ErrorResponse(INTERNAL_SERVER_ERROR,
"An error occurred when generating the list of api resources.");
}
}
- // TODO: Use jackson with a "Resources" class instead of JSONObject
- private static String resourceList(URI requestUri, List<String> resources) throws JSONException {
+ private static String resourceList(URI requestUri, List<String> resources) throws JsonProcessingException {
int port = requestUri.getPort();
String host = requestUri.getHost();
StringBuilder base = new StringBuilder("http://");
@@ -66,13 +68,14 @@ public abstract class HttpHandlerBase extends ThreadedHttpRequestHandler {
base.append(":").append(port);
}
String uriBase = base.toString();
- JSONArray linkList = new JSONArray();
+ ArrayNode linkList = jsonMapper.createArrayNode();
for (String api : resources) {
- JSONObject resource = new JSONObject();
+ ObjectNode resource = jsonMapper.createObjectNode();
resource.put("url", uriBase + api);
- linkList.put(resource);
+ linkList.add(resource);
}
- return new JSONObject().put("resources", linkList).toString(4);
+ return jsonMapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(jsonMapper.createObjectNode().set("resources", linkList));
}
}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java
index d105eaa9d98..f1ef7894511 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java
@@ -1,17 +1,16 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.vespa.defaults.Defaults;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.io.IOException;
import java.io.UncheckedIOException;
-import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.time.Instant;
-import java.util.Set;
import java.util.stream.Stream;
/**
@@ -19,19 +18,17 @@ import java.util.stream.Stream;
*/
public class CoredumpGatherer {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final Path COREDUMP_PATH = Path.of(Defaults.getDefaults().underVespaHome("var/crash/processing"));
- public static JSONObject gatherCoredumpMetrics(FileWrapper fileWrapper) {
+ public static JsonNode gatherCoredumpMetrics(FileWrapper fileWrapper) {
int coredumps = getNumberOfCoredumps(fileWrapper);
- JSONObject packet = new JSONObject();
-
- try {
- packet.put("status_code", coredumps == 0 ? 0 : 1);
- packet.put("status_msg", coredumps == 0 ? "OK" : String.format("Found %d coredump(s)", coredumps));
- packet.put("timestamp", Instant.now().getEpochSecond());
- packet.put("application", "system-coredumps-processing");
-
- } catch (JSONException e) {}
+ ObjectNode packet = jsonMapper.createObjectNode();
+ packet.put("status_code", coredumps == 0 ? 0 : 1);
+ packet.put("status_msg", coredumps == 0 ? "OK" : String.format("Found %d coredump(s)", coredumps));
+ packet.put("timestamp", Instant.now().getEpochSecond());
+ packet.put("application", "system-coredumps-processing");
return packet;
}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java
index 730f7bc13cd..28f99096d84 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java
@@ -1,8 +1,9 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.nio.file.Path;
@@ -13,9 +14,11 @@ import java.time.Instant;
*/
public class HostLifeGatherer {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final Path UPTIME_PATH = Path.of("/proc");
- public static JSONObject getHostLifePacket(FileWrapper fileWrapper) {
+ public static JsonNode getHostLifePacket(FileWrapper fileWrapper) {
long upTime;
int statusCode = 0;
String statusMessage = "OK";
@@ -29,19 +32,15 @@ public class HostLifeGatherer {
}
- JSONObject jsonObject = new JSONObject();
- try {
- jsonObject.put("status_code", statusCode);
- jsonObject.put("status_msg", statusMessage);
- jsonObject.put("timestamp", Instant.now().getEpochSecond());
- jsonObject.put("application", "host_life");
- JSONObject metrics = new JSONObject();
- metrics.put("uptime", upTime);
- metrics.put("alive", 1);
- jsonObject.put("metrics", metrics);
-
- } catch (JSONException e) {}
-
+ ObjectNode jsonObject = jsonMapper.createObjectNode();
+ jsonObject.put("status_code", statusCode);
+ jsonObject.put("status_msg", statusMessage);
+ jsonObject.put("timestamp", Instant.now().getEpochSecond());
+ jsonObject.put("application", "host_life");
+ ObjectNode metrics = jsonMapper.createObjectNode();
+ metrics.put("uptime", upTime);
+ metrics.put("alive", 1);
+ jsonObject.set("metrics", metrics);
return jsonObject;
}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java
deleted file mode 100644
index d22dd9d6f4b..00000000000
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.jdisc.state;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * A JSONObject that wraps the checked JSONException in a RuntimeException with a legible error message.
- *
- * @author gjoranv
- */
-class JSONObjectWithLegibleException extends JSONObject {
-
- @Override
- public JSONObject put(String s, boolean b) {
- try {
- return super.put(s, b);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, b, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, double v) {
- try {
- Double guardedVal = (((Double) v).isNaN() || ((Double) v).isInfinite()) ?
- 0.0 : v;
- return super.put(s, guardedVal);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, v, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, int i) {
- try {
- return super.put(s, i);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, i, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, long l) {
- try {
- return super.put(s, l);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, l, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, Collection collection) {
- try {
- return super.put(s, collection);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, collection, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, Map map) {
- try {
- return super.put(s, map);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, map, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, Object o) {
- try {
- return super.put(s, o);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, o, e), e);
- }
- }
-
- private String getErrorMessage(String key, Object value, JSONException e) {
- return "Trying to add invalid JSON object with key '" + key +
- "' and value '" + value + "' - " + e.getMessage();
- }
-
-}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java
new file mode 100644
index 00000000000..4c697fb5ada
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java
@@ -0,0 +1,15 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.state;
+
+/**
+ * @author bjorncs
+ */
+class JsonUtil {
+
+ private JsonUtil() {}
+
+ static double sanitizeDouble(double value) {
+ return (((Double) value).isNaN() || ((Double) value).isInfinite()) ? 0.0 : value;
+ }
+
+}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java
index 6a06a6362f5..add69403455 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java
@@ -1,7 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
import java.util.ArrayList;
import java.util.List;
@@ -13,9 +13,9 @@ import java.util.List;
*/
public class MetricGatherer {
- static List<JSONObject> getAdditionalMetrics() {
+ static List<JsonNode> getAdditionalMetrics() {
FileWrapper fileWrapper = new FileWrapper();
- List<JSONObject> packetList = new ArrayList<>();
+ List<JsonNode> packetList = new ArrayList<>();
packetList.add(CoredumpGatherer.gatherCoredumpMetrics(fileWrapper));
if (System.getProperty("os.name").contains("nux"))
packetList.add(HostLifeGatherer.getHostLifePacket(fileWrapper));
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
index c1a6f650a9c..824323af3d6 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
@@ -1,6 +1,11 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
import com.yahoo.collections.Tuple2;
import com.yahoo.component.provider.ComponentRegistry;
@@ -13,9 +18,6 @@ import com.yahoo.jdisc.handler.ResponseDispatch;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.HttpHeaders;
import com.yahoo.metrics.MetricsPresentationConfig;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -26,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import static com.yahoo.container.jdisc.state.JsonUtil.sanitizeDouble;
import static com.yahoo.container.jdisc.state.StateHandler.getSnapshotPreprocessor;
/**
@@ -44,6 +47,8 @@ import static com.yahoo.container.jdisc.state.StateHandler.getSnapshotPreprocess
*/
public class MetricsPacketsHandler extends AbstractRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
static final String APPLICATION_KEY = "application";
static final String TIMESTAMP_KEY = "timestamp";
static final String STATUS_CODE_KEY = "status_code";
@@ -97,19 +102,19 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
}
String output = jsonToString(getStatusPacket()) + getAllMetricsPackets() + "\n";
return output.getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
- private byte[] getMetricsArray() throws JSONException {
- JSONObject root = new JSONObject();
- JSONArray jsonArray = new JSONArray();
- jsonArray.put(getStatusPacket());
+ private byte[] getMetricsArray() throws JsonProcessingException {
+ ObjectNode root = jsonMapper.createObjectNode();
+ ArrayNode jsonArray = jsonMapper.createArrayNode();
+ jsonArray.add(getStatusPacket());
getPacketsForSnapshot(getSnapshot(), applicationName, timer.currentTimeMillis())
- .forEach(jsonArray::put);
- MetricGatherer.getAdditionalMetrics().forEach(jsonArray::put);
- root.put("metrics", jsonArray);
+ .forEach(jsonArray::add);
+ MetricGatherer.getAdditionalMetrics().forEach(jsonArray::add);
+ root.set("metrics", jsonArray);
return jsonToString(root)
.getBytes(StandardCharsets.UTF_8);
}
@@ -117,8 +122,8 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
/**
* Exactly one status packet is added to the response.
*/
- private JSONObject getStatusPacket() throws JSONException {
- JSONObject packet = new JSONObjectWithLegibleException();
+ private JsonNode getStatusPacket() {
+ ObjectNode packet = jsonMapper.createObjectNode();
packet.put(APPLICATION_KEY, applicationName);
StateMonitor.Status status = monitor.status();
@@ -127,14 +132,15 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
return packet;
}
- private String jsonToString(JSONObject jsonObject) throws JSONException {
- return jsonObject.toString(4);
+ private static String jsonToString(JsonNode jsonObject) throws JsonProcessingException {
+ return jsonMapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(jsonObject);
}
- private String getAllMetricsPackets() throws JSONException {
+ private String getAllMetricsPackets() throws JsonProcessingException {
StringBuilder ret = new StringBuilder();
- List<JSONObject> metricsPackets = getPacketsForSnapshot(getSnapshot(), applicationName, timer.currentTimeMillis());
- for (JSONObject packet : metricsPackets) {
+ List<JsonNode> metricsPackets = getPacketsForSnapshot(getSnapshot(), applicationName, timer.currentTimeMillis());
+ for (JsonNode packet : metricsPackets) {
ret.append(PACKET_SEPARATOR); // For legibility and parsing in unit tests
ret.append(jsonToString(packet));
}
@@ -143,22 +149,23 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
private MetricSnapshot getSnapshot() {
if (snapshotPreprocessor == null) {
- return monitor.snapshot();
+ // TODO: throw exception in ctor instead
+ return new MetricSnapshot(0L, 0L, TimeUnit.MILLISECONDS);
} else {
return snapshotPreprocessor.latestSnapshot();
}
}
- private List<JSONObject> getPacketsForSnapshot(MetricSnapshot metricSnapshot, String application, long timestamp) throws JSONException {
+ private List<JsonNode> getPacketsForSnapshot(MetricSnapshot metricSnapshot, String application, long timestamp) {
if (metricSnapshot == null) return Collections.emptyList();
- List<JSONObject> packets = new ArrayList<>();
+ List<JsonNode> packets = new ArrayList<>();
for (Map.Entry<MetricDimensions, MetricSet> snapshotEntry : metricSnapshot) {
MetricDimensions metricDimensions = snapshotEntry.getKey();
MetricSet metricSet = snapshotEntry.getValue();
- JSONObjectWithLegibleException packet = new JSONObjectWithLegibleException();
+ ObjectNode packet = jsonMapper.createObjectNode();
addMetaData(timestamp, application, packet);
addDimensions(metricDimensions, packet);
addMetrics(metricSet, packet);
@@ -167,25 +174,27 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
return packets;
}
- private void addMetaData(long timestamp, String application, JSONObjectWithLegibleException packet) {
+ private void addMetaData(long timestamp, String application, ObjectNode packet) {
packet.put(APPLICATION_KEY, application);
packet.put(TIMESTAMP_KEY, TimeUnit.MILLISECONDS.toSeconds(timestamp));
}
- private void addDimensions(MetricDimensions metricDimensions, JSONObjectWithLegibleException packet) throws JSONException {
+ private void addDimensions(MetricDimensions metricDimensions, ObjectNode packet) {
+ if (metricDimensions == null) return;
+
Iterator<Map.Entry<String, String>> dimensionsIterator = metricDimensions.iterator();
if (dimensionsIterator.hasNext()) {
- JSONObject jsonDim = new JSONObjectWithLegibleException();
- packet.put(DIMENSIONS_KEY, jsonDim);
+ ObjectNode jsonDim = jsonMapper.createObjectNode();
+ packet.set(DIMENSIONS_KEY, jsonDim);
for (Map.Entry<String, String> dimensionEntry : metricDimensions) {
jsonDim.put(dimensionEntry.getKey(), dimensionEntry.getValue());
}
}
}
- private void addMetrics(MetricSet metricSet, JSONObjectWithLegibleException packet) throws JSONException {
- JSONObjectWithLegibleException metrics = new JSONObjectWithLegibleException();
- packet.put(METRICS_KEY, metrics);
+ private void addMetrics(MetricSet metricSet, ObjectNode packet) {
+ ObjectNode metrics = jsonMapper.createObjectNode();
+ packet.set(METRICS_KEY, metrics);
for (Map.Entry<String, MetricValue> metric : metricSet) {
String name = metric.getKey();
MetricValue value = metric.getValue();
@@ -193,9 +202,9 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
metrics.put(name + ".count", ((CountMetric) value).getCount());
} else if (value instanceof GaugeMetric) {
GaugeMetric gauge = (GaugeMetric) value;
- metrics.put(name + ".average", gauge.getAverage())
- .put(name + ".last", gauge.getLast())
- .put(name + ".max", gauge.getMax());
+ metrics.put(name + ".average", sanitizeDouble(gauge.getAverage()))
+ .put(name + ".last", sanitizeDouble(gauge.getLast()))
+ .put(name + ".max", sanitizeDouble(gauge.getMax()));
if (gauge.getPercentiles().isPresent()) {
for (Tuple2<String, Double> prefixAndValue : gauge.getPercentiles().get()) {
metrics.put(name + "." + prefixAndValue.first + "percentile", prefixAndValue.second.doubleValue());
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java
index 8858465e846..5ac2871d9dd 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java
@@ -1,6 +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.container.jdisc.state;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
import com.yahoo.collections.Tuple2;
import com.yahoo.component.Vtag;
@@ -16,21 +21,18 @@ import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.HttpHeaders;
import com.yahoo.metrics.MetricsPresentationConfig;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
import java.net.URI;
import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
-import java.io.PrintStream;
-import java.io.ByteArrayOutputStream;
+
+import static com.yahoo.container.jdisc.state.JsonUtil.sanitizeDouble;
/**
* A handler which returns state (health) information from this container instance: Status, metrics and vespa version.
@@ -39,6 +41,8 @@ import java.io.ByteArrayOutputStream;
*/
public class StateHandler extends AbstractRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
public static final String STATE_API_ROOT = "/state/v1";
private static final String METRICS_PATH = "metrics";
private static final String HISTOGRAMS_PATH = "metrics/histograms";
@@ -63,11 +67,7 @@ public class StateHandler extends AbstractRequestHandler {
static SnapshotProvider getSnapshotPreprocessor(ComponentRegistry<SnapshotProvider> preprocessors, MetricsPresentationConfig presentation) {
List<SnapshotProvider> allPreprocessors = preprocessors.allComponents();
- if (presentation.slidingwindow() && allPreprocessors.size() > 0) {
- return allPreprocessors.get(0);
- } else {
- return null;
- }
+ return (allPreprocessors.size() > 0) ? allPreprocessors.get(0) : null;
}
@Override
@@ -128,17 +128,16 @@ public class StateHandler extends AbstractRequestHandler {
}
base.append(STATE_API_ROOT);
String uriBase = base.toString();
- JSONArray linkList = new JSONArray();
+ ArrayNode linkList = jsonMapper.createArrayNode();
for (String api : new String[] {METRICS_PATH, CONFIG_GENERATION_PATH, HEALTH_PATH, VERSION_PATH}) {
- JSONObject resource = new JSONObject();
+ ObjectNode resource = jsonMapper.createObjectNode();
resource.put("url", uriBase + "/" + api);
- linkList.put(resource);
+ linkList.add(resource);
}
- return new JSONObjectWithLegibleException()
- .put("resources", linkList)
- .toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
- throw new RuntimeException("Bad JSON construction.", e);
+ JsonNode resources = jsonMapper.createObjectNode().set("resources", linkList);
+ return toPrettyString(resources);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Bad JSON construction", e);
}
}
@@ -158,31 +157,31 @@ public class StateHandler extends AbstractRequestHandler {
private static byte[] buildConfigOutput(ApplicationMetadataConfig config) {
try {
- return new JSONObjectWithLegibleException()
- .put(CONFIG_GENERATION_PATH, new JSONObjectWithLegibleException()
- .put("generation", config.generation())
- .put("container", new JSONObjectWithLegibleException()
- .put("generation", config.generation())))
- .toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ return toPrettyString(
+ jsonMapper.createObjectNode()
+ .set(CONFIG_GENERATION_PATH, jsonMapper.createObjectNode()
+ .put("generation", config.generation())
+ .set("container", jsonMapper.createObjectNode()
+ .put("generation", config.generation()))));
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
private static byte[] buildVersionOutput() {
try {
- return new JSONObjectWithLegibleException()
- .put("version", Vtag.currentVersion)
- .toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ return toPrettyString(
+ jsonMapper.createObjectNode()
+ .put("version", Vtag.currentVersion.toString()));
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
private byte[] buildMetricOutput(String consumer) {
try {
- return buildJsonForConsumer(consumer).toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ return toPrettyString(buildJsonForConsumer(consumer));
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
@@ -195,17 +194,18 @@ public class StateHandler extends AbstractRequestHandler {
return baos.toByteArray();
}
- private JSONObjectWithLegibleException buildJsonForConsumer(String consumer) throws JSONException {
- JSONObjectWithLegibleException ret = new JSONObjectWithLegibleException();
+ private ObjectNode buildJsonForConsumer(String consumer) {
+ ObjectNode ret = jsonMapper.createObjectNode();
ret.put("time", timer.currentTimeMillis());
- ret.put("status", new JSONObjectWithLegibleException().put("code", getStatus().name()));
- ret.put(METRICS_PATH, buildJsonForSnapshot(consumer, getSnapshot()));
+ ret.set("status", jsonMapper.createObjectNode().put("code", getStatus().name()));
+ ret.set(METRICS_PATH, buildJsonForSnapshot(consumer, getSnapshot()));
return ret;
}
private MetricSnapshot getSnapshot() {
if (snapshotPreprocessor == null) {
- return monitor.snapshot();
+ // TODO: throw exception in ctor instead
+ return new MetricSnapshot(0L, 0L, TimeUnit.MILLISECONDS);
} else {
return snapshotPreprocessor.latestSnapshot();
}
@@ -215,55 +215,62 @@ public class StateHandler extends AbstractRequestHandler {
return monitor.status();
}
- private JSONObjectWithLegibleException buildJsonForSnapshot(String consumer, MetricSnapshot metricSnapshot) throws JSONException {
+ private ObjectNode buildJsonForSnapshot(String consumer, MetricSnapshot metricSnapshot) {
if (metricSnapshot == null) {
- return new JSONObjectWithLegibleException();
+ return jsonMapper.createObjectNode();
}
- JSONObjectWithLegibleException jsonMetric = new JSONObjectWithLegibleException();
- jsonMetric.put("snapshot", new JSONObjectWithLegibleException()
- .put("from", metricSnapshot.getFromTime(TimeUnit.MILLISECONDS) / 1000.0)
- .put("to", metricSnapshot.getToTime(TimeUnit.MILLISECONDS) / 1000.0));
+ ObjectNode jsonMetric = jsonMapper.createObjectNode();
+ jsonMetric.set("snapshot", jsonMapper.createObjectNode()
+ .put("from", sanitizeDouble(metricSnapshot.getFromTime(TimeUnit.MILLISECONDS) / 1000.0))
+ .put("to", sanitizeDouble(metricSnapshot.getToTime(TimeUnit.MILLISECONDS) / 1000.0)));
boolean includeDimensions = !consumer.equals(HEALTH_PATH);
long periodInMillis = metricSnapshot.getToTime(TimeUnit.MILLISECONDS) -
metricSnapshot.getFromTime(TimeUnit.MILLISECONDS);
for (Tuple tuple : collapseMetrics(metricSnapshot, consumer)) {
- JSONObjectWithLegibleException jsonTuple = new JSONObjectWithLegibleException();
+ ObjectNode jsonTuple = jsonMapper.createObjectNode();
jsonTuple.put("name", tuple.key);
if (tuple.val instanceof CountMetric) {
CountMetric count = (CountMetric)tuple.val;
- jsonTuple.put("values", new JSONObjectWithLegibleException()
+ jsonTuple.set("values", jsonMapper.createObjectNode()
.put("count", count.getCount())
- .put("rate", (count.getCount() * 1000.0) / periodInMillis));
+ .put("rate", sanitizeDouble(count.getCount() * 1000.0) / periodInMillis));
} else if (tuple.val instanceof GaugeMetric) {
GaugeMetric gauge = (GaugeMetric) tuple.val;
- JSONObjectWithLegibleException valueFields = new JSONObjectWithLegibleException();
- valueFields.put("average", gauge.getAverage())
- .put("sum", gauge.getSum())
+ ObjectNode valueFields = jsonMapper.createObjectNode();
+ valueFields.put("average", sanitizeDouble(gauge.getAverage()))
+ .put("sum", sanitizeDouble(gauge.getSum()))
.put("count", gauge.getCount())
- .put("last", gauge.getLast())
- .put("max", gauge.getMax())
- .put("min", gauge.getMin())
- .put("rate", (gauge.getCount() * 1000.0) / periodInMillis);
+ .put("last", sanitizeDouble(gauge.getLast()))
+ .put("max", sanitizeDouble(gauge.getMax()))
+ .put("min", sanitizeDouble(gauge.getMin()))
+ .put("rate", sanitizeDouble((gauge.getCount() * 1000.0) / periodInMillis));
if (gauge.getPercentiles().isPresent()) {
for (Tuple2<String, Double> prefixAndValue : gauge.getPercentiles().get()) {
- valueFields.put(prefixAndValue.first + "percentile", prefixAndValue.second.doubleValue());
+ valueFields.put(prefixAndValue.first + "percentile", sanitizeDouble(prefixAndValue.second));
}
}
- jsonTuple.put("values", valueFields);
+ jsonTuple.set("values", valueFields);
} else {
throw new UnsupportedOperationException(tuple.val.getClass().getName());
}
- Iterator<Map.Entry<String, String>> it = tuple.dim.iterator();
- if (it.hasNext() && includeDimensions) {
- JSONObjectWithLegibleException jsonDim = new JSONObjectWithLegibleException();
- while (it.hasNext()) {
- Map.Entry<String, String> entry = it.next();
- jsonDim.put(entry.getKey(), entry.getValue());
+ if (tuple.dim != null) {
+ Iterator<Map.Entry<String, String>> it = tuple.dim.iterator();
+ if (it.hasNext() && includeDimensions) {
+ ObjectNode jsonDim = jsonMapper.createObjectNode();
+ while (it.hasNext()) {
+ Map.Entry<String, String> entry = it.next();
+ jsonDim.put(entry.getKey(), entry.getValue());
+ }
+ jsonTuple.set("dimensions", jsonDim);
}
- jsonTuple.put("dimensions", jsonDim);
}
- jsonMetric.append("values", jsonTuple);
+ ArrayNode values = (ArrayNode) jsonMetric.get("values");
+ if (values == null) {
+ values = jsonMapper.createArrayNode();
+ jsonMetric.set("values", values);
+ }
+ values.add(jsonTuple);
}
return jsonMetric;
}
@@ -317,6 +324,12 @@ public class StateHandler extends AbstractRequestHandler {
return metrics;
}
+ private static byte[] toPrettyString(JsonNode resources) throws JsonProcessingException {
+ return jsonMapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(resources)
+ .getBytes();
+ }
+
static class Tuple {
final MetricDimensions dim;
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java
deleted file mode 100644
index dfa791304e0..00000000000
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMetricConsumer.java
+++ /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.
-package com.yahoo.container.jdisc.state;
-
-import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.application.MetricConsumer;
-
-import java.util.Map;
-
-/**
- * @author Simon Thoresen Hult
- */
-final class StateMetricConsumer implements MetricConsumer {
-
- final static Metric.Context NULL_CONTEXT = StateMetricContext.newInstance(null);
- private final Object lock = new Object();
- private MetricSnapshot metricSnapshot = new MetricSnapshot();
-
- @Override
- public void set(String key, Number val, Metric.Context ctx) {
- synchronized (lock) {
- metricSnapshot.set(dimensionsOrDefault(ctx), key, val);
- }
- }
-
- private MetricDimensions dimensionsOrDefault(Metric.Context ctx) {
- return (MetricDimensions)(ctx != null ? ctx : NULL_CONTEXT);
- }
-
- @Override
- public void add(String key, Number val, Metric.Context ctx) {
- synchronized (lock) {
- metricSnapshot.add(dimensionsOrDefault(ctx), key, val);
- }
- }
-
- @Override
- public Metric.Context createContext(Map<String, ?> properties) {
- return StateMetricContext.newInstance(properties);
- }
-
- MetricSnapshot createSnapshot() {
- MetricSnapshot metricSnapshot;
- synchronized (lock) {
- metricSnapshot = this.metricSnapshot;
- this.metricSnapshot = this.metricSnapshot.createSnapshot();
- }
- return metricSnapshot;
- }
-
-}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java
index 40a0ef10fbc..027ce02a2aa 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java
@@ -4,25 +4,12 @@ package com.yahoo.container.jdisc.state;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
import com.yahoo.container.jdisc.config.HealthMonitorConfig;
-import com.yahoo.jdisc.Timer;
-import com.yahoo.jdisc.application.MetricConsumer;
-import com.yahoo.jdisc.core.SystemTimer;
-import java.util.Map;
-import java.util.Optional;
-import java.util.TreeSet;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * A state monitor keeps track of the current health and metrics state of a container.
- * It is used by jDisc to hand out metric update API endpoints to workers through {@link #newMetricConsumer},
- * and to inspect the current accumulated state of metrics through {@link #snapshot}.
+ * A state monitor keeps track of the current health state of a container.
*
* @author Simon Thoresen Hult
*/
@@ -32,44 +19,15 @@ public class StateMonitor extends AbstractComponent {
public enum Status {up, down, initializing}
- private final CopyOnWriteArrayList<StateMetricConsumer> consumers = new CopyOnWriteArrayList<>();
- private final Optional<ScheduledExecutorService> executor;
- private final Timer timer;
- private final long snapshotIntervalMs;
- private volatile long lastSnapshotTimeMs;
- private volatile MetricSnapshot snapshot;
private volatile Status status;
- private final TreeSet<String> valueNames = new TreeSet<>();
@Inject
- public StateMonitor(HealthMonitorConfig config, Timer timer) {
- this(config, timer, runnable -> {
- Thread thread = new Thread(runnable, "StateMonitor");
- thread.setDaemon(true);
- return thread;
- });
- }
-
- public static StateMonitor createForTesting() {
- return new StateMonitor(new HealthMonitorConfig.Builder().build(), new SystemTimer());
- }
-
- /** Non-private only for unit testing this class. */
- StateMonitor(HealthMonitorConfig config, Timer timer, ThreadFactory threadFactory) {
- this.timer = timer;
- this.snapshotIntervalMs = (long)(config.snapshot_interval() * TimeUnit.SECONDS.toMillis(1));
- this.lastSnapshotTimeMs = timer.currentTimeMillis();
+ public StateMonitor(HealthMonitorConfig config) {
this.status = Status.valueOf(config.initialStatus());
- this.executor = Optional.ofNullable(threadFactory).map(Executors::newSingleThreadScheduledExecutor);
- this.executor.ifPresent(exec -> exec.scheduleAtFixedRate(this::updateSnapshot, snapshotIntervalMs,
- snapshotIntervalMs, TimeUnit.MILLISECONDS));
}
- /** Returns a metric consumer for jDisc which will write metrics back to this */
- public MetricConsumer newMetricConsumer() {
- StateMetricConsumer consumer = new StateMetricConsumer();
- consumers.add(consumer);
- return consumer;
+ public static StateMonitor createForTesting() {
+ return new StateMonitor(new HealthMonitorConfig.Builder().build());
}
public void status(Status status) {
@@ -81,60 +39,4 @@ public class StateMonitor extends AbstractComponent {
public Status status() { return status; }
- /** Returns the last snapshot taken of the metrics in this system */
- public MetricSnapshot snapshot() {
- return snapshot;
- }
-
- /** Returns the interval between each metrics snapshot used by this */
- public long getSnapshotIntervalMillis() { return snapshotIntervalMs; }
-
- /** NOTE: Non-private for unit testing only. **/
- void updateSnapshot() {
- long now = timer.currentTimeMillis();
- snapshot = createSnapshot(lastSnapshotTimeMs, now);
- lastSnapshotTimeMs = now;
- }
-
- private MetricSnapshot createSnapshot(long fromMillis, long toMillis) {
- MetricSnapshot snapshot = new MetricSnapshot(fromMillis, toMillis, TimeUnit.MILLISECONDS);
- for (StateMetricConsumer consumer : consumers) {
- snapshot.add(consumer.createSnapshot());
- }
- updateNames(snapshot);
- return snapshot;
- }
-
- private void updateNames(MetricSnapshot current) {
- TreeSet<String> seen = new TreeSet<>();
- for (Map.Entry<MetricDimensions, MetricSet> dimensionAndMetric : current) {
- for (Map.Entry<String, MetricValue> nameAndMetric : dimensionAndMetric.getValue()) {
- seen.add(nameAndMetric.getKey());
- }
- }
- synchronized (valueNames) {
- for (String name : valueNames) {
- if (!seen.contains(name)) {
- current.add((MetricDimensions) StateMetricConsumer.NULL_CONTEXT, name, 0);
- }
- }
- valueNames.addAll(seen);
- }
- }
-
- @Override
- public void deconstruct() {
- executor.ifPresent(exec -> {
- exec.shutdown();
- try {
- exec.awaitTermination(5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- }
-
- if (!exec.isTerminated()) {
- log.warning("StateMonitor failed to terminate within 5 seconds of interrupt signal. Ignoring.");
- }
- });
- }
-
}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/package-info.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/package-info.java
index 4d83d69a402..52b7af2a02c 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/package-info.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/package-info.java
@@ -1,12 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+/**
+ * Metrics implementation for jDisc. This consumes metrics over the jDisc metric API and
+ * makes these available for in-process consumption, and off-process through a jDisc handler.
+ */
@ExportPackage
package com.yahoo.container.jdisc.state;
import com.yahoo.osgi.annotation.ExportPackage;
-
-/**
- * Metrics implementation for jDisc. This consumes metrics over the jDisc metric API
- * and makes these available for in-process consumption through
- * {@link com.yahoo.container.jdisc.state.StateMonitor#snapshot},
- * and off-process through a jDisc handler.
- */
diff --git a/container-core/src/main/java/org/json/package-info.java b/container-core/src/main/java/org/json/package-info.java
index 44630ad235a..7ca9fe91e31 100644
--- a/container-core/src/main/java/org/json/package-info.java
+++ b/container-core/src/main/java/org/json/package-info.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
package org.json;
-
+// TODO Vespa 8: stop providing org.json
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-core/src/main/resources/configdefinitions/metrics.metrics-presentation.def b/container-core/src/main/resources/configdefinitions/metrics.metrics-presentation.def
index b6c40993ef5..aeb597ed326 100644
--- a/container-core/src/main/resources/configdefinitions/metrics.metrics-presentation.def
+++ b/container-core/src/main/resources/configdefinitions/metrics.metrics-presentation.def
@@ -2,6 +2,5 @@
namespace=metrics
-## Sliding window means present the last n minutes of data, as opposed to
-## presenting the newest completed n minute interval.
+## TODO Vespa 8: remove
slidingwindow bool default=true
diff --git a/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java b/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java
index 9020ed91026..ca4bec30322 100644
--- a/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java
+++ b/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java
@@ -1,17 +1,18 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.handler.metrics;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import java.io.BufferedReader;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Executors;
@@ -34,6 +35,8 @@ import static org.junit.Assert.fail;
*/
public class MetricsV2HandlerTest {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final String URI_BASE = "http://localhost";
private static final String V2_URI = URI_BASE + V2_PATH;
@@ -79,29 +82,29 @@ public class MetricsV2HandlerTest {
@Test
public void v2_response_contains_values_uri() throws Exception {
String response = testDriver.sendRequest(V2_URI).readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("resources"));
- JSONArray resources = root.getJSONArray("resources");
- assertEquals(1, resources.length());
+ ArrayNode resources = (ArrayNode) root.get("resources");
+ assertEquals(1, resources.size());
- JSONObject valuesUri = resources.getJSONObject(0);
- assertEquals(VALUES_URI, valuesUri.getString("url"));
+ JsonNode valuesUri = resources.get(0);
+ assertEquals(VALUES_URI, valuesUri.get("url").textValue());
}
@Ignore
@Test
- public void visually_inspect_values_response() throws Exception {
- JSONObject responseJson = getResponseAsJson(null);
- System.out.println(responseJson.toString(4));
+ public void visually_inspect_values_response() {
+ JsonNode responseJson = getResponseAsJson(null);
+ System.out.println(responseJson);
}
@Test
public void invalid_path_yields_error_response() throws Exception {
String response = testDriver.sendRequest(V2_URI + "/invalid").readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("error"));
- assertTrue(root.getString("error" ).startsWith("No content"));
+ assertTrue(root.get("error" ).textValue().startsWith("No content"));
}
@Test
@@ -111,23 +114,23 @@ public class MetricsV2HandlerTest {
}
@Test
- public void consumer_is_propagated_to_metrics_proxy_api() throws JSONException {
- JSONObject responseJson = getResponseAsJson(CUSTOM_CONSUMER);
+ public void consumer_is_propagated_to_metrics_proxy_api() {
+ JsonNode responseJson = getResponseAsJson(CUSTOM_CONSUMER);
- JSONObject firstNodeMetricsValues =
- responseJson.getJSONArray("nodes").getJSONObject(0)
- .getJSONObject("node")
- .getJSONArray("metrics").getJSONObject(0)
- .getJSONObject("values");
+ JsonNode firstNodeMetricsValues =
+ responseJson.get("nodes").get(0)
+ .get("node")
+ .get("metrics").get(0)
+ .get("values");
assertTrue(firstNodeMetricsValues.has(REPLACED_CPU_METRIC));
}
- private JSONObject getResponseAsJson(String consumer) {
+ private JsonNode getResponseAsJson(String consumer) {
String response = testDriver.sendRequest(VALUES_URI + consumerQuery(consumer)).readAll();
try {
- return new JSONObject(response);
- } catch (JSONException e) {
+ return jsonMapper.readTree(response);
+ } catch (IOException e) {
fail("Failed to create json object: " + e.getMessage());
throw new RuntimeException(e);
}
diff --git a/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java b/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java
index a0e8c131c2b..9ffce6d1c28 100644
--- a/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java
+++ b/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java
@@ -1,15 +1,11 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.handler.metrics;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.stream.Collectors;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -26,13 +22,14 @@ import static com.yahoo.container.handler.metrics.MetricsV2Handler.consumerQuery
import static com.yahoo.container.handler.metrics.MetricsV2HandlerTest.getFileContents;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
/**
* @author gjoranv
*/
public class PrometheusV1HandlerTest {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final String URI_BASE = "http://localhost";
private static final String V1_URI = URI_BASE + PrometheusV1Handler.V1_PATH;
@@ -79,14 +76,14 @@ public class PrometheusV1HandlerTest {
@Test
public void v1_response_contains_values_uri() throws Exception {
String response = testDriver.sendRequest(V1_URI).readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("resources"));
- JSONArray resources = root.getJSONArray("resources");
- assertEquals(1, resources.length());
+ ArrayNode resources = (ArrayNode) root.get("resources");
+ assertEquals(1, resources.size());
- JSONObject valuesUri = resources.getJSONObject(0);
- assertEquals(VALUES_URI, valuesUri.getString("url"));
+ JsonNode valuesUri = resources.get(0);
+ assertEquals(VALUES_URI, valuesUri.get("url").asText());
}
@Ignore
@@ -99,9 +96,9 @@ public class PrometheusV1HandlerTest {
@Test
public void invalid_path_yields_error_response() throws Exception {
String response = testDriver.sendRequest(V1_URI + "/invalid").readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("error"));
- assertTrue(root.getString("error" ).startsWith("No content"));
+ assertTrue(root.get("error" ).textValue().startsWith("No content"));
}
@Test
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java
index c1f7d790fa5..8a3d0e837c5 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java
@@ -1,8 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import java.nio.file.Path;
@@ -17,12 +16,12 @@ import static org.junit.Assert.assertEquals;
public class CoredumpGathererTest {
@Test
- public void finds_one_coredump() throws JSONException {
- JSONObject packet = CoredumpGatherer.gatherCoredumpMetrics(new MockFileWrapper());
+ public void finds_one_coredump() {
+ JsonNode packet = CoredumpGatherer.gatherCoredumpMetrics(new MockFileWrapper());
- assertEquals("system-coredumps-processing", packet.getString("application"));
- assertEquals(1, packet.getInt("status_code"));
- assertEquals("Found 1 coredump(s)", packet.getString("status_msg"));
+ assertEquals("system-coredumps-processing", packet.get("application").textValue());
+ assertEquals(1, packet.get("status_code").intValue());
+ assertEquals("Found 1 coredump(s)", packet.get("status_msg").textValue());
}
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java
index d025b9662d2..12852c9d54c 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java
@@ -1,8 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import java.nio.file.Path;
@@ -16,15 +15,15 @@ import static org.junit.Assert.assertEquals;
public class HostLifeGathererTest {
@Test
- public void host_is_alive() throws JSONException {
- JSONObject packet = HostLifeGatherer.getHostLifePacket(new MockFileWrapper());
- JSONObject metrics = packet.getJSONObject("metrics");
- assertEquals("host_life", packet.getString("application"));
- assertEquals(0, packet.getInt("status_code"));
- assertEquals("OK", packet.getString("status_msg"));
-
- assertEquals(123l, metrics.getLong("uptime"));
- assertEquals(1, metrics.getInt("alive"));
+ public void host_is_alive() {
+ JsonNode packet = HostLifeGatherer.getHostLifePacket(new MockFileWrapper());
+ JsonNode metrics = packet.get("metrics");
+ assertEquals("host_life", packet.get("application").textValue());
+ assertEquals(0, packet.get("status_code").intValue());
+ assertEquals("OK", packet.get("status_msg").textValue());
+
+ assertEquals(123L, metrics.get("uptime").longValue());
+ assertEquals(1, metrics.get("alive").intValue());
}
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java
deleted file mode 100644
index 777f43c5607..00000000000
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricConsumerProviders.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.jdisc.state;
-
-import com.google.inject.Provider;
-import com.yahoo.jdisc.application.MetricConsumer;
-
-/**
- * @author Simon Thoresen Hult
- */
-class MetricConsumerProviders {
-
- public static Provider<MetricConsumer> wrap(final StateMonitor statetMonitor) {
- return new Provider<MetricConsumer>() {
-
- @Override
- public MetricConsumer get() {
- return statetMonitor.newMetricConsumer();
- }
- };
- }
-}
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java
index ef700597537..89bde3eeb5b 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java
@@ -3,12 +3,13 @@ package com.yahoo.container.jdisc.state;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.yahoo.jdisc.Metric;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.Map;
import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.APPLICATION_KEY;
import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.DIMENSIONS_KEY;
@@ -25,9 +26,21 @@ import static org.junit.Assert.assertTrue;
*/
public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
+ private static final String APPLICATION_NAME = "state-handler-test-base";
+
+ private static MetricsPacketsHandler metricsPacketsHandler;
+
+ @Before
+ public void setupHandler() {
+ metricsPacketsHandlerConfig = new MetricsPacketsHandlerConfig(new MetricsPacketsHandlerConfig.Builder()
+ .application(APPLICATION_NAME));
+ metricsPacketsHandler = new MetricsPacketsHandler(monitor, timer, snapshotProviderRegistry,
+ metricsPresentationConfig, metricsPacketsHandlerConfig);
+ testDriver = new RequestHandlerTestDriver(metricsPacketsHandler);
+ }
+
@Test
- public void only_status_packet_is_returned_prior_to_first_snapshot() throws Exception {
- metric.add("not_included", 1, null);
+ public void status_packet_is_returned_prior_to_first_snapshot() throws Exception {
String response = requestAsString("http://localhost/metrics-packets");
List<JsonNode> packets = toJsonPackets(response);
@@ -41,7 +54,7 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
@Test
public void metrics_are_included_after_snapshot() throws Exception {
- metric.add("counter", 1, null);
+ createSnapshotWithCountMetric("counter", 1, null);
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
assertEquals(2, packets.size());
@@ -51,7 +64,7 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
@Test
public void metadata_is_included_in_each_metrics_packet() throws Exception {
- metric.add("counter", 1, null);
+ createSnapshotWithCountMetric("counter", 1, null);
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
JsonNode counterPacket = packets.get(1);
@@ -62,7 +75,7 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
@Test
public void timestamp_resolution_is_in_seconds() throws Exception {
- metric.add("counter", 1, null);
+ createSnapshotWithCountMetric("counter", 1, null);
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
JsonNode counterPacket = packets.get(1);
@@ -71,8 +84,10 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
@Test
public void expected_aggregators_are_output_for_gauge_metrics() throws Exception{
- Metric.Context context = metric.createContext(Collections.singletonMap("dim1", "value1"));
- metric.set("gauge", 0.2, null);
+ var context = StateMetricContext.newInstance(Map.of("dim1", "value1"));
+ var snapshot = new MetricSnapshot();
+ snapshot.set(context, "gauge", 0.2);
+ snapshotProvider.setSnapshot(snapshot);
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
JsonNode gaugeMetric = packets.get(1).get(METRICS_KEY);
@@ -84,8 +99,8 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
@Test
public void dimensions_from_context_are_included() throws Exception {
- Metric.Context context = metric.createContext(Collections.singletonMap("dim1", "value1"));
- metric.add("counter", 1, context);
+ var context = StateMetricContext.newInstance(Map.of("dim1", "value1"));
+ createSnapshotWithCountMetric("counter", 1, context);
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
JsonNode counterPacket = packets.get(1);
@@ -97,9 +112,11 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
@Test
public void metrics_with_identical_dimensions_are_contained_in_the_same_packet() throws Exception {
- Metric.Context context = metric.createContext(Collections.singletonMap("dim1", "value1"));
- metric.add("counter1", 1, context);
- metric.add("counter2", 2, context);
+ var context = StateMetricContext.newInstance(Map.of("dim1", "value1"));
+ var snapshot = new MetricSnapshot();
+ snapshot.add(context, "counter1", 1);
+ snapshot.add(context, "counter2", 2);
+ snapshotProvider.setSnapshot(snapshot);
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
assertEquals(2, packets.size());
@@ -112,10 +129,12 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
@Test
public void metrics_with_different_dimensions_get_separate_packets() throws Exception {
- Metric.Context context1 = metric.createContext(Collections.singletonMap("dim1", "value1"));
- Metric.Context context2 = metric.createContext(Collections.singletonMap("dim2", "value2"));
- metric.add("counter1", 1, context1);
- metric.add("counter2", 1, context2);
+ var context1 = StateMetricContext.newInstance(Map.of("dim1", "value1"));
+ var context2 = StateMetricContext.newInstance(Map.of("dim2", "value2"));
+ var snapshot = new MetricSnapshot();
+ snapshot.add(context1, "counter1", 1);
+ snapshot.add(context2, "counter2", 2);
+ snapshotProvider.setSnapshot(snapshot);
List<JsonNode> packets = incrementTimeAndGetJsonPackets();
assertEquals(3, packets.size());
@@ -145,4 +164,10 @@ public class MetricsPacketsHandlerTest extends StateHandlerTestBase {
assertEquals(expected, counterMetrics.get(metricName).asLong());
}
+ private void createSnapshotWithCountMetric(String name, Number value, MetricDimensions context) {
+ var snapshot = new MetricSnapshot();
+ snapshot.add(context, name, value);
+ snapshotProvider.setSnapshot(snapshot);
+ }
+
}
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/MockSnapshotProvider.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/MockSnapshotProvider.java
new file mode 100644
index 00000000000..5cc65be855d
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/MockSnapshotProvider.java
@@ -0,0 +1,23 @@
+package com.yahoo.container.jdisc.state;
+
+import java.io.PrintStream;
+
+/**
+ * @author gjoranv
+ */
+public class MockSnapshotProvider implements SnapshotProvider {
+
+ private MetricSnapshot snapshot;
+
+ public void setSnapshot(MetricSnapshot snapshot) {
+ this.snapshot = snapshot;
+ }
+
+ @Override
+ public MetricSnapshot latestSnapshot() {
+ return snapshot;
+ }
+
+ @Override
+ public void histogram(PrintStream output) { }
+}
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java
index 26a2f817acc..385eb627427 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java
@@ -3,14 +3,13 @@ package com.yahoo.container.jdisc.state;
import com.fasterxml.jackson.databind.JsonNode;
import com.yahoo.component.Vtag;
-import com.yahoo.jdisc.Metric;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
import com.yahoo.vespa.defaults.Defaults;
+import org.junit.Before;
import org.junit.Test;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
-import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -21,237 +20,63 @@ import static org.junit.Assert.assertTrue;
*/
public class StateHandlerTest extends StateHandlerTestBase {
+ private static final String V1_URI = URI_BASE + "/state/v1/";
+ private static StateHandler stateHandler;
+
+ @Before
+ public void setupHandler() {
+ stateHandler = new StateHandler(monitor, timer, applicationMetadataConfig, snapshotProviderRegistry , metricsPresentationConfig);
+ testDriver = new RequestHandlerTestDriver(stateHandler);
+ }
+
@Test
- public void testReportPriorToFirstSnapshot() throws Exception {
- metric.add("foo", 1, null);
- metric.set("bar", 4, null);
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
+ public void testStatusReportPriorToFirstSnapshot() throws Exception {
+ JsonNode json = requestAsJson(V1_URI + "/all");
assertEquals(json.toString(), "up", json.get("status").get("code").asText());
assertFalse(json.toString(), json.get("metrics").has("values"));
}
@Test
public void testReportIncludesMetricsAfterSnapshot() throws Exception {
- metric.add("foo", 1, null);
- metric.set("bar", 4, null);
- advanceToNextSnapshot();
- JsonNode json1 = requestAsJson("http://localhost/state/v1/metrics");
+ var snapshot = new MetricSnapshot();
+ snapshot.add(null, "foo", 1);
+ snapshot.set(null, "bar", 4);
+ snapshotProvider.setSnapshot(snapshot);
+
+ JsonNode json1 = requestAsJson(V1_URI + "metrics");
assertEquals(json1.toString(), "up", json1.get("status").get("code").asText());
assertEquals(json1.toString(), 2, json1.get("metrics").get("values").size());
-
- metric.add("fuz", 1, metric.createContext(new HashMap<>(0)));
- advanceToNextSnapshot();
- JsonNode json2 = requestAsJson("http://localhost/state/v1/metrics");
- assertEquals(json2.toString(), "up", json2.get("status").get("code").asText());
- assertEquals(json2.toString(), 3, json2.get("metrics").get("values").size());
- }
-
- /**
- * Tests that we restart an metric when it changes type from gauge to counter or back.
- * This may happen in practice on config reloads.
- */
- @Test
- public void testMetricTypeChangeIsAllowed() {
- String metricName = "myMetric";
- Metric.Context metricContext = null;
-
- {
- // Add a count metric
- metric.add(metricName, 1, metricContext);
- metric.add(metricName, 2, metricContext);
- // Change it to a gauge metric
- metric.set(metricName, 9, metricContext);
- advanceToNextSnapshot();
- MetricValue resultingMetric = monitor.snapshot().iterator().next().getValue().get(metricName);
- assertEquals(GaugeMetric.class, resultingMetric.getClass());
- assertEquals("Value was reset and produces the last gauge value",
- 9.0, ((GaugeMetric) resultingMetric).getLast(), 0.000001);
- }
-
- {
- // Add a gauge metric
- metric.set(metricName, 9, metricContext);
- // Change it to a count metric
- metric.add(metricName, 1, metricContext);
- metric.add(metricName, 2, metricContext);
- advanceToNextSnapshot();
- MetricValue resultingMetric = monitor.snapshot().iterator().next().getValue().get(metricName);
- assertEquals(CountMetric.class, resultingMetric.getClass());
- assertEquals("Value was reset, and changed to add semantics giving 1+2",
- 3, ((CountMetric) resultingMetric).getCount());
- }
- }
-
- @Test
- public void testAverageAggregationOfValues() throws Exception {
- metric.set("bar", 4, null);
- metric.set("bar", 5, null);
- metric.set("bar", 7, null);
- metric.set("bar", 2, null);
- advanceToNextSnapshot();
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- assertEquals(json.toString(), "up", json.get("status").get("code").asText());
- assertEquals(json.toString(), 1, json.get("metrics").get("values").size());
- assertEquals(json.toString(), 4.5,
- json.get("metrics").get("values").get(0).get("values").get("average").asDouble(), 0.001);
}
@Test
- public void testSumAggregationOfCounts() throws Exception {
- metric.add("foo", 1, null);
- metric.add("foo", 1, null);
- metric.add("foo", 2, null);
- metric.add("foo", 1, null);
- advanceToNextSnapshot();
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- assertEquals(json.toString(), "up", json.get("status").get("code").asText());
- assertEquals(json.toString(), 1, json.get("metrics").get("values").size());
- assertEquals(json.toString(), 5,
- json.get("metrics").get("values").get(0).get("values").get("count").asDouble(), 0.001);
- }
+ public void testCountMetricCount() throws Exception {
+ var snapshot = new MetricSnapshot();
+ snapshot.add(null, "foo", 4);
+ snapshot.add(null, "foo", 2);
+ snapshotProvider.setSnapshot(snapshot);
- @Test
- public void testReadabilityOfJsonReport() throws Exception {
- metric.add("foo", 1, null);
- advanceToNextSnapshot();
- assertEquals("{\n" +
- " \"metrics\": {\n" +
- " \"snapshot\": {\n" +
- " \"from\": 0,\n" +
- " \"to\": 300\n" +
- " },\n" +
- " \"values\": [{\n" +
- " \"name\": \"foo\",\n" +
- " \"values\": {\n" +
- " \"count\": 1,\n" +
- " \"rate\": 0.0033333333333333335\n" +
- " }\n" +
- " }]\n" +
- " },\n" +
- " \"status\": {\"code\": \"up\"},\n" +
- " \"time\": 300000\n" +
- "}",
- requestAsString("http://localhost/state/v1/all"));
-
- Metric.Context ctx = metric.createContext(Collections.singletonMap("component", "test"));
- metric.set("bar", 2, ctx);
- metric.set("bar", 3, ctx);
- metric.set("bar", 4, ctx);
- metric.set("bar", 5, ctx);
- advanceToNextSnapshot();
- assertEquals("{\n" +
- " \"metrics\": {\n" +
- " \"snapshot\": {\n" +
- " \"from\": 300,\n" +
- " \"to\": 600\n" +
- " },\n" +
- " \"values\": [\n" +
- " {\n" +
- " \"name\": \"foo\",\n" +
- " \"values\": {\n" +
- " \"count\": 0,\n" +
- " \"rate\": 0\n" +
- " }\n" +
- " },\n" +
- " {\n" +
- " \"dimensions\": {\"component\": \"test\"},\n" +
- " \"name\": \"bar\",\n" +
- " \"values\": {\n" +
- " \"average\": 3.5,\n" +
- " \"count\": 4,\n" +
- " \"last\": 5,\n" +
- " \"max\": 5,\n" +
- " \"min\": 2,\n" +
- " \"rate\": 0.013333333333333334,\n" +
- " \"sum\": 14\n" +
- " }\n" +
- " }\n" +
- " ]\n" +
- " },\n" +
- " \"status\": {\"code\": \"up\"},\n" +
- " \"time\": 600000\n" +
- "}",
- requestAsString("http://localhost/state/v1/all"));
- }
-
- @Test
- public void testNotAggregatingCountsBeyondSnapshots() throws Exception {
- metric.add("foo", 1, null);
- metric.add("foo", 1, null);
- advanceToNextSnapshot();
- metric.add("foo", 2, null);
- metric.add("foo", 1, null);
- advanceToNextSnapshot();
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
+ JsonNode json = requestAsJson(V1_URI + "all");
assertEquals(json.toString(), "up", json.get("status").get("code").asText());
assertEquals(json.toString(), 1, json.get("metrics").get("values").size());
- assertEquals(json.toString(), 3,
+ assertEquals(json.toString(), 6,
json.get("metrics").get("values").get(0).get("values").get("count").asDouble(), 0.001);
}
@Test
- public void testSnapshottingTimes() throws Exception {
- metric.add("foo", 1, null);
- metric.set("bar", 3, null);
- // At this time we should not have done any snapshotting
- {
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- assertFalse(json.toString(), json.get("metrics").has("snapshot"));
- }
- // At this time first snapshot should have been generated
- advanceToNextSnapshot();
- {
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- assertTrue(json.toString(), json.get("metrics").has("snapshot"));
- assertEquals(0.0, json.get("metrics").get("snapshot").get("from").asDouble(), 0.00001);
- assertEquals(300.0, json.get("metrics").get("snapshot").get("to").asDouble(), 0.00001);
- }
- // No new snapshot
- {
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- assertTrue(json.toString(), json.get("metrics").has("snapshot"));
- assertEquals(0.0, json.get("metrics").get("snapshot").get("from").asDouble(), 0.00001);
- assertEquals(300.0, json.get("metrics").get("snapshot").get("to").asDouble(), 0.00001);
- }
- // A new snapshot
- advanceToNextSnapshot();
- {
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- assertTrue(json.toString(), json.get("metrics").has("snapshot"));
- assertEquals(300.0, json.get("metrics").get("snapshot").get("from").asDouble(), 0.00001);
- assertEquals(600.0, json.get("metrics").get("snapshot").get("to").asDouble(), 0.00001);
- }
- }
-
- @Test
- public void testFreshStartOfValuesBeyondSnapshot() throws Exception {
- metric.set("bar", 4, null);
- metric.set("bar", 5, null);
- advanceToNextSnapshot();
- metric.set("bar", 4, null);
- metric.set("bar", 2, null);
- advanceToNextSnapshot();
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- assertEquals(json.toString(), "up", json.get("status").get("code").asText());
- assertEquals(json.toString(), 1, json.get("metrics").get("values").size());
- assertEquals(json.toString(), 3,
- json.get("metrics").get("values").get(0).get("values").get("average").asDouble(), 0.001);
- }
+ public void gaugeSnapshotsTracksCountMinMaxAvgPerPeriod() throws Exception {
+ var snapshot = new MetricSnapshot();
+ snapshot.set(null, "bar", 20);
+ snapshot.set(null, "bar", 40);
+ snapshotProvider.setSnapshot(snapshot);
- @Test
- public void snapshotsPreserveLastGaugeValue() throws Exception {
- metric.set("bar", 4, null);
- advanceToNextSnapshot();
- advanceToNextSnapshot();
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
+ JsonNode json = requestAsJson(V1_URI + "all");
JsonNode metricValues = getFirstMetricValueNode(json);
- assertEquals(json.toString(), 4, metricValues.get("last").asDouble(), 0.001);
- // Use 'last' as avg/min/max when none has been set explicitly during snapshot period
- assertEquals(json.toString(), 4, metricValues.get("average").asDouble(), 0.001);
- assertEquals(json.toString(), 4, metricValues.get("min").asDouble(), 0.001);
- assertEquals(json.toString(), 4, metricValues.get("max").asDouble(), 0.001);
- // Count is tracked per period.
- assertEquals(json.toString(), 0, metricValues.get("count").asInt());
+ assertEquals(json.toString(), 40, metricValues.get("last").asDouble(), 0.001);
+ // Last snapshot had explicit values set
+ assertEquals(json.toString(), 30, metricValues.get("average").asDouble(), 0.001);
+ assertEquals(json.toString(), 20, metricValues.get("min").asDouble(), 0.001);
+ assertEquals(json.toString(), 40, metricValues.get("max").asDouble(), 0.001);
+ assertEquals(json.toString(), 2, metricValues.get("count").asInt());
}
private JsonNode getFirstMetricValueNode(JsonNode root) {
@@ -262,38 +87,65 @@ public class StateHandlerTest extends StateHandlerTestBase {
}
@Test
- public void gaugeSnapshotsTracksCountMinMaxAvgPerPeriod() throws Exception {
- metric.set("bar", 10000, null); // Ensure any cross-snapshot noise is visible
- advanceToNextSnapshot();
- metric.set("bar", 20, null);
- metric.set("bar", 40, null);
+ public void testReadabilityOfJsonReport() {
+ var snapshot = new MetricSnapshot(0L, SNAPSHOT_INTERVAL, TimeUnit.MILLISECONDS);
+ snapshot.add(null, "foo", 1);
+ var ctx = StateMetricContext.newInstance(Map.of("component", "test"));
+ snapshot.set(ctx, "bar", 2);
+ snapshot.set(ctx, "bar", 3);
+ snapshot.set(ctx, "bar", 4);
+ snapshot.set(ctx, "bar", 5);
+ snapshotProvider.setSnapshot(snapshot);
advanceToNextSnapshot();
- JsonNode json = requestAsJson("http://localhost/state/v1/all");
- JsonNode metricValues = getFirstMetricValueNode(json);
- assertEquals(json.toString(), 40, metricValues.get("last").asDouble(), 0.001);
- // Last snapshot had explicit values set
- assertEquals(json.toString(), 30, metricValues.get("average").asDouble(), 0.001);
- assertEquals(json.toString(), 20, metricValues.get("min").asDouble(), 0.001);
- assertEquals(json.toString(), 40, metricValues.get("max").asDouble(), 0.001);
- assertEquals(json.toString(), 2, metricValues.get("count").asInt());
+ assertEquals("{\n" +
+ " \"time\" : 300000,\n" +
+ " \"status\" : {\n" +
+ " \"code\" : \"up\"\n" +
+ " },\n" +
+ " \"metrics\" : {\n" +
+ " \"snapshot\" : {\n" +
+ " \"from\" : 0.0,\n" +
+ " \"to\" : 300.0\n" +
+ " },\n" +
+ " \"values\" : [ {\n" +
+ " \"name\" : \"foo\",\n" +
+ " \"values\" : {\n" +
+ " \"count\" : 1,\n" +
+ " \"rate\" : 0.0033333333333333335\n" +
+ " }\n" +
+ " }, {\n" +
+ " \"name\" : \"bar\",\n" +
+ " \"values\" : {\n" +
+ " \"average\" : 3.5,\n" +
+ " \"sum\" : 14.0,\n" +
+ " \"count\" : 4,\n" +
+ " \"last\" : 5.0,\n" +
+ " \"max\" : 5.0,\n" +
+ " \"min\" : 2.0,\n" +
+ " \"rate\" : 0.013333333333333334\n" +
+ " },\n" +
+ " \"dimensions\" : {\n" +
+ " \"component\" : \"test\"\n" +
+ " }\n" +
+ " } ]\n" +
+ " }\n" +
+ "}",
+ requestAsString(V1_URI + "all"));
}
@Test
public void testHealthAggregation() throws Exception {
- Map<String, String> dimensions1 = new TreeMap<>();
- dimensions1.put("port", String.valueOf(Defaults.getDefaults().vespaWebServicePort()));
- Metric.Context context1 = metric.createContext(dimensions1);
- Map<String, String> dimensions2 = new TreeMap<>();
- dimensions2.put("port", "80");
- Metric.Context context2 = metric.createContext(dimensions2);
-
- metric.add("serverNumSuccessfulResponses", 4, context1);
- metric.add("serverNumSuccessfulResponses", 2, context2);
- metric.set("serverTotalSuccessfulResponseLatency", 20, context1);
- metric.set("serverTotalSuccessfulResponseLatency", 40, context2);
- metric.add("random", 3, context1);
- advanceToNextSnapshot();
- JsonNode json = requestAsJson("http://localhost/state/v1/health");
+ var context1 = StateMetricContext.newInstance(Map.of("port", Defaults.getDefaults().vespaWebServicePort()));
+ var context2 = StateMetricContext.newInstance(Map.of("port", 80));
+ var snapshot = new MetricSnapshot();
+ snapshot.add(context1, "serverNumSuccessfulResponses", 4);
+ snapshot.add(context2, "serverNumSuccessfulResponses", 2);
+ snapshot.set(context1, "serverTotalSuccessfulResponseLatency", 20);
+ snapshot.set(context2, "serverTotalSuccessfulResponseLatency", 40);
+ snapshot.add(context1, "random", 3);
+ snapshotProvider.setSnapshot(snapshot);
+
+ JsonNode json = requestAsJson(V1_URI + "health");
assertEquals(json.toString(), "up", json.get("status").get("code").asText());
assertEquals(json.toString(), 2, json.get("metrics").get("values").size());
assertEquals(json.toString(), "requestsPerSecond",
@@ -308,7 +160,7 @@ public class StateHandlerTest extends StateHandlerTestBase {
@Test
public void testStateConfig() throws Exception {
- JsonNode root = requestAsJson("http://localhost/state/v1/config");
+ JsonNode root = requestAsJson(V1_URI + "config");
JsonNode config = root.get("config");
JsonNode container = config.get("container");
@@ -317,7 +169,7 @@ public class StateHandlerTest extends StateHandlerTestBase {
@Test
public void testStateVersion() throws Exception {
- JsonNode root = requestAsJson("http://localhost/state/v1/version");
+ JsonNode root = requestAsJson(V1_URI + "version");
JsonNode version = root.get("version");
assertEquals(Vtag.currentVersion.toString(), version.asText());
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java
index 57a9ba4abdb..68c1ae10ec6 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java
@@ -3,103 +3,63 @@ package com.yahoo.container.jdisc.state;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.inject.AbstractModule;
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.container.core.ApplicationMetadataConfig;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
import com.yahoo.container.jdisc.config.HealthMonitorConfig;
-import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.Timer;
-import com.yahoo.jdisc.application.ContainerBuilder;
-import com.yahoo.jdisc.application.MetricConsumer;
-import com.yahoo.jdisc.handler.BufferedContentChannel;
-import com.yahoo.jdisc.handler.ContentChannel;
-import com.yahoo.jdisc.handler.ResponseHandler;
-import com.yahoo.jdisc.test.TestDriver;
import com.yahoo.metrics.MetricsPresentationConfig;
-import org.junit.After;
import org.junit.Before;
+import org.junit.BeforeClass;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
/**
- * @author Simon Thoresen Hult
* @author gjoranv
*/
public class StateHandlerTestBase {
final static long SNAPSHOT_INTERVAL = TimeUnit.SECONDS.toMillis(300);
final static long META_GENERATION = 69;
- static final String APPLICATION_NAME = "state-handler-test-base";
- TestDriver driver;
- StateMonitor monitor;
- Metric metric;
+
+ static final String URI_BASE = "http://localhost";
+
+ static StateMonitor monitor;
+ static RequestHandlerTestDriver testDriver;
+
+ static HealthMonitorConfig healthMonitorConfig;
+ static ApplicationMetadataConfig applicationMetadataConfig;
+ static MetricsPresentationConfig metricsPresentationConfig;
+ static MetricsPacketsHandlerConfig metricsPacketsHandlerConfig;
+
final AtomicLong currentTimeMillis = new AtomicLong(0);
+ Timer timer;
- @Before
- public void startTestDriver() {
- Timer timer = this.currentTimeMillis::get;
- this.driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() {
- @Override
- protected void configure() {
- bind(Timer.class).toInstance(timer);
- }
- });
- ContainerBuilder builder = driver.newContainerBuilder();
- HealthMonitorConfig healthMonitorConfig =
- new HealthMonitorConfig(
- new HealthMonitorConfig.Builder()
- .snapshot_interval(TimeUnit.MILLISECONDS.toSeconds(SNAPSHOT_INTERVAL))
- .initialStatus("up"));
- this.monitor = new StateMonitor(healthMonitorConfig, timer, null);
- builder.guiceModules().install(new AbstractModule() {
+ MockSnapshotProvider snapshotProvider;
+ ComponentRegistry<SnapshotProvider> snapshotProviderRegistry;
- @Override
- protected void configure() {
- bind(StateMonitor.class).toInstance(monitor);
- bind(MetricConsumer.class).toProvider(MetricConsumerProviders.wrap(monitor));
- bind(ApplicationMetadataConfig.class).toInstance(new ApplicationMetadataConfig(
- new ApplicationMetadataConfig.Builder().generation(META_GENERATION)));
- bind(MetricsPresentationConfig.class)
- .toInstance(new MetricsPresentationConfig(new MetricsPresentationConfig.Builder()));
- bind(MetricsPacketsHandlerConfig.class).toInstance(new MetricsPacketsHandlerConfig(
- new MetricsPacketsHandlerConfig.Builder().application(APPLICATION_NAME)));
- }
- });
- builder.serverBindings().bind("http://*/*", builder.getInstance(StateHandler.class));
- builder.serverBindings().bind("http://*/metrics-packets", builder.getInstance(MetricsPacketsHandler.class));
- driver.activateContainer(builder);
- metric = builder.getInstance(Metric.class);
+ @BeforeClass
+ public static void setupClass() {
+ metricsPresentationConfig = new MetricsPresentationConfig(new MetricsPresentationConfig.Builder());
+ healthMonitorConfig = new HealthMonitorConfig(new HealthMonitorConfig.Builder()
+ .initialStatus("up"));
+ applicationMetadataConfig = new ApplicationMetadataConfig(new ApplicationMetadataConfig.Builder()
+ .generation(META_GENERATION));
}
- @After
- public void stopTestDriver() {
- assertTrue(driver.close());
+ @Before
+ public void setupSnapshotProvider() {
+ timer = currentTimeMillis::get;
+ snapshotProvider = new MockSnapshotProvider();
+ snapshotProviderRegistry = new ComponentRegistry<>();
+ snapshotProviderRegistry.register(new ComponentId("foo"), snapshotProvider);
+ monitor = new StateMonitor(healthMonitorConfig);
}
- String requestAsString(String requestUri) throws Exception {
- final BufferedContentChannel content = new BufferedContentChannel();
- Response response = driver.dispatchRequest(requestUri, new ResponseHandler() {
-
- @Override
- public ContentChannel handleResponse(Response response) {
- return content;
- }
- }).get(60, TimeUnit.SECONDS);
- assertNotNull(response);
- assertEquals(Response.Status.OK, response.getStatus());
- StringBuilder str = new StringBuilder();
- Reader in = new InputStreamReader(content.toStream(), StandardCharsets.UTF_8);
- for (int c; (c = in.read()) != -1; ) {
- str.append((char)c);
- }
- return str.toString();
+ String requestAsString(String requestUri) {
+ return testDriver.sendRequest(requestUri).readAll();
}
JsonNode requestAsJson(String requestUri) throws Exception {
@@ -109,7 +69,6 @@ public class StateHandlerTestBase {
void advanceToNextSnapshot() {
currentTimeMillis.addAndGet(SNAPSHOT_INTERVAL);
- monitor.updateSnapshot();
}
}
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java
deleted file mode 100644
index 22a2fc274c7..00000000000
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateMonitorBenchmarkTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.jdisc.state;
-
-import com.google.inject.Provider;
-import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.application.ContainerThread;
-import com.yahoo.jdisc.application.MetricConsumer;
-import com.yahoo.jdisc.application.MetricProvider;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author Simon Thoresen Hult
- */
-public class StateMonitorBenchmarkTest {
-
- private final static int NUM_THREADS = 32;
- private final static int NUM_UPDATES = 1000;//0000;
-
- @Test
- public void requireThatHealthMonitorDoesNotBlockMetricThreads() throws Exception {
- StateMonitor monitor = StateMonitor.createForTesting();
- Provider<MetricConsumer> provider = MetricConsumerProviders.wrap(monitor);
- performUpdates(provider, 8);
- for (int i = 1; i <= NUM_THREADS; i *= 2) {
- long millis = performUpdates(provider, i);
- System.err.format("%2d threads, %5d millis => %9d ups\n",
- i, millis, (int)((i * NUM_UPDATES) / (millis / 1000.0)));
- }
- monitor.deconstruct();
- }
-
- private long performUpdates(Provider<MetricConsumer> metricProvider, int numThreads) throws Exception {
- ThreadFactory threadFactory = new ContainerThread.Factory(metricProvider);
- ExecutorService executor = Executors.newFixedThreadPool(numThreads, threadFactory);
- List<Callable<Boolean>> tasks = new ArrayList<>(numThreads);
- for (int i = 0; i < numThreads; ++i) {
- tasks.add(new UpdateTask(new MetricProvider(metricProvider).get()));
- }
- long before = System.nanoTime();
- List<Future<Boolean>> results = executor.invokeAll(tasks);
- long after = System.nanoTime();
- for (Future<Boolean> result : results) {
- assertTrue(result.get());
- }
- return TimeUnit.NANOSECONDS.toMillis(after - before);
- }
-
- public static class UpdateTask implements Callable<Boolean> {
-
- final Metric metric;
-
- UpdateTask(Metric metric) {
- this.metric = metric;
- }
-
- @Override
- public Boolean call() throws Exception {
- Metric.Context ctx = metric.createContext(Collections.<String, Object>emptyMap());
- for (int i = 0; i < NUM_UPDATES; ++i) {
- metric.add("foo", 69L, ctx);
- }
- return true;
- }
- }
-}
diff --git a/container-dependency-versions/pom.xml b/container-dependency-versions/pom.xml
index 8691d9a7ffb..08d6e0103bf 100644
--- a/container-dependency-versions/pom.xml
+++ b/container-dependency-versions/pom.xml
@@ -313,7 +313,7 @@
<artifactId>javassist</artifactId>
<version>${javassist.version}</version>
</dependency>
- <dependency> <!-- TODO Vespa 8: upgrade to newest version. Consider removing as provided dependency -->
+ <dependency> <!-- TODO Vespa 8: remove as provided dependency -->
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>${org.json.version}</version>
diff --git a/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java b/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java
index d43f96c9b4a..065733a719a 100644
--- a/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java
+++ b/container-di/src/main/java/com/yahoo/container/di/CloudSubscriberFactory.java
@@ -114,7 +114,8 @@ public class CloudSubscriberFactory implements SubscriberFactory {
catch (IllegalArgumentException e) {
numExceptions++;
log.log(Level.WARNING, "Got exception from the config system (ignore if you just removed a " +
- "component from your application that used the mentioned config): ", e);
+ "component from your application that used the mentioned config) Subscriber info: " +
+ subscriber.toString(), e);
if (numExceptions >= 5)
throw new IllegalArgumentException("Failed retrieving the next config generation", e);
}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
index a300364d848..9b907639e57 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
@@ -4,10 +4,7 @@ package com.yahoo.container.jdisc.metric;
import com.google.inject.Inject;
import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.container.jdisc.MetricConsumerFactory;
-import com.yahoo.container.jdisc.metric.state.StateMetricConsumerFactory;
-import com.yahoo.container.jdisc.state.StateMonitor;
import com.yahoo.jdisc.application.MetricConsumer;
-import com.yahoo.metrics.MetricsPresentationConfig;
/**
@@ -27,17 +24,9 @@ public class MetricConsumerProvider {
private final MetricConsumerFactory[] factories;
@Inject
- public MetricConsumerProvider(ComponentRegistry<MetricConsumerFactory> factoryRegistry,
- MetricsPresentationConfig presentationConfig,
- StateMonitor stateMonitor) {
- MetricConsumerFactory[] factories;
- if (factoryRegistry.getComponentCount() == 0 || ! presentationConfig.slidingwindow()) {
- factories = new MetricConsumerFactory[1];
- factories[0] = new StateMetricConsumerFactory(stateMonitor);
- } else {
- factories = new MetricConsumerFactory[factoryRegistry.getComponentCount()];
- factoryRegistry.allComponents().toArray(factories);
- }
+ public MetricConsumerProvider(ComponentRegistry<MetricConsumerFactory> factoryRegistry) {
+ MetricConsumerFactory[] factories = new MetricConsumerFactory[factoryRegistry.getComponentCount()];
+ factoryRegistry.allComponents().toArray(factories);
this.factories = factories;
}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java
index a44650a153d..ca3d47bc338 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java
@@ -20,10 +20,8 @@ public class MetricConsumerProviderProvider implements Provider<MetricConsumerPr
private final MetricConsumerProvider provided;
@Inject
- public MetricConsumerProviderProvider(ComponentRegistry<MetricConsumerFactory> factoryRegistry,
- MetricsPresentationConfig presentationConfig,
- StateMonitor stateMonitor) {
- provided = new MetricConsumerProvider(factoryRegistry, presentationConfig, stateMonitor);
+ public MetricConsumerProviderProvider(ComponentRegistry<MetricConsumerFactory> factoryRegistry) {
+ provided = new MetricConsumerProvider(factoryRegistry);
}
@Override
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java
deleted file mode 100644
index d1cf660f571..00000000000
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.jdisc.metric.state;
-
-import com.yahoo.container.jdisc.MetricConsumerFactory;
-import com.yahoo.container.jdisc.state.StateMonitor;
-import com.yahoo.jdisc.application.MetricConsumer;
-
-/**
- * @author Simon Thoresen Hult
- */
-public class StateMetricConsumerFactory implements MetricConsumerFactory {
-
- private final StateMonitor stateMonitor;
-
- public StateMetricConsumerFactory(StateMonitor stateMonitor) {
- this.stateMonitor = stateMonitor;
- }
-
- @Override
- public MetricConsumer newInstance() {
- return stateMonitor.newMetricConsumer();
- }
-
-}
diff --git a/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java b/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java
index 709441999d0..df7cacdc768 100644
--- a/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java
+++ b/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java
@@ -1,9 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.usability;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
import com.yahoo.component.ComponentId;
-import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.container.Container;
import com.yahoo.container.jdisc.JdiscBindingsConfig;
import com.yahoo.jdisc.handler.AbstractRequestHandler;
@@ -15,16 +19,11 @@ import com.yahoo.jdisc.handler.ResponseDispatch;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.HttpRequest;
import com.yahoo.jdisc.http.HttpRequest.Method;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -33,6 +32,8 @@ import java.util.Map;
*/
public class BindingsOverviewHandler extends AbstractRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private final JdiscBindingsConfig bindingsConfig;
@Inject
@@ -42,7 +43,7 @@ public class BindingsOverviewHandler extends AbstractRequestHandler {
@Override
public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) {
- JSONObject json;
+ JsonNode json;
int statusToReturn;
if (request instanceof HttpRequest && ((HttpRequest) request).getMethod() != Method.GET) {
@@ -63,7 +64,9 @@ public class BindingsOverviewHandler extends AbstractRequestHandler {
}.connect(handler));
try {
- writer.write(json.toString());
+ writer.write(jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(json));
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
} finally {
writer.close();
}
@@ -71,63 +74,58 @@ public class BindingsOverviewHandler extends AbstractRequestHandler {
return new IgnoredContent();
}
- private JSONObject errorMessageInJson() {
- JSONObject error = new JSONObject();
- try {
- error.put("error", "This API, "
- + this.getClass().getSimpleName()
- + ", only supports HTTP GET."
- + " You are probably looking for another API/path.");
- } catch (org.json.JSONException e) {
- // just ignore it
- }
+ private static JsonNode errorMessageInJson() {
+ ObjectNode error = jsonMapper.createObjectNode();
+ error.put("error", "This API, "
+ + BindingsOverviewHandler.class.getSimpleName()
+ + ", only supports HTTP GET."
+ + " You are probably looking for another API/path.");
return error;
}
- static JSONArray renderRequestHandlers(JdiscBindingsConfig bindingsConfig,
+ static ArrayNode renderRequestHandlers(JdiscBindingsConfig bindingsConfig,
Map<ComponentId, ? extends RequestHandler> handlersById) {
- JSONArray ret = new JSONArray();
+ ArrayNode ret = jsonMapper.createArrayNode();
for (Map.Entry<ComponentId, ? extends RequestHandler> handlerEntry : handlersById.entrySet()) {
String id = handlerEntry.getKey().stringValue();
RequestHandler handler = handlerEntry.getValue();
- JSONObject handlerJson = renderComponent(handler, handlerEntry.getKey());
+ ObjectNode handlerJson = renderComponent(handler, handlerEntry.getKey());
addBindings(bindingsConfig, id, handlerJson);
- ret.put(handlerJson);
+ ret.add(handlerJson);
}
return ret;
}
- private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, JSONObject handlerJson) {
+ private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, ObjectNode handlerJson) {
List<String> serverBindings = new ArrayList<>();
JdiscBindingsConfig.Handlers handlerConfig = bindingsConfig.handlers(id);
if (handlerConfig != null) {
serverBindings = handlerConfig.serverBindings();
}
- putJson(handlerJson, "serverBindings", renderBindings(serverBindings));
+ handlerJson.set("serverBindings", renderBindings(serverBindings));
}
- private static JSONArray renderBindings(List<String> bindings) {
- JSONArray array = new JSONArray();
+ private static JsonNode renderBindings(List<String> bindings) {
+ ArrayNode array = jsonMapper.createArrayNode();
for (String binding : bindings)
- array.put(binding);
+ array.add(binding);
return array;
}
- private static JSONObject renderComponent(Object component, ComponentId id) {
- JSONObject jc = new JSONObject();
- putJson(jc, "id", id.stringValue());
+ private static ObjectNode renderComponent(Object component, ComponentId id) {
+ ObjectNode jc = jsonMapper.createObjectNode();
+ jc.put("id", id.stringValue());
addBundleInfo(jc, component);
return jc;
}
- private static void addBundleInfo(JSONObject jsonObject, Object component) {
+ private static void addBundleInfo(ObjectNode jsonObject, Object component) {
BundleInfo bundleInfo = bundleInfo(component);
- putJson(jsonObject, "class", bundleInfo.className);
- putJson(jsonObject, "bundle", bundleInfo.bundleName);
-
+ jsonObject.put("class", bundleInfo.className);
+ jsonObject.put("bundle", bundleInfo.bundleName);
}
private static BundleInfo bundleInfo(Object component) {
@@ -143,15 +141,6 @@ public class BindingsOverviewHandler extends AbstractRequestHandler {
}
}
- private static void putJson(JSONObject json, String key, Object value) {
- try {
- json.put(key, value);
- } catch (JSONException e) {
- // The original JSONException lacks key-value info.
- throw new RuntimeException("Trying to add invalid JSON object with key '" + key + "' and value '" + value + "' - " + e.getMessage(), e);
- }
- }
-
static final class BundleInfo {
public final String className;
@@ -172,10 +161,10 @@ public class BindingsOverviewHandler extends AbstractRequestHandler {
this.bindingsConfig = bindingsConfig;
}
- public JSONObject render() {
- JSONObject root = new JSONObject();
+ public JsonNode render() {
+ ObjectNode root = jsonMapper.createObjectNode();
- putJson(root, "handlers",
+ root.set("handlers",
renderRequestHandlers(bindingsConfig, Container.get().getRequestHandlerRegistry().allComponentsById()));
return root;
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
index 5e3a49c2dda..0acbb701612 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
@@ -34,10 +34,4 @@ public class MetricConsumerProviderTest {
Mockito.verify(bar, Mockito.times(1)).add("foo", 6, null);
}
- @Test
- public void requireThatDefaultConsumerFactoryIsStateMetric() {
- MetricConsumer consumer = MetricConsumerProviders.newDefaultInstance().newInstance();
- assertEquals("StateMetricConsumer", consumer.getClass().getSimpleName());
- }
-
}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
index dd96414bbc7..4e576d2cd8c 100644
--- a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
@@ -22,15 +22,7 @@ class MetricConsumerProviders {
}
public static MetricConsumerProvider newInstance(MetricConsumerFactory... factories) {
- return new MetricConsumerProvider(newComponentRegistry(factories),
- new MetricsPresentationConfig(new MetricsPresentationConfig.Builder()),
- StateMonitor.createForTesting());
- }
-
- public static MetricConsumerProvider newDefaultInstance() {
- return new MetricConsumerProvider(newComponentRegistry(),
- new MetricsPresentationConfig(new MetricsPresentationConfig.Builder()),
- StateMonitor.createForTesting());
+ return new MetricConsumerProvider(newComponentRegistry(factories));
}
public static ComponentRegistry<MetricConsumerFactory> newComponentRegistry(MetricConsumerFactory... factories) {
diff --git a/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java
index 57c5e768cfb..943eef1e0bf 100644
--- a/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java
+++ b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java
@@ -1,6 +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.container.handler.observability;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
import com.yahoo.component.ComponentId;
@@ -29,15 +34,11 @@ import com.yahoo.processing.execution.chain.ChainRegistry;
import com.yahoo.processing.handler.ProcessingHandler;
import com.yahoo.search.handler.SearchHandler;
import com.yahoo.search.searchchain.SearchChainRegistry;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@@ -51,11 +52,13 @@ import java.util.Map;
*/
public class ApplicationStatusHandler extends AbstractRequestHandler {
- private final JSONObject applicationJson;
- private final JSONArray clientsJson;
- private final JSONArray serversJson;
- private final JSONArray requestFiltersJson;
- private final JSONArray responseFiltersJson;
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
+ private final JsonNode applicationJson;
+ private final JsonNode clientsJson;
+ private final JsonNode serversJson;
+ private final JsonNode requestFiltersJson;
+ private final JsonNode responseFiltersJson;
private final JdiscBindingsConfig bindingsConfig;
@Inject
@@ -78,7 +81,7 @@ public class ApplicationStatusHandler extends AbstractRequestHandler {
@Override
public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) {
- JSONObject json = new StatusResponse(applicationJson, clientsJson, serversJson,
+ JsonNode json = new StatusResponse(applicationJson, clientsJson, serversJson,
requestFiltersJson, responseFiltersJson, bindingsConfig)
.render();
@@ -91,62 +94,66 @@ public class ApplicationStatusHandler extends AbstractRequestHandler {
}
}.connect(handler));
- writer.write(json.toString());
+ try {
+ writer.write(jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(json));
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Invalid JSON: " + e.getMessage(), e);
+ }
writer.close();
return new IgnoredContent();
}
- static JSONObject renderApplicationConfigs(ApplicationMetadataConfig metaConfig,
+ static JsonNode renderApplicationConfigs(ApplicationMetadataConfig metaConfig,
ApplicationUserdataConfig userConfig) {
- JSONObject vespa = new JSONObject();
- putJson(vespa, "version", Vtag.currentVersion);
-
- JSONObject meta = new JSONObject();
- putJson(meta, "name", metaConfig.name());
- putJson(meta, "user", metaConfig.user());
- putJson(meta, "path", metaConfig.path());
- putJson(meta, "generation", metaConfig.generation());
- putJson(meta, "timestamp", metaConfig.timestamp());
- putJson(meta, "date", new Date(metaConfig.timestamp()).toString());
- putJson(meta, "checksum", metaConfig.checksum());
-
- JSONObject user = new JSONObject();
- putJson(user, "version", userConfig.version());
-
- JSONObject application = new JSONObject();
- putJson(application, "vespa", vespa);
- putJson(application, "meta", meta);
- putJson(application, "user", user);
+ ObjectNode vespa = jsonMapper.createObjectNode();
+ vespa.put("version", Vtag.currentVersion.toString());
+
+ ObjectNode meta = jsonMapper.createObjectNode();
+ meta.put("name", metaConfig.name());
+ meta.put("user", metaConfig.user());
+ meta.put("path", metaConfig.path());
+ meta.put("generation", metaConfig.generation());
+ meta.put("timestamp", metaConfig.timestamp());
+ meta.put("date", new Date(metaConfig.timestamp()).toString());
+ meta.put("checksum", metaConfig.checksum());
+
+ ObjectNode user = jsonMapper.createObjectNode();
+ user.put("version", userConfig.version());
+
+ ObjectNode application = jsonMapper.createObjectNode();
+ application.set("vespa", vespa);
+ application.set("meta", meta);
+ application.set("user", user);
return application;
}
- static JSONArray renderObjectComponents(Map<ComponentId, ?> componentsById) {
- JSONArray ret = new JSONArray();
+ static JsonNode renderObjectComponents(Map<ComponentId, ?> componentsById) {
+ ArrayNode ret = jsonMapper.createArrayNode();
for (Map.Entry<ComponentId, ?> componentEntry : componentsById.entrySet()) {
- JSONObject jc = renderComponent(componentEntry.getValue(), componentEntry.getKey());
- ret.put(jc);
+ JsonNode jc = renderComponent(componentEntry.getValue(), componentEntry.getKey());
+ ret.add(jc);
}
return ret;
}
- static JSONArray renderRequestHandlers(JdiscBindingsConfig bindingsConfig,
+ static JsonNode renderRequestHandlers(JdiscBindingsConfig bindingsConfig,
Map<ComponentId, ? extends RequestHandler> handlersById) {
- JSONArray ret = new JSONArray();
+ ArrayNode ret = jsonMapper.createArrayNode();
for (Map.Entry<ComponentId, ? extends RequestHandler> handlerEntry : handlersById.entrySet()) {
String id = handlerEntry.getKey().stringValue();
RequestHandler handler = handlerEntry.getValue();
- JSONObject handlerJson = renderComponent(handler, handlerEntry.getKey());
+ ObjectNode handlerJson = renderComponent(handler, handlerEntry.getKey());
addBindings(bindingsConfig, id, handlerJson);
- ret.put(handlerJson);
+ ret.add(handlerJson);
}
return ret;
}
- private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, JSONObject handlerJson) {
+ private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, ObjectNode handlerJson) {
List<String> serverBindings = new ArrayList<>();
List<String> clientBindings = new ArrayList<>();
@@ -155,40 +162,40 @@ public class ApplicationStatusHandler extends AbstractRequestHandler {
serverBindings = handlerConfig.serverBindings();
clientBindings = handlerConfig.clientBindings();
}
- putJson(handlerJson, "serverBindings", renderBindings(serverBindings));
- putJson(handlerJson, "clientBindings", renderBindings(clientBindings));
+ handlerJson.set("serverBindings", renderBindings(serverBindings));
+ handlerJson.set("clientBindings", renderBindings(clientBindings));
}
- private static JSONArray renderBindings(List<String> bindings) {
- JSONArray ret = new JSONArray();
+ private static JsonNode renderBindings(List<String> bindings) {
+ ArrayNode ret = jsonMapper.createArrayNode();
for (String binding : bindings)
- ret.put(binding);
+ ret.add(binding);
return ret;
}
- private static JSONArray renderAbstractComponents(List<? extends AbstractComponent> components) {
- JSONArray ret = new JSONArray();
+ private static JsonNode renderAbstractComponents(List<? extends AbstractComponent> components) {
+ ArrayNode ret = jsonMapper.createArrayNode();
for (AbstractComponent c : components) {
- JSONObject jc = renderComponent(c, c.getId());
- ret.put(jc);
+ JsonNode jc = renderComponent(c, c.getId());
+ ret.add(jc);
}
return ret;
}
- private static JSONObject renderComponent(Object component, ComponentId id) {
- JSONObject jc = new JSONObject();
- putJson(jc, "id", id.stringValue());
+ private static ObjectNode renderComponent(Object component, ComponentId id) {
+ ObjectNode jc = jsonMapper.createObjectNode();
+ jc.put("id", id.stringValue());
addBundleInfo(jc, component);
return jc;
}
- private static void addBundleInfo(JSONObject jsonObject, Object component) {
+ private static void addBundleInfo(ObjectNode jsonObject, Object component) {
BundleInfo bundleInfo = bundleInfo(component);
- putJson(jsonObject, "class", bundleInfo.className);
- putJson(jsonObject, "bundle", bundleInfo.bundleName);
+ jsonObject.put("class", bundleInfo.className);
+ jsonObject.put("bundle", bundleInfo.bundleName);
}
@@ -205,15 +212,6 @@ public class ApplicationStatusHandler extends AbstractRequestHandler {
}
}
- private static void putJson(JSONObject json, String key, Object value) {
- try {
- json.put(key, value);
- } catch (JSONException e) {
- // The original JSONException lacks key-value info.
- throw new RuntimeException("Trying to add invalid JSON object with key '" + key + "' and value '" + value + "' - " + e.getMessage(), e);
- }
- }
-
static final class BundleInfo {
public final String className;
public final String bundleName;
@@ -224,18 +222,18 @@ public class ApplicationStatusHandler extends AbstractRequestHandler {
}
static final class StatusResponse {
- private final JSONObject applicationJson;
- private final JSONArray clientsJson;
- private final JSONArray serversJson;
- private final JSONArray requestFiltersJson;
- private final JSONArray responseFiltersJson;
+ private final JsonNode applicationJson;
+ private final JsonNode clientsJson;
+ private final JsonNode serversJson;
+ private final JsonNode requestFiltersJson;
+ private final JsonNode responseFiltersJson;
private final JdiscBindingsConfig bindingsConfig;
- StatusResponse(JSONObject applicationJson,
- JSONArray clientsJson,
- JSONArray serversJson,
- JSONArray requestFiltersJson,
- JSONArray responseFiltersJson,
+ StatusResponse(JsonNode applicationJson,
+ JsonNode clientsJson,
+ JsonNode serversJson,
+ JsonNode requestFiltersJson,
+ JsonNode responseFiltersJson,
JdiscBindingsConfig bindingsConfig) {
this.applicationJson = applicationJson;
this.clientsJson = clientsJson;
@@ -245,52 +243,52 @@ public class ApplicationStatusHandler extends AbstractRequestHandler {
this.bindingsConfig = bindingsConfig;
}
- public JSONObject render() {
- JSONObject root = new JSONObject();
+ public JsonNode render() {
+ ObjectNode root = jsonMapper.createObjectNode();
- putJson(root, "application", applicationJson);
- putJson(root, "abstractComponents",
+ root.set("application", applicationJson);
+ root.set("abstractComponents",
renderAbstractComponents(Container.get().getComponentRegistry().allComponents()));
- putJson(root, "handlers",
+ root.set("handlers",
renderRequestHandlers(bindingsConfig, Container.get().getRequestHandlerRegistry().allComponentsById()));
- putJson(root, "clients", clientsJson);
- putJson(root, "servers", serversJson);
- putJson(root, "httpRequestFilters", requestFiltersJson);
- putJson(root, "httpResponseFilters", responseFiltersJson);
+ root.set("clients", clientsJson);
+ root.set("servers", serversJson);
+ root.set("httpRequestFilters", requestFiltersJson);
+ root.set("httpResponseFilters", responseFiltersJson);
- putJson(root, "searchChains", renderSearchChains(Container.get()));
- putJson(root, "docprocChains", renderDocprocChains(Container.get()));
- putJson(root, "processingChains", renderProcessingChains(Container.get()));
+ root.set("searchChains", renderSearchChains(Container.get()));
+ root.set("docprocChains", renderDocprocChains(Container.get()));
+ root.set("processingChains", renderProcessingChains(Container.get()));
return root;
}
- private static JSONObject renderSearchChains(Container container) {
+ private static JsonNode renderSearchChains(Container container) {
for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) {
if (h instanceof SearchHandler) {
SearchChainRegistry scReg = ((SearchHandler) h).getSearchChainRegistry();
return renderChains(scReg);
}
}
- return new JSONObject();
+ return jsonMapper.createObjectNode();
}
- private static JSONObject renderDocprocChains(Container container) {
- JSONObject ret = new JSONObject();
+ private static JsonNode renderDocprocChains(Container container) {
+ ObjectNode ret = jsonMapper.createObjectNode();
for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) {
if (h instanceof DocumentProcessingHandler) {
ComponentRegistry<DocprocService> registry = ((DocumentProcessingHandler) h).getDocprocServiceRegistry();
for (DocprocService service : registry.allComponents()) {
- putJson(ret, service.getId().stringValue(), renderCalls(service.getCallStack().iterator()));
+ ret.set(service.getId().stringValue(), renderCalls(service.getCallStack().iterator()));
}
}
}
return ret;
}
- private static JSONObject renderProcessingChains(Container container) {
- JSONObject ret = new JSONObject();
+ private static JsonNode renderProcessingChains(Container container) {
+ JsonNode ret = jsonMapper.createObjectNode();
for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) {
if (h instanceof ProcessingHandler) {
ChainRegistry<Processor> registry = ((ProcessingHandler) h).getChainRegistry();
@@ -301,20 +299,20 @@ public class ApplicationStatusHandler extends AbstractRequestHandler {
}
// Note the generic param here! The key to make this work is '? extends Chain', but why?
- static JSONObject renderChains(ComponentRegistry<? extends Chain<?>> chains) {
- JSONObject ret = new JSONObject();
+ static JsonNode renderChains(ComponentRegistry<? extends Chain<?>> chains) {
+ ObjectNode ret = jsonMapper.createObjectNode();
for (Chain<?> chain : chains.allComponents()) {
- putJson(ret, chain.getId().stringValue(), renderAbstractComponents(chain.components()));
+ ret.set(chain.getId().stringValue(), renderAbstractComponents(chain.components()));
}
return ret;
}
- private static JSONArray renderCalls(Iterator<Call> components) {
- JSONArray ret = new JSONArray();
+ private static JsonNode renderCalls(Iterator<Call> components) {
+ ArrayNode ret = jsonMapper.createArrayNode();
while (components.hasNext()) {
Call c = components.next();
- JSONObject jc = renderComponent(c.getDocumentProcessor(), c.getDocumentProcessor().getId());
- ret.put(jc);
+ JsonNode jc = renderComponent(c.getDocumentProcessor(), c.getDocumentProcessor().getId());
+ ret.add(jc);
}
return ret;
}
diff --git a/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java b/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java
index 9ddcc7a7e69..3132f3744c9 100644
--- a/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java
+++ b/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java
@@ -1,8 +1,10 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.query.gui;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
-
import com.yahoo.container.QrSearchersConfig;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
@@ -11,6 +13,7 @@ import com.yahoo.prelude.IndexModel;
import com.yahoo.prelude.querytransform.RecallSearcher;
import com.yahoo.restapi.Path;
import com.yahoo.search.Query;
+import com.yahoo.search.config.IndexInfoConfig;
import com.yahoo.search.query.Model;
import com.yahoo.search.query.Presentation;
import com.yahoo.search.query.Ranking;
@@ -19,19 +22,12 @@ import com.yahoo.search.query.ranking.MatchPhase;
import com.yahoo.search.query.restapi.ErrorResponse;
import com.yahoo.search.yql.MinimalQueryInserter;
import com.yahoo.vespa.config.search.RankProfilesConfig;
-import com.yahoo.search.config.IndexInfoConfig;
import com.yahoo.yolean.Exceptions;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
import java.util.logging.Level;
@@ -42,6 +38,8 @@ import java.util.logging.Level;
*/
public class GUIHandler extends LoggingRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private final IndexModel indexModel;
private final RankProfilesConfig rankProfilesConfig;
@@ -112,7 +110,7 @@ public class GUIHandler extends LoggingRequestHandler {
InputStream is;
if (this.path.equals("config.json")){
String json = "{}";
- try { json = getGUIConfig(); } catch (JSONException e) { /*Something happened while parsing JSON */ }
+ try { json = getGUIConfig(); } catch (IOException e) { /*Something happened while parsing JSON */ }
is = new ByteArrayInputStream(json.getBytes());
} else{
is = GUIHandler.class.getClassLoader().getResourceAsStream("gui/"+this.path);
@@ -160,50 +158,50 @@ public class GUIHandler extends LoggingRequestHandler {
return "text/html";
}
- private String getGUIConfig() throws JSONException {
- JSONObject json = new JSONObject();
- json.put("ranking_properties", Arrays.asList("propertyname"));
- json.put("ranking_features", Arrays.asList("featurename"));
+ private String getGUIConfig() throws IOException {
+ ObjectNode json = jsonMapper.createObjectNode();
+ json.set("ranking_properties", jsonMapper.createArrayNode().add("propertyname"));
+ json.set("ranking_features", jsonMapper.createArrayNode().add("featurename"));
- List<String> sources = new ArrayList<>();
+ ArrayNode sources = jsonMapper.createArrayNode();
try {
- sources = new ArrayList<>(indexModel.getMasterClusters().keySet());
+ indexModel.getMasterClusters().keySet().forEach(sources::add);
} catch (NullPointerException ex){ /* clusters are not set */ }
- json.put("model_sources", sources);
+ json.set("model_sources", sources);
- List<String> rankProfiles = new ArrayList<>();
+ ArrayNode rankProfiles = jsonMapper.createArrayNode();
try {
rankProfilesConfig.rankprofile().forEach(rankProfile -> rankProfiles.add(rankProfile.name()));
} catch (NullPointerException ex){ /* rankprofiles are not set*/ }
- json.put("ranking_profile", rankProfiles);
+ json.set("ranking_profile", rankProfiles);
// Creating map from parent to children for GUI: parameter --> child-parameters
- HashMap<String, List<String>> childMap = new HashMap<>();
- childMap.put(Model.MODEL, Arrays.asList(Model.DEFAULT_INDEX, Model.ENCODING, Model.LANGUAGE, Model.QUERY_STRING, Model.RESTRICT, Model.SEARCH_PATH, Model.SOURCES, Model.TYPE));
- childMap.put(Ranking.RANKING, Arrays.asList(Ranking.LOCATION, Ranking.FEATURES, Ranking.LIST_FEATURES, Ranking.PROFILE, Ranking.PROPERTIES, Ranking.SORTING, Ranking.FRESHNESS, Ranking.QUERYCACHE, Ranking.MATCH_PHASE));
- childMap.put(Ranking.RANKING +"."+ Ranking.MATCH_PHASE, Arrays.asList(MatchPhase.MAX_HITS, MatchPhase.ATTRIBUTE, MatchPhase.ASCENDING, Ranking.DIVERSITY));
- childMap.put(Ranking.RANKING +"."+ Ranking.MATCH_PHASE +"."+Ranking.DIVERSITY, Arrays.asList(Diversity.ATTRIBUTE, Diversity.MINGROUPS));
- childMap.put(Presentation.PRESENTATION, Arrays.asList(Presentation.BOLDING, Presentation.FORMAT, Presentation.SUMMARY, "template", Presentation.TIMING ));
- childMap.put("trace", Arrays.asList("timestamps"));
- childMap.put("tracelevel", Arrays.asList("rules"));
- childMap.put("metrics", Arrays.asList("ignore"));
- childMap.put("collapse", Arrays.asList("summary"));
- childMap.put("pos", Arrays.asList("ll", "radius", "bb", "attribute"));
- childMap.put("streaming", Arrays.asList("userid", "groupname", "selection", "priority", "maxbucketspervisitor"));
- childMap.put("rules", Arrays.asList("off", "rulebase"));
- json.put("childMap", childMap);
-
- List<String> levelZeroParameters = Arrays.asList(MinimalQueryInserter.YQL.toString(), Query.HITS.toString(), Query.OFFSET.toString(),
- "queryProfile", Query.NO_CACHE.toString(), Query.GROUPING_SESSION_CACHE.toString(),
- Query.SEARCH_CHAIN.toString(), Query.TIMEOUT.toString(), "trace", "tracelevel",
- Query.TRACE_LEVEL.toString(), Query.EXPLAIN_LEVEL.toString(), "explainlevel", Model.MODEL, Ranking.RANKING, "collapse", "collapsesize","collapsefield",
- Presentation.PRESENTATION, "pos", "streaming", "rules", RecallSearcher.recallName.toString(), "user",
- "metrics", "");
- json.put("levelZeroParameters", levelZeroParameters);
-
- return json.toString();
+ ObjectNode childMap = jsonMapper.createObjectNode();
+ childMap.set(Model.MODEL, jsonMapper.createArrayNode().add(Model.DEFAULT_INDEX).add(Model.ENCODING).add(Model.LANGUAGE).add(Model.QUERY_STRING).add(Model.RESTRICT).add(Model.SEARCH_PATH).add(Model.SOURCES).add(Model.TYPE));
+ childMap.set(Ranking.RANKING, jsonMapper.createArrayNode().add(Ranking.LOCATION).add(Ranking.FEATURES).add(Ranking.LIST_FEATURES).add(Ranking.PROFILE).add(Ranking.PROPERTIES).add(Ranking.SORTING).add(Ranking.FRESHNESS).add(Ranking.QUERYCACHE).add(Ranking.MATCH_PHASE));
+ childMap.set(Ranking.RANKING +"."+ Ranking.MATCH_PHASE, jsonMapper.createArrayNode().add(MatchPhase.MAX_HITS).add(MatchPhase.ATTRIBUTE).add(MatchPhase.ASCENDING).add(Ranking.DIVERSITY));
+ childMap.set(Ranking.RANKING +"."+ Ranking.MATCH_PHASE +"."+Ranking.DIVERSITY, jsonMapper.createArrayNode().add(Diversity.ATTRIBUTE).add(Diversity.MINGROUPS));
+ childMap.set(Presentation.PRESENTATION, jsonMapper.createArrayNode().add(Presentation.BOLDING).add(Presentation.FORMAT).add(Presentation.SUMMARY).add("template").add(Presentation.TIMING ));
+ childMap.set("trace", jsonMapper.createArrayNode().add("timestamps"));
+ childMap.set("tracelevel", jsonMapper.createArrayNode().add("rules"));
+ childMap.set("metrics", jsonMapper.createArrayNode().add("ignore"));
+ childMap.set("collapse", jsonMapper.createArrayNode().add("summary"));
+ childMap.set("pos", jsonMapper.createArrayNode().add("ll").add("radius").add("bb").add("attribute"));
+ childMap.set("streaming", jsonMapper.createArrayNode().add("userid").add("groupname").add("selection").add("priority").add("maxbucketspervisitor"));
+ childMap.set("rules", jsonMapper.createArrayNode().add("off").add("rulebase"));
+ json.set("childMap", childMap);
+
+ ArrayNode levelZeroParameters = jsonMapper.createArrayNode().add(MinimalQueryInserter.YQL.toString()).add(Query.HITS.toString()).add(Query.OFFSET.toString())
+ .add("queryProfile").add(Query.NO_CACHE.toString()).add(Query.GROUPING_SESSION_CACHE.toString())
+ .add(Query.SEARCH_CHAIN.toString()).add(Query.TIMEOUT.toString()).add("trace").add("tracelevel")
+ .add(Query.TRACE_LEVEL.toString()).add(Query.EXPLAIN_LEVEL.toString()).add("explainlevel").add(Model.MODEL).add(Ranking.RANKING).add("collapse").add("collapsesize").add("collapsefield")
+ .add(Presentation.PRESENTATION).add("pos").add("streaming").add("rules").add(RecallSearcher.recallName.toString()).add("user")
+ .add("metrics").add("");
+ json.set("levelZeroParameters", levelZeroParameters);
+
+ return jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
}
}
} \ No newline at end of file
diff --git a/container-search/pom.xml b/container-search/pom.xml
index 074f5827122..014b7dda14f 100644
--- a/container-search/pom.xml
+++ b/container-search/pom.xml
@@ -150,6 +150,11 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java b/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java
index 209bfd08e6b..55438aa35ba 100644
--- a/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java
+++ b/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java
@@ -21,6 +21,7 @@ import java.util.Iterator;
*
* @author Steinar Knutsen
*/
+// TODO Vespa 8: remove methods leaking org.json types (replace with Slime equivalent?)
public class JSONString implements Inspectable {
private Inspector value;
@@ -436,6 +437,8 @@ public class JSONString implements Inspectable {
return content;
}
+ /** @deprecated Use {@link #getContent()} instead and parse content yourself */
+ @Deprecated(forRemoval = true, since = "7")
public Object getParsedJSON() {
initContent();
if (parsedJSON == null) {
@@ -444,6 +447,7 @@ public class JSONString implements Inspectable {
return parsedJSON;
}
+ @Deprecated(forRemoval = true, since = "7")
public void setParsedJSON(Object parsedJSON) {
this.parsedJSON = parsedJSON;
}
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
index 31f8194b3b7..c4f850307ae 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
@@ -45,8 +45,6 @@ import com.yahoo.search.result.Hit;
import com.yahoo.search.result.HitGroup;
import com.yahoo.search.result.NanNumber;
import com.yahoo.tensor.Tensor;
-import org.json.JSONArray;
-import org.json.JSONObject;
import java.io.IOException;
import java.io.OutputStream;
@@ -671,14 +669,6 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
} else if (field instanceof FieldValue) {
// the null below is the field which has already been written
((FieldValue) field).serialize(null, new JsonWriter(generator));
- } else if (field instanceof JSONArray || field instanceof JSONObject) {
- // org.json returns null if the object would not result in syntactically correct JSON
- String s = field.toString();
- if (s == null) {
- generator.writeNull();
- } else {
- generator.writeRawValue(s);
- }
} else {
generator.writeString(field.toString());
}
diff --git a/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java b/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java
index d1399cabc75..49df321e581 100644
--- a/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java
@@ -4,16 +4,21 @@ package com.yahoo.prelude.fastsearch;
import com.google.common.collect.ImmutableSet;
import com.yahoo.config.subscription.ConfigGetter;
import com.yahoo.data.access.slime.SlimeAdapter;
+import com.yahoo.prelude.hitfield.JSONString;
import com.yahoo.prelude.hitfield.RawData;
import com.yahoo.prelude.hitfield.XMLString;
-import com.yahoo.prelude.hitfield.JSONString;
import com.yahoo.search.result.FeatureData;
import com.yahoo.search.result.Hit;
-import com.yahoo.search.result.NanNumber;
import com.yahoo.search.result.StructuredData;
+import com.yahoo.slime.BinaryFormat;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.tensor.Tensor;
+import com.yahoo.tensor.serialization.TypedBinaryFormat;
+import org.junit.Test;
+
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
@@ -21,14 +26,6 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
-import com.yahoo.slime.BinaryFormat;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Inspector;
-import com.yahoo.slime.Slime;
-import com.yahoo.tensor.Tensor;
-import com.yahoo.tensor.serialization.TypedBinaryFormat;
-import org.junit.Test;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -102,7 +99,7 @@ public class SlimeSummaryTestCase {
if (hit.getField("jsonstring_field") instanceof JSONString) {
JSONString jstr = (JSONString) hit.getField("jsonstring_field");
assertEquals("{\"foo\":1,\"bar\":2}", jstr.getContent());
- assertNotNull(jstr.getParsedJSON());
+ assertNotNull(getParsedJSON(jstr));
com.yahoo.data.access.Inspector value = jstr.inspect();
assertEquals(1L, value.field("foo").asLong());
@@ -126,6 +123,8 @@ public class SlimeSummaryTestCase {
assertEquals(tensor2, featureData.getTensor("tensor2_feature"));
}
+ @SuppressWarnings("removal") private static Object getParsedJSON(JSONString jstr) { return jstr.getParsedJSON(); }
+
@Test
public void testFieldAccessAPI() {
DocsumDefinitionSet partialDocsum1 = createDocsumDefinitionSet(partial_summary1_cf);
diff --git a/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java b/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java
index 3cca053d0e5..80e629ca4cb 100644
--- a/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java
@@ -1,10 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.handler.test;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.container.Container;
import com.yahoo.container.core.config.testutil.HandlersConfigurerTestWrapper;
import com.yahoo.container.jdisc.HttpRequest;
-
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
import com.yahoo.container.protect.Error;
import com.yahoo.io.IOUtils;
@@ -13,8 +16,8 @@ import com.yahoo.search.handler.SearchHandler;
import com.yahoo.search.searchchain.config.test.SearchChainConfigurerTestCase;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.SlimeUtils;
-import org.json.JSONArray;
-import org.json.JSONObject;
+import com.yahoo.test.json.JsonTestHelper;
+import org.assertj.core.api.Assertions;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -23,13 +26,19 @@ import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
-import java.util.Map;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
+import java.util.Map;
import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
/**
* Tests submitting the query as JSON.
@@ -38,6 +47,8 @@ import static org.junit.Assert.*;
*/
public class JSONSearchHandlerTestCase {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final String testDir = "src/test/java/com/yahoo/search/handler/test/config";
private static final String myHostnameHeader = "my-hostname-header";
private static final String selfHostname = HostName.getLocalhost();
@@ -97,8 +108,8 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testFailing() throws Exception {
- JSONObject json = new JSONObject();
+ public void testFailing() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "test");
json.put("searchChain", "classLoadingError");
assertTrue(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE).readAll().contains("NoClassDefFoundError"));
@@ -106,16 +117,16 @@ public class JSONSearchHandlerTestCase {
@Test
- public synchronized void testPluginError() throws Exception {
- JSONObject json = new JSONObject();
+ public synchronized void testPluginError() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "test");
json.put("searchChain", "exceptionInPlugin");
assertTrue(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE).readAll().contains("NullPointerException"));
}
@Test
- public synchronized void testWorkingReconfiguration() throws Exception {
- JSONObject json = new JSONObject();
+ public synchronized void testWorkingReconfiguration() throws IOException {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "abc");
assertJsonResult(json, driver);
@@ -135,7 +146,7 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testInvalidYqlQuery() throws Exception {
+ public void testInvalidYqlQuery() throws IOException {
IOUtils.copyDirectory(new File(testDir, "config_yql"), new File(tempDir), 1);
generateComponentsConfigForActive();
configurer.reloadConfig();
@@ -143,7 +154,7 @@ public class JSONSearchHandlerTestCase {
SearchHandler newSearchHandler = fetchSearchHandler(configurer);
assertTrue("Do I have a new instance of the search handler?", searchHandler != newSearchHandler);
try (RequestHandlerTestDriver newDriver = new RequestHandlerTestDriver(newSearchHandler)) {
- JSONObject json = new JSONObject();
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("yql", "select * from foo where bar > 1453501295");
RequestHandlerTestDriver.MockResponseHandler responseHandler = newDriver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE);
responseHandler.readAll();
@@ -153,14 +164,14 @@ public class JSONSearchHandlerTestCase {
// Query handling takes a different code path when a query profile is active, so we test both paths.
@Test
- public void testInvalidQueryParamWithQueryProfile() throws Exception {
+ public void testInvalidQueryParamWithQueryProfile() throws IOException {
try (RequestHandlerTestDriver newDriver = driverWithConfig("config_invalid_param")) {
testInvalidQueryParam(newDriver);
}
}
- private void testInvalidQueryParam(final RequestHandlerTestDriver testDriver) throws Exception {
- JSONObject json = new JSONObject();
+ private void testInvalidQueryParam(final RequestHandlerTestDriver testDriver) {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "status_code:0");
json.put("hits", 20);
json.put("offset", -20);
@@ -173,16 +184,16 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testNormalResultJsonAliasRendering() throws Exception {
- JSONObject json = new JSONObject();
+ public void testNormalResultJsonAliasRendering() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("format", "json");
json.put("query", "abc");
assertJsonResult(json, driver);
}
@Test
- public void testNullQuery() throws Exception {
- JSONObject json = new JSONObject();
+ public void testNullQuery() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("format", "xml");
assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
@@ -195,8 +206,8 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testWebServiceStatus() throws Exception {
- JSONObject json = new JSONObject();
+ public void testWebServiceStatus() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "web_service_status_code");
RequestHandlerTestDriver.MockResponseHandler responseHandler =
driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE);
@@ -206,39 +217,39 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testNormalResultImplicitDefaultRendering() throws Exception {
- JSONObject json = new JSONObject();
+ public void testNormalResultImplicitDefaultRendering() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "abc");
assertJsonResult(json, driver);
}
@Test
- public void testNormalResultExplicitDefaultRendering() throws Exception {
- JSONObject json = new JSONObject();
+ public void testNormalResultExplicitDefaultRendering() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "abc");
json.put("format", "default");
assertJsonResult(json, driver);
}
@Test
- public void testNormalResultXmlAliasRendering() throws Exception {
- JSONObject json = new JSONObject();
+ public void testNormalResultXmlAliasRendering() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "abc");
json.put("format", "xml");
assertXmlResult(json, driver);
}
@Test
- public void testNormalResultExplicitDefaultRenderingFullRendererName1() throws Exception {
- JSONObject json = new JSONObject();
+ public void testNormalResultExplicitDefaultRenderingFullRendererName1() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "abc");
json.put("format", "XmlRenderer");
assertXmlResult(json, driver);
}
@Test
- public void testNormalResultExplicitDefaultRenderingFullRendererName2() throws Exception {
- JSONObject json = new JSONObject();
+ public void testNormalResultExplicitDefaultRenderingFullRendererName2() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "abc");
json.put("format", "JsonRenderer");
assertJsonResult(json, driver);
@@ -253,7 +264,7 @@ public class JSONSearchHandlerTestCase {
" </hit>\n" +
"</result>\n";
- private void assertXmlResult(JSONObject json, RequestHandlerTestDriver driver) {
+ private void assertXmlResult(JsonNode json, RequestHandlerTestDriver driver) {
assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), xmlResult);
}
@@ -263,7 +274,7 @@ public class JSONSearchHandlerTestCase {
+ "{\"id\":\"testHit\",\"relevance\":1.0,\"fields\":{\"uri\":\"testHit\"}}"
+ "]}}";
- private void assertJsonResult(JSONObject json, RequestHandlerTestDriver driver) {
+ private void assertJsonResult(JsonNode json, RequestHandlerTestDriver driver) {
assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), jsonResult);
}
@@ -288,7 +299,7 @@ public class JSONSearchHandlerTestCase {
}
- private RequestHandlerTestDriver driverWithConfig(String configDirectory) throws Exception {
+ private RequestHandlerTestDriver driverWithConfig(String configDirectory) throws IOException {
IOUtils.copyDirectory(new File(testDir, configDirectory), new File(tempDir), 1);
generateComponentsConfigForActive();
configurer.reloadConfig();
@@ -299,44 +310,44 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testSelectParameters() throws Exception {
- JSONObject json = new JSONObject();
+ public void testSelectParameters() throws IOException {
+ ObjectNode json = jsonMapper.createObjectNode();
- JSONObject select = new JSONObject();
+ ObjectNode select = jsonMapper.createObjectNode();
- JSONObject where = new JSONObject();
+ ObjectNode where = jsonMapper.createObjectNode();
where.put("where", "where");
- JSONObject grouping = new JSONObject();
+ ObjectNode grouping = jsonMapper.createObjectNode();
grouping.put("grouping", "grouping");
- select.put("where", where);
- select.put("grouping", grouping);
+ select.set("where", where);
+ select.set("grouping", grouping);
- json.put("select", select);
+ json.set("select", select);
- Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes("utf-8")).get();
+ Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes(StandardCharsets.UTF_8)).get();
Map<String, String> map = new HashMap<>();
searchHandler.createRequestMapping(inspector, map, "");
- JSONObject processedWhere = new JSONObject(map.get("select.where"));
- assertEquals(where.toString(), processedWhere.toString());
+ JsonNode processedWhere = jsonMapper.readTree(map.get("select.where"));
+ JsonTestHelper.assertJsonEquals(where.toString(), processedWhere.toString());
- JSONObject processedGrouping = new JSONObject(map.get("select.grouping"));
- assertEquals(grouping.toString(), processedGrouping.toString());
+ JsonNode processedGrouping = jsonMapper.readTree(map.get("select.grouping"));
+ JsonTestHelper.assertJsonEquals(grouping.toString(), processedGrouping.toString());
}
@Test
- public void testJsonQueryWithSelectWhere() throws Exception {
- JSONObject root = new JSONObject();
- JSONObject select = new JSONObject();
- JSONObject where = new JSONObject();
- JSONArray term = new JSONArray();
- term.put("default");
- term.put("bad");
- where.put("contains", term);
- select.put("where", where);
- root.put("select", select);
+ public void testJsonQueryWithSelectWhere() {
+ ObjectNode root = jsonMapper.createObjectNode();
+ ObjectNode select = jsonMapper.createObjectNode();
+ ObjectNode where = jsonMapper.createObjectNode();
+ ArrayNode term = jsonMapper.createArrayNode();
+ term.add("default");
+ term.add("bad");
+ where.set("contains", term);
+ select.set("where", where);
+ root.set("select", select);
// Run query
String result = driver.sendRequest(uri + "searchChain=echoingQuery", com.yahoo.jdisc.http.HttpRequest.Method.POST, root.toString(), JSON_CONTENT_TYPE).readAll();
@@ -393,8 +404,8 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testJsonQueryWithYQL() throws Exception {
- JSONObject root = new JSONObject();
+ public void testJsonQueryWithYQL() {
+ ObjectNode root = jsonMapper.createObjectNode();
root.put("yql", "select * from sources * where default contains 'bad';");
// Run query
@@ -404,10 +415,10 @@ public class JSONSearchHandlerTestCase {
}
@Test
- public void testRequestMapping() throws Exception {
- JSONObject json = new JSONObject();
+ public void testRequestMapping() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("yql", "select * from sources * where sddocname contains \"blog_post\" limit 0 | all(group(date) max(3) order(-count())each(output(count())));");
- json.put("hits", 10.0);
+ json.put("hits", 10);
json.put("offset", 5);
json.put("queryProfile", "foo");
json.put("nocache", false);
@@ -417,7 +428,7 @@ public class JSONSearchHandlerTestCase {
json.put("select", "_all");
- JSONObject model = new JSONObject();
+ ObjectNode model = jsonMapper.createObjectNode();
model.put("defaultIndex", 1);
model.put("encoding", "json");
model.put("filter", "default");
@@ -427,9 +438,9 @@ public class JSONSearchHandlerTestCase {
model.put("searchPath", "node1");
model.put("sources", "source1,source2");
model.put("type", "yql");
- json.put("model", model);
+ json.set("model", model);
- JSONObject ranking = new JSONObject();
+ ObjectNode ranking = jsonMapper.createObjectNode();
ranking.put("location", "123789.89123N;128123W");
ranking.put("features", "none");
ranking.put("listFeatures", false);
@@ -439,61 +450,61 @@ public class JSONSearchHandlerTestCase {
ranking.put("freshness", "0.05");
ranking.put("queryCache", false);
- JSONObject matchPhase = new JSONObject();
+ ObjectNode matchPhase = jsonMapper.createObjectNode();
matchPhase.put("maxHits", "100");
matchPhase.put("attribute", "title");
matchPhase.put("ascending", true);
- JSONObject diversity = new JSONObject();
+ ObjectNode diversity = jsonMapper.createObjectNode();
diversity.put("attribute", "title");
diversity.put("minGroups", 1);
- matchPhase.put("diversity", diversity);
- ranking.put("matchPhase", matchPhase);
- json.put("ranking", ranking);
+ matchPhase.set("diversity", diversity);
+ ranking.set("matchPhase", matchPhase);
+ json.set("ranking", ranking);
- JSONObject presentation = new JSONObject();
+ ObjectNode presentation = jsonMapper.createObjectNode();
presentation.put("bolding", true);
presentation.put("format", "json");
presentation.put("summary", "none");
presentation.put("template", "json");
presentation.put("timing", false);
- json.put("presentation", presentation);
+ json.set("presentation", presentation);
- JSONObject collapse = new JSONObject();
+ ObjectNode collapse = jsonMapper.createObjectNode();
collapse.put("field", "none");
collapse.put("size", 2);
collapse.put("summary", "default");
- json.put("collapse", collapse);
+ json.set("collapse", collapse);
- JSONObject trace = new JSONObject();
+ ObjectNode trace = jsonMapper.createObjectNode();
trace.put("level", 1);
trace.put("timestamps", false);
trace.put("rules", "none");
- json.put("trace", trace);
+ json.set("trace", trace);
- JSONObject pos = new JSONObject();
+ ObjectNode pos = jsonMapper.createObjectNode();
pos.put("ll", "1263123N;1231.9W");
pos.put("radius", "71234m");
pos.put("bb", "1237123W;123218N");
pos.put("attribute", "default");
- json.put("pos", pos);
+ json.set("pos", pos);
- JSONObject streaming = new JSONObject();
+ ObjectNode streaming = jsonMapper.createObjectNode();
streaming.put("userid", 123);
streaming.put("groupname", "abc");
streaming.put("selection", "none");
streaming.put("priority", 10);
streaming.put("maxbucketspervisitor", 5);
- json.put("streaming", streaming);
+ json.set("streaming", streaming);
- JSONObject rules = new JSONObject();
+ ObjectNode rules = jsonMapper.createObjectNode();
rules.put("off", false);
rules.put("rulebase", "default");
- json.put("rules", rules);
+ json.set("rules", rules);
- JSONObject metrics = new JSONObject();
+ ObjectNode metrics = jsonMapper.createObjectNode();
metrics.put("ignore", "_all");
- json.put("metrics", metrics);
+ json.set("metrics", metrics);
json.put("recall", "none");
json.put("user", 123);
@@ -501,7 +512,7 @@ public class JSONSearchHandlerTestCase {
json.put("hitcountestimate", true);
// Create mapping
- Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes("utf-8")).get();
+ Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes(StandardCharsets.UTF_8)).get();
Map<String, String> map = new HashMap<>();
searchHandler.createRequestMapping(inspector, map, "");
@@ -518,12 +529,12 @@ public class JSONSearchHandlerTestCase {
// Get mapping
Map<String, String> propertyMap = request.propertyMap();
- assertEquals("Should have same mapping for properties", map, propertyMap);
+ Assertions.assertThat(propertyMap).isEqualTo(map);
}
@Test
- public void testContentTypeParsing() throws Exception {
- JSONObject json = new JSONObject();
+ public void testContentTypeParsing() {
+ ObjectNode json = jsonMapper.createObjectNode();
json.put("query", "abc");
assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), "Application/JSON; charset=utf-8"), jsonResult);
}
diff --git a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
index c4d49c11f5e..48003f6586f 100644
--- a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.rendering;
-import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture;
@@ -56,9 +55,6 @@ import com.yahoo.tensor.TensorType;
import com.yahoo.tensor.serialization.TypedBinaryFormat;
import com.yahoo.text.Utf8;
import com.yahoo.yolean.trace.TraceNode;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
@@ -67,7 +63,6 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -83,6 +78,8 @@ import static org.junit.Assert.assertTrue;
*/
public class JsonRendererTestCase {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private JsonRenderer originalRenderer;
private JsonRenderer renderer;
@@ -959,7 +956,7 @@ public class JsonRendererTestCase {
}
@Test
- public void testJsonObjects() throws InterruptedException, ExecutionException, IOException, JSONException {
+ public void testJsonObjects() throws InterruptedException, ExecutionException, IOException {
String expected = "{"
+ " \"root\": {"
+ " \"children\": ["
@@ -973,14 +970,6 @@ public class JsonRendererTestCase {
+ " },"
+ " \"json producer\": {"
+ " \"long in structured\": 7809531904"
- + " },"
- + " \"org.json array\": ["
- + " true,"
- + " true,"
- + " false"
- + " ],"
- + " \"org.json object\": {"
- + " \"forty-two\": 42"
+ " }"
+ " },"
+ " \"id\": \"json objects\","
@@ -996,26 +985,17 @@ public class JsonRendererTestCase {
+ "}";
Result r = newEmptyResult();
Hit h = new Hit("json objects");
- JSONObject o = new JSONObject();
- JSONArray a = new JSONArray();
- ObjectMapper mapper = new ObjectMapper();
- JsonNode j = mapper.createObjectNode();
+ ObjectNode j = jsonMapper.createObjectNode();
JSONString s = new JSONString("{\"a\": \"b\"}");
Slime slime = new Slime();
Cursor c = slime.setObject();
c.setLong("long in structured", 7809531904L);
SlimeAdapter slimeInit = new SlimeAdapter(slime.get());
StructuredData struct = new StructuredData(slimeInit);
- ((ObjectNode) j).put("Nineteen-eighty-four", 1984);
- o.put("forty-two", 42);
- a.put(true);
- a.put(true);
- a.put(false);
+ j.put("Nineteen-eighty-four", 1984);
h.setField("inspectable", s);
h.setField("jackson", j);
h.setField("json producer", struct);
- h.setField("org.json array", a);
- h.setField("org.json object", o);
r.hits().add(h);
String summary = render(r);
assertEqualJson(expected, summary);
@@ -1236,11 +1216,13 @@ public class JsonRendererTestCase {
public void testThatTheJsonValidatorCanCatchErrors() {
String json = "{"
+ " \"root\": {"
- + " \"duplicate\": 1,"
- + " \"duplicate\": 2"
+ + " \"invalidvalue\": 1adsf,"
+ " }"
+ "}";
- assertEquals("Duplicate key \"duplicate\"", validateJSON(json));
+ assertEquals(
+ "Unexpected character ('a' (code 97)): was expecting comma to separate Object entries\n" +
+ " at [Source: { \"root\": { \"invalidvalue\": 1adsf, }}; line: 1, column: 41]",
+ validateJSON(json));
}
@Test
@@ -1316,9 +1298,9 @@ public class JsonRendererTestCase {
private String validateJSON(String presumablyValidJson) {
try {
- new JSONObject(presumablyValidJson);
+ jsonMapper.readTree(presumablyValidJson);
return "";
- } catch (JSONException e) {
+ } catch (IOException e) {
return e.getMessage();
}
}
diff --git a/container-search/src/test/java/com/yahoo/select/SelectTestCase.java b/container-search/src/test/java/com/yahoo/select/SelectTestCase.java
index 3239a97a094..9bcd3addd92 100644
--- a/container-search/src/test/java/com/yahoo/select/SelectTestCase.java
+++ b/container-search/src/test/java/com/yahoo/select/SelectTestCase.java
@@ -1,6 +1,9 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.select;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.prelude.query.AndItem;
import com.yahoo.prelude.query.ExactStringItem;
import com.yahoo.prelude.query.Item;
@@ -17,19 +20,15 @@ import com.yahoo.prelude.query.WordItem;
import com.yahoo.processing.IllegalInputException;
import com.yahoo.search.Query;
import com.yahoo.search.grouping.GroupingRequest;
-import com.yahoo.search.grouping.request.AllOperation;
import com.yahoo.search.query.QueryTree;
import com.yahoo.search.query.Select;
import com.yahoo.search.query.SelectParser;
import com.yahoo.search.query.parser.Parsable;
import com.yahoo.search.query.parser.ParserEnvironment;
import com.yahoo.search.yql.VespaGroupingStep;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
@@ -48,15 +47,18 @@ import static org.junit.Assert.fail;
*/
public class SelectTestCase {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private final SelectParser parser = new SelectParser(new ParserEnvironment());
//------------------------------------------------------------------- "where" tests
@Test
- public void test_contains() throws Exception {
- JSONObject json = new JSONObject();
- List<String> contains = Arrays.asList("default", "foo");
- json.put("contains", contains);
+ public void test_contains() {
+ ObjectNode json = jsonMapper.createObjectNode();
+ ArrayNode arrayNode = jsonMapper.createArrayNode();
+ arrayNode.add("default").add("foo");
+ json.set("contains", arrayNode);
assertParse(json.toString(), "default:foo");
}
@@ -77,21 +79,21 @@ public class SelectTestCase {
@Test
public void testOr() throws Exception {
- JSONObject json_two_or = new JSONObject();
- JSONObject json_three_or = new JSONObject();
- List<String> contains1 = Arrays.asList("title", "madonna");
- List<String> contains2 = Arrays.asList("title", "saint");
- List<String> contains3 = Arrays.asList("title", "angel");
-
- JSONObject contains_json1 = new JSONObject();
- JSONObject contains_json2 = new JSONObject();
- JSONObject contains_json3 = new JSONObject();
- contains_json1.put("contains", contains1);
- contains_json2.put("contains", contains2);
- contains_json3.put("contains", contains3);
-
- json_two_or.put("or", Arrays.asList(contains_json1, contains_json2));
- json_three_or.put("or", Arrays.asList(contains_json1, contains_json2, contains_json3));
+ ObjectNode json_two_or = jsonMapper.createObjectNode();
+ ObjectNode json_three_or = jsonMapper.createObjectNode();
+ ArrayNode contains1 = jsonMapper.createArrayNode().add("title").add("madonna");
+ ArrayNode contains2 = jsonMapper.createArrayNode().add("title").add("saint");
+ ArrayNode contains3 = jsonMapper.createArrayNode().add("title").add("angel");
+
+ ObjectNode contains_json1 = jsonMapper.createObjectNode();
+ ObjectNode contains_json2 = jsonMapper.createObjectNode();
+ ObjectNode contains_json3 = jsonMapper.createObjectNode();
+ contains_json1.set("contains", contains1);
+ contains_json2.set("contains", contains2);
+ contains_json3.set("contains", contains3);
+
+ json_two_or.set("or", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2));
+ json_three_or.set("or", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2).add(contains_json3));
assertParse(json_two_or.toString(), "OR title:madonna title:saint");
assertParse(json_three_or.toString(), "OR title:madonna title:saint title:angel");
@@ -99,178 +101,178 @@ public class SelectTestCase {
@Test
public void testAnd() throws Exception{
- JSONObject json_two_and = new JSONObject();
- JSONObject json_three_and = new JSONObject();
- List<String> contains1 = Arrays.asList("title", "madonna");
- List<String> contains2 = Arrays.asList("title", "saint");
- List<String> contains3 = Arrays.asList("title", "angel");
-
- JSONObject contains_json1 = new JSONObject();
- JSONObject contains_json2 = new JSONObject();
- JSONObject contains_json3 = new JSONObject();
- contains_json1.put("contains", contains1);
- contains_json2.put("contains", contains2);
- contains_json3.put("contains", contains3);
-
- json_two_and.put("and", Arrays.asList(contains_json1, contains_json2));
- json_three_and.put("and", Arrays.asList(contains_json1, contains_json2, contains_json3));
+ ObjectNode json_two_and = jsonMapper.createObjectNode();
+ ObjectNode json_three_and = jsonMapper.createObjectNode();
+ ArrayNode contains1 = jsonMapper.createArrayNode().add("title").add("madonna");
+ ArrayNode contains2 = jsonMapper.createArrayNode().add("title").add("saint");
+ ArrayNode contains3 = jsonMapper.createArrayNode().add("title").add("angel");
+
+ ObjectNode contains_json1 = jsonMapper.createObjectNode();
+ ObjectNode contains_json2 = jsonMapper.createObjectNode();
+ ObjectNode contains_json3 = jsonMapper.createObjectNode();
+ contains_json1.set("contains", contains1);
+ contains_json2.set("contains", contains2);
+ contains_json3.set("contains", contains3);
+
+ json_two_and.set("and", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2));
+ json_three_and.set("and", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2).add(contains_json3));
assertParse(json_two_and.toString(), "AND title:madonna title:saint");
assertParse(json_three_and.toString(), "AND title:madonna title:saint title:angel");
}
@Test
- public void testAndNot() throws JSONException {
- JSONObject json_and_not = new JSONObject();
- List<String> contains1 = Arrays.asList("title", "madonna");
- List<String> contains2 = Arrays.asList("title", "saint");
+ public void testAndNot() {
+ ObjectNode json_and_not = jsonMapper.createObjectNode();
+ ArrayNode contains1 = jsonMapper.createArrayNode().add("title").add("madonna");
+ ArrayNode contains2 = jsonMapper.createArrayNode().add("title").add("saint");
- JSONObject contains_json1 = new JSONObject();
- JSONObject contains_json2 = new JSONObject();
- contains_json1.put("contains", contains1);
- contains_json2.put("contains", contains2);
+ ObjectNode contains_json1 = jsonMapper.createObjectNode();
+ ObjectNode contains_json2 = jsonMapper.createObjectNode();
+ contains_json1.set("contains", contains1);
+ contains_json2.set("contains", contains2);
- json_and_not.put("and_not", Arrays.asList(contains_json1, contains_json2));
+ json_and_not.set("and_not", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2));
assertParse(json_and_not.toString(),
"+title:madonna -title:saint");
}
@Test
- public void testLessThan() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testLessThan() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put("<", 500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:<500");
}
@Test
- public void testGreaterThan() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testGreaterThan() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put(">", 500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:>500");
}
@Test
- public void testLessThanOrEqual() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testLessThanOrEqual() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put("<=", 500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:[;500]");
}
@Test
- public void testGreaterThanOrEqual() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testGreaterThanOrEqual() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put(">=", 500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:[500;]");
}
@Test
- public void testEquality() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testEquality() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put("=", 500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:500");
}
@Test
- public void testNegativeLessThan() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testNegativeLessThan() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put("<", -500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:<-500");
}
@Test
- public void testNegativeGreaterThan() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testNegativeGreaterThan() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put(">", -500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:>-500");
}
@Test
- public void testNegativeLessThanOrEqual() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testNegativeLessThanOrEqual() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put("<=", -500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:[;-500]");
}
@Test
- public void testNegativeGreaterThanOrEqual() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testNegativeGreaterThanOrEqual() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put(">=", -500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:[-500;]");
}
@Test
- public void testNegativeEquality() throws JSONException {
- JSONObject range_json = new JSONObject();
- JSONObject operators = new JSONObject();
+ public void testNegativeEquality() {
+ ObjectNode range_json = jsonMapper.createObjectNode();
+ ObjectNode operators = jsonMapper.createObjectNode();
operators.put("=", -500);
- List<Object> range = Arrays.asList("price", operators);
+ ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators);
- range_json.put("range", range);
+ range_json.set("range", range);
assertParse(range_json.toString(),
"price:-500");
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java
index c6d907ec7fc..ed88902e094 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java
@@ -1,14 +1,19 @@
package com.yahoo.vespa.hosted.controller.api.application.v4.model;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
import java.util.HashMap;
import java.util.Map;
+import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
-import org.json.JSONException;
-import org.json.JSONObject;
public class ProtonMetrics {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final Logger logger = LogManager.getLogManager().getLogger(ProtonMetrics.class.getName());
public static final String DOCUMENTS_ACTIVE_COUNT = "documentsActiveCount";
@@ -45,19 +50,19 @@ public class ProtonMetrics {
return this;
}
- public JSONObject toJson() {
+ public JsonNode toJson() {
try {
- JSONObject protonMetrics = new JSONObject();
+ ObjectNode protonMetrics = jsonMapper.createObjectNode();
protonMetrics.put("clusterId", clusterId);
- JSONObject jsonMetrics = new JSONObject();
+ ObjectNode jsonMetrics = jsonMapper.createObjectNode();
for (Map.Entry<String, Double> entry : metrics.entrySet()) {
jsonMetrics.put(entry.getKey(), entry.getValue());
}
- protonMetrics.put("metrics", jsonMetrics);
+ protonMetrics.set("metrics", jsonMetrics);
return protonMetrics;
- } catch (JSONException e) {
- logger.severe("Unable to convert Proton Metrics to JSON Object");
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Unable to convert Proton Metrics to JSON Object: " + e.getMessage(), e);
}
- return new JSONObject();
+ return jsonMapper.createObjectNode();
}
}
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 76fa52c0706..87531240752 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
@@ -1,6 +1,5 @@
package com.yahoo.vespa.hosted.controller.certificate;
-import com.google.common.collect.Sets;
import com.google.common.hash.Hashing;
import com.google.common.io.BaseEncoding;
import com.yahoo.config.application.api.DeploymentInstanceSpec;
@@ -14,19 +13,15 @@ import com.yahoo.container.jdisc.secretstore.SecretNotFoundException;
import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.security.SubjectAlternativeName;
import com.yahoo.security.X509CertificateUtils;
-import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.flags.BooleanFlag;
-import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.StringFlag;
import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateProvider;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
-import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import org.jetbrains.annotations.NotNull;
@@ -35,17 +30,12 @@ import java.security.cert.X509Certificate;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
-import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
-import java.util.OptionalInt;
import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -54,6 +44,8 @@ import java.util.stream.Collectors;
* Looks up stored endpoint certificate metadata, provisions new certificates if none is found,
* re-provisions if zone is not covered, and uses refreshed certificates if a newer version is available.
*
+ * See also EndpointCertificateMaintainer, which handles refreshes, deletions and triggers deployments
+ *
* @author andreer
*/
public class EndpointCertificateManager {
@@ -66,9 +58,6 @@ public class EndpointCertificateManager {
private final EndpointCertificateProvider endpointCertificateProvider;
private final Clock clock;
private final BooleanFlag validateEndpointCertificates;
- private final StringFlag deleteUnusedEndpointCertificates;
- private final BooleanFlag endpointCertInSharedRouting;
- private final BooleanFlag useEndpointCertificateMaintainer;
public EndpointCertificateManager(ZoneRegistry zoneRegistry,
CuratorDb curator,
@@ -81,16 +70,6 @@ public class EndpointCertificateManager {
this.endpointCertificateProvider = endpointCertificateProvider;
this.clock = clock;
this.validateEndpointCertificates = Flags.VALIDATE_ENDPOINT_CERTIFICATES.bindTo(flagSource);
- this.deleteUnusedEndpointCertificates = Flags.DELETE_UNUSED_ENDPOINT_CERTIFICATES.bindTo(flagSource);
- this.endpointCertInSharedRouting = Flags.ENDPOINT_CERT_IN_SHARED_ROUTING.bindTo(flagSource);
- this.useEndpointCertificateMaintainer = Flags.USE_ENDPOINT_CERTIFICATE_MAINTAINER.bindTo(flagSource);
- Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
- try {
- this.deleteUnusedCertificates();
- } catch (Throwable t) {
- log.log(Level.INFO, "Unexpected Throwable caught while deleting unused endpoint certificates", t);
- }
- }, 1, 10, TimeUnit.MINUTES);
}
public Optional<EndpointCertificateMetadata> getEndpointCertificateMetadata(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
@@ -105,10 +84,6 @@ public class EndpointCertificateManager {
@NotNull
private Optional<EndpointCertificateMetadata> getOrProvision(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
- boolean endpointCertInSharedRouting = this.endpointCertInSharedRouting.with(FetchVector.Dimension.APPLICATION_ID, instance.id().serializedForm()).value();
- if (!zoneRegistry.zones().directlyRouted().ids().contains(zone) && !endpointCertInSharedRouting)
- return Optional.empty();
-
final var currentCertificateMetadata = curator.readEndpointCertificateMetadata(instance.id());
if (currentCertificateMetadata.isEmpty()) {
@@ -130,74 +105,10 @@ public class EndpointCertificateManager {
return Optional.of(reprovisionedCertificateMetadata);
}
- if (!useEndpointCertificateMaintainer.value()) {
- // Look for and use refreshed certificate
- var latestAvailableVersion = latestVersionInSecretStore(currentCertificateMetadata.get());
- if (latestAvailableVersion.isPresent() && latestAvailableVersion.getAsInt() > currentCertificateMetadata.get().version()) {
- var refreshedCertificateMetadata = currentCertificateMetadata.get()
- .withVersion(latestAvailableVersion.getAsInt())
- .withLastRefreshed(clock.instant().getEpochSecond());
- validateEndpointCertificate(refreshedCertificateMetadata, instance, zone);
- curator.writeEndpointCertificateMetadata(instance.id(), refreshedCertificateMetadata);
- return Optional.of(refreshedCertificateMetadata);
- }
- }
-
validateEndpointCertificate(currentCertificateMetadata.get(), instance, zone);
return currentCertificateMetadata;
}
- enum CleanupMode {
- DISABLE,
- DRYRUN,
- ENABLE
- }
-
- private void deleteUnusedCertificates() {
- CleanupMode mode = CleanupMode.valueOf(deleteUnusedEndpointCertificates.value().toUpperCase());
- if (mode == CleanupMode.DISABLE || useEndpointCertificateMaintainer.value()) return;
-
- var oneMonthAgo = clock.instant().minus(30, ChronoUnit.DAYS);
- curator.readAllEndpointCertificateMetadata().forEach((applicationId, storedMetaData) -> {
- var lastRequested = Instant.ofEpochSecond(storedMetaData.lastRequested());
- if (lastRequested.isBefore(oneMonthAgo) && hasNoDeployments(applicationId)) {
- try (Lock lock = lock(applicationId)) {
- if (Optional.of(storedMetaData).equals(curator.readEndpointCertificateMetadata(applicationId))) {
- 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) {
- endpointCertificateProvider.deleteCertificate(applicationId, storedMetaData);
- curator.deleteEndpointCertificateMetadata(applicationId);
- }
- }
- }
- }
- });
- }
-
- private Lock lock(ApplicationId applicationId) {
- return curator.lock(TenantAndApplicationId.from(applicationId));
- }
-
- private boolean hasNoDeployments(ApplicationId applicationId) {
- var deployments = curator.readApplication(TenantAndApplicationId.from(applicationId))
- .flatMap(app -> app.get(applicationId.instance()))
- .map(Instance::deployments);
-
- return deployments.isEmpty() || deployments.get().size() == 0;
- }
-
- private OptionalInt latestVersionInSecretStore(EndpointCertificateMetadata originalCertificateMetadata) {
- try {
- var certVersions = new HashSet<>(secretStore.listSecretVersions(originalCertificateMetadata.certName()));
- var keyVersions = new HashSet<>(secretStore.listSecretVersions(originalCertificateMetadata.keyName()));
- return Sets.intersection(certVersions, keyVersions).stream().mapToInt(Integer::intValue).max();
- } catch (SecretNotFoundException s) {
- return OptionalInt.empty(); // Likely because the certificate is very recently provisioned - keep current version
- }
- }
-
private EndpointCertificateMetadata provisionEndpointCertificate(Instance instance, Optional<EndpointCertificateMetadata> currentMetadata, ZoneId deploymentZone, Optional<DeploymentInstanceSpec> instanceSpec) {
List<String> currentlyPresentNames = currentMetadata.isPresent() ?
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java
index 786547d4a67..8a48cbd281d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueue.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.dns;
import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService;
+import com.yahoo.yolean.Exceptions;
import java.util.ArrayList;
import java.util.Collection;
@@ -82,7 +83,7 @@ public class NameServiceQueue {
request.dispatchTo(nameService);
queue.requests.poll();
} catch (Exception e) {
- log.log(Level.WARNING, "Failed to execute " + request + ": " + e.getMessage() +
+ log.log(Level.WARNING, "Failed to execute " + request + ": " + Exceptions.toMessageString(e) +
", request will be retried");
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
index 56b7e5b2e46..979cd9060d9 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
@@ -118,13 +118,13 @@ public class ControllerMaintenance extends AbstractComponent {
this.outstandingChangeDeployer = duration(3, MINUTES);
this.versionStatusUpdater = duration(3, MINUTES);
this.readyJobsTrigger = duration(1, MINUTES);
- this.deploymentMetricsMaintainer = duration(5, MINUTES);
+ this.deploymentMetricsMaintainer = duration(10, MINUTES);
this.applicationOwnershipConfirmer = duration(12, HOURS);
- this.systemUpgrader = duration(1, MINUTES);
+ this.systemUpgrader = duration(90, SECONDS);
this.jobRunner = duration(90, SECONDS);
this.osUpgrader = duration(1, MINUTES);
this.contactInformationMaintainer = duration(12, HOURS);
- this.nameServiceDispatcher = duration(10, SECONDS);
+ this.nameServiceDispatcher = duration(30, SECONDS);
this.costReportMaintainer = duration(2, HOURS);
this.resourceMeterMaintainer = duration(1, MINUTES);
this.cloudEventReporter = duration(30, MINUTES);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java
index a1d7c3d16b4..e59875e9588 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java
@@ -8,8 +8,6 @@ import com.yahoo.container.jdisc.secretstore.SecretNotFoundException;
import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.curator.Lock;
-import com.yahoo.vespa.flags.BooleanFlag;
-import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
@@ -27,12 +25,13 @@ import java.time.temporal.ChronoUnit;
import java.util.HashSet;
import java.util.Optional;
import java.util.OptionalInt;
-import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * Updates refreshed endpoint certificates and triggers redeployment, and deletes unused certificates
+ * Updates refreshed endpoint certificates and triggers redeployment, and deletes unused certificates.
+ *
+ * See also EndpointCertificateManager, which provisions, reprovisions and validates certificates on deploy
*
* @author andreer
*/
@@ -45,7 +44,6 @@ public class EndpointCertificateMaintainer extends ControllerMaintainer {
private final CuratorDb curator;
private final SecretStore secretStore;
private final EndpointCertificateProvider endpointCertificateProvider;
- private final BooleanFlag useEndpointCertificateMaintainer;
public EndpointCertificateMaintainer(Controller controller, Duration interval) {
super(controller, interval, null, SystemName.all());
@@ -54,15 +52,10 @@ public class EndpointCertificateMaintainer extends ControllerMaintainer {
this.secretStore = controller.secretStore();
this.curator = controller().curator();
this.endpointCertificateProvider = controller.serviceRegistry().endpointCertificateProvider();
- this.useEndpointCertificateMaintainer = Flags.USE_ENDPOINT_CERTIFICATE_MAINTAINER.bindTo(controller().flagSource());
}
@Override
protected boolean maintain() {
-
- if (!useEndpointCertificateMaintainer.value())
- return true; // handled by EndpointCertificateManager for now
-
try {
// In order of importance
deployRefreshedCertificates();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java
index d6739581c79..73528977166 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java
@@ -80,10 +80,12 @@ public class JobRunner extends ControllerMaintainer {
/** Advances each of the ready steps for the given run, or marks it as finished, and stashes it. Public for testing. */
public void advance(Run run) {
if ( ! run.hasFailed()
- && controller().clock().instant().isAfter(run.start().plus(jobTimeout))) {
- jobs.abort(run.id());
- advance(jobs.run(run.id()).get());
- }
+ && controller().clock().instant().isAfter(run.start().plus(jobTimeout)))
+ executors.execute(() -> {
+ jobs.abort(run.id());
+ advance(jobs.run(run.id()).get());
+ });
+
else if (run.readySteps().isEmpty())
executors.execute(() -> finish(run.id()));
else
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 5a1496bf507..9331e5086cc 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
@@ -2,6 +2,8 @@
package com.yahoo.vespa.hosted.controller.restapi.application;
import ai.vespa.hosted.api.Signatures;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
@@ -100,9 +102,6 @@ import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
import com.yahoo.vespa.serviceview.bindings.ApplicationView;
import com.yahoo.yolean.Exceptions;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.InternalServerErrorException;
@@ -151,6 +150,8 @@ import static java.util.stream.Collectors.toUnmodifiableList;
@SuppressWarnings("unused") // created by injection
public class ApplicationApiHandler extends LoggingRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final String OPTIONAL_PREFIX = "/api";
private final Controller controller;
@@ -789,15 +790,15 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private JsonResponse buildResponseFromProtonMetrics(List<ProtonMetrics> protonMetrics) {
try {
- var jsonObject = new JSONObject();
- var jsonArray = new JSONArray();
+ var jsonObject = jsonMapper.createObjectNode();
+ var jsonArray = jsonMapper.createArrayNode();
for (ProtonMetrics metrics : protonMetrics) {
- jsonArray.put(metrics.toJson());
+ jsonArray.add(metrics.toJson());
}
- jsonObject.put("metrics", jsonArray);
- return new JsonResponse(200, jsonObject.toString());
- } catch (JSONException e) {
- log.severe("Unable to build JsonResponse with Proton data");
+ jsonObject.set("metrics", jsonArray);
+ return new JsonResponse(200, jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject));
+ } catch (JsonProcessingException e) {
+ log.log(Level.SEVERE, "Unable to build JsonResponse with Proton data: " + e.getMessage(), e);
return new JsonResponse(500, "");
}
}
@@ -1682,11 +1683,20 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
controller.jobController().deploy(id, type, version, applicationPackage);
RunId runId = controller.jobController().last(id, type).get().id();
+ DeploymentId deploymentId = new DeploymentId(id, type.zone(controller.system()));
+
Slime slime = new Slime();
Cursor rootObject = slime.setObject();
rootObject.setString("message", "Deployment started in " + runId +
". This may take about 15 minutes the first time.");
rootObject.setLong("run", runId.number());
+ var endpointArray = rootObject.setArray("endpoints");
+ EndpointList zoneEndpoints = controller.routing().endpointsOf(deploymentId)
+ .scope(Endpoint.Scope.zone)
+ .not().legacy();
+ for (var endpoint : controller.routing().directEndpoints(zoneEndpoints, deploymentId.applicationId())) {
+ toSlime(endpoint, endpointArray.addObject());
+ }
return new SlimeJsonResponse(slime);
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index 8fcbb365804..63452f40dbb 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -736,8 +736,8 @@ public class ControllerTest {
.map(prefix -> prefix + "app1.tenant1." + zone.region().value() +
(zone.environment() == Environment.prod ? "" : "." + zone.environment().value()) +
".vespa.oath.cloud")))
- .collect(Collectors.toUnmodifiableList()),
- tester.controllerTester().serviceRegistry().endpointCertificateMock().dnsNamesOf(context1.instanceId()));
+ .collect(Collectors.toUnmodifiableSet()),
+ Set.copyOf(tester.controllerTester().serviceRegistry().endpointCertificateMock().dnsNamesOf(context1.instanceId())));
// Next deployment reuses certificate
context1.submit(applicationPackage).deploy();
@@ -751,7 +751,7 @@ public class ControllerTest {
tester.controller().applications().deploy(context2.instanceId(), devZone, Optional.of(applicationPackage), DeployOptions.none());
assertTrue("Application deployed and activated",
tester.configServer().application(context2.instanceId(), devZone).get().activated());
- assertFalse("Does not provision certificate in zones with routing layer", certificate.apply(context2.instance()).isPresent());
+ assertTrue("Provisions certificate also in zone with routing layer", certificate.apply(context2.instance()).isPresent());
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java
index 4f2000b1902..0d70fe48a77 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java
@@ -146,27 +146,6 @@ public class EndpointCertificateManagerTest {
}
@Test
- public void uses_refreshed_certificate_when_available_and_valid() {
- secretStore.setSecret(testKeyName, "secret-key", 7);
- secretStore.setSecret(testCertName, "cert", 7);
- secretStore.setSecret(testKeyName, KeyUtils.toPem(testKeyPair.getPrivate()), 8);
- secretStore.setSecret(testKeyName, KeyUtils.toPem(testKeyPair.getPrivate()), 9);
- secretStore.setSecret(testCertName, X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 8);
- mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, 7, 0, "request_id",
- List.of("vt2ktgkqme5zlnp4tj4ttyor7fj3v7q5o.vespa.oath.cloud",
- "default.default.global.vespa.oath.cloud",
- "*.default.default.global.vespa.oath.cloud",
- "default.default.aws-us-east-1a.vespa.oath.cloud",
- "*.default.default.aws-us-east-1a.vespa.oath.cloud"),
- "issuer", Optional.empty(), Optional.empty()));
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificateManager.getEndpointCertificateMetadata(testInstance, testZone, Optional.empty());
- assertTrue(endpointCertificateMetadata.isPresent());
- assertEquals(testKeyName, endpointCertificateMetadata.get().keyName());
- assertEquals(testCertName, endpointCertificateMetadata.get().certName());
- assertEquals(8, endpointCertificateMetadata.get().version());
- }
-
- @Test
public void reprovisions_certificate_when_necessary() {
mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, 0, "uuid", List.of(), "issuer", Optional.empty(), Optional.empty()));
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java
index dbf102f23d7..66bda66bbf9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java
@@ -2,15 +2,12 @@
package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock;
-import org.junit.Before;
import org.junit.Test;
import java.time.Duration;
@@ -33,11 +30,6 @@ public class EndpointCertificateMaintainerTest {
private final EndpointCertificateMaintainer maintainer = new EndpointCertificateMaintainer(tester.controller(), Duration.ofHours(1));
private final EndpointCertificateMetadata exampleMetadata = new EndpointCertificateMetadata("keyName", "certName", 0, 0, "uuid", List.of(), "issuer", Optional.empty(), Optional.empty());
- @Before
- public void setUp() throws Exception {
- ((InMemoryFlagSource) tester.controller().flagSource()).withBooleanFlag(Flags.USE_ENDPOINT_CERTIFICATE_MAINTAINER.id(), true);
- }
-
@Test
public void old_and_unused_cert_is_deleted() {
tester.curator().writeEndpointCertificateMetadata(ApplicationId.defaultId(), exampleMetadata);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java
index bda5a708a94..2bf6eb39089 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.ApplicationName;
import com.yahoo.container.http.filter.FilterChainRepository;
import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
import com.yahoo.jdisc.http.filter.SecurityRequestFilterChain;
+import com.yahoo.test.json.JsonTestHelper;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -65,6 +66,10 @@ public class ContainerTester {
.addRoleMember(action, identity);
}
+ public void assertJsonResponse(Supplier<Request> request, File responseFile) {
+ assertResponse(request.get(), responseFile, 200, false, true);
+ }
+
public void assertResponse(Supplier<Request> request, File responseFile) {
assertResponse(request.get(), responseFile);
}
@@ -82,6 +87,10 @@ public class ContainerTester {
}
public void assertResponse(Request request, File responseFile, int expectedStatusCode, boolean removeWhitespace) {
+ assertResponse(request, responseFile, expectedStatusCode, removeWhitespace, false);
+ }
+
+ private void assertResponse(Request request, File responseFile, int expectedStatusCode, boolean removeWhitespace, boolean compareJson) {
String expectedResponse = readTestFile(responseFile.toString());
expectedResponse = include(expectedResponse);
if (removeWhitespace) expectedResponse = expectedResponse.replaceAll("(\"[^\"]*\")|\\s*", "$1"); // Remove whitespace
@@ -106,7 +115,11 @@ public class ContainerTester {
expectedResponsePattern, responseString);
}
} else {
- assertEquals(responseFile.toString(), expectedResponse, responseString);
+ if (compareJson) {
+ JsonTestHelper.assertJsonEquals(expectedResponse, responseString);
+ } else {
+ assertEquals(responseFile.toString(), expectedResponse, responseString);
+ }
}
assertEquals("Status code", expectedStatusCode, response.getStatus());
}
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 434c83898ee..6626134b69a 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
@@ -242,7 +242,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/production-us-east-3/", POST)
.data(entity)
.userIdentity(HOSTED_VESPA_OPERATOR),
- "{\"message\":\"Deployment started in run 1 of production-us-east-3 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}");
+ "{\"message\":\"Deployment started in run 1 of production-us-east-3 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1," +
+ "\"endpoints\":[{\"cluster\":\"default\",\"tls\":true,\"url\":\"https://instance1--application1--tenant1.us-east-3.vespa.oath.cloud:4443/\",\"scope\":\"zone\",\"routingMethod\":\"shared\"}]}");
app1.runJob(JobType.productionUsEast3);
tester.controller().applications().deactivate(app1.instanceId(), ZoneId.from("prod", "us-east-3"));
@@ -250,7 +251,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/dev-us-east-1/", POST)
.data(entity)
.userIdentity(USER_ID),
- "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}");
+ "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1," +
+ "\"endpoints\":[{\"cluster\":\"default\",\"tls\":true,\"url\":\"https://instance1--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/\",\"scope\":\"zone\",\"routingMethod\":\"shared\"}]}");
app1.runJob(JobType.devUsEast1);
// GET dev application package
@@ -514,7 +516,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
updateMetrics();
// GET metrics
- tester.assertResponse(request("/application/v4/tenant/tenant2/application/application1/environment/dev/region/us-east-1/instance/default/metrics", GET)
+ tester.assertJsonResponse(request("/application/v4/tenant/tenant2/application/application1/environment/dev/region/us-east-1/instance/default/metrics", GET)
.userIdentity(USER_ID),
new File("proton-metrics.json"));
@@ -1426,7 +1428,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/new-user/deploy/dev-us-east-1", POST)
.data(entity)
.userIdentity(userId),
- "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.new-user. This may take about 15 minutes the first time.\",\"run\":1}");
+ "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.new-user. This may take about 15 minutes the first time.\",\"run\":1," +
+ "\"endpoints\":[{\"cluster\":\"default\",\"tls\":true,\"url\":\"https://new-user--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/\",\"scope\":\"zone\",\"routingMethod\":\"shared\"}]}");
}
@Test
@@ -1471,7 +1474,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/sandbox/application/myapp/instance/default/deploy/dev-us-east-1", POST)
.data(entity)
.userIdentity(developer),
- "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":1}",
+ "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":1," +
+ "\"endpoints\":[{\"cluster\":\"default\",\"tls\":true,\"url\":\"https://myapp--sandbox.us-east-1.dev.vespa.oath.cloud:4443/\",\"scope\":\"zone\",\"routingMethod\":\"shared\"}]}",
200);
// To add temporary support allowing tenant admins to launch services
@@ -1482,7 +1486,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/sandbox/application/myapp/instance/default/deploy/dev-us-east-1", POST)
.data(entity)
.userIdentity(developer2),
- "{\"message\":\"Deployment started in run 2 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":2}",
+ "{\"message\":\"Deployment started in run 2 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":2," +
+ "\"endpoints\":[{\"cluster\":\"default\",\"tls\":true,\"url\":\"https://myapp--sandbox.us-east-1.dev.vespa.oath.cloud:4443/\",\"scope\":\"zone\",\"routingMethod\":\"shared\"}]}",
200);
@@ -1491,7 +1496,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
.data(applicationPackageInstance1.zippedContent())
.contentType("application/zip")
.userIdentity(developer2),
- "{\"message\":\"Deployment started in run 3 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":3}");
+ "{\"message\":\"Deployment started in run 3 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":3," +
+ "\"endpoints\":[{\"cluster\":\"default\",\"tls\":true,\"url\":\"https://myapp--sandbox.us-east-1.dev.vespa.oath.cloud:4443/\",\"scope\":\"zone\",\"routingMethod\":\"shared\"}]}");
// POST (deploy) an application package not as content type application/zip — not multipart — is disallowed
tester.assertResponse(request("/application/v4/tenant/sandbox/application/myapp/instance/default/deploy/dev-us-east-1", POST)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
index 828e2856cae..c43abf276c5 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.restapi.application;
import com.yahoo.component.Version;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.test.json.JsonTestHelper;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
@@ -12,8 +13,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.TestReport;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -180,12 +179,10 @@ public class JobControllerApiHandlerHelperTest {
"jobs-direct-deployment.json");
}
- private void compare(HttpResponse response, String expected) throws JSONException, IOException {
+ private void compare(HttpResponse response, String expected) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.render(baos);
- JSONObject actualJSON = new JSONObject(new String(baos.toByteArray()));
- JSONObject expectedJSON = new JSONObject(expected);
- assertEquals(expectedJSON.toString(), actualJSON.toString());
+ JsonTestHelper.assertJsonEquals(expected, baos.toString());
}
private void assertResponse(HttpResponse response, String fileName) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-job-accepted-2.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-job-accepted-2.json
index 8ea3f318d1d..c53cee8fd97 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-job-accepted-2.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-job-accepted-2.json
@@ -1,4 +1,13 @@
{
"message": "Deployment started in run 1 of dev-us-east-1 for tenant1.application1.myuser. This may take about 15 minutes the first time.",
- "run": 1
+ "run": 1,
+ "endpoints": [
+ {
+ "cluster": "default",
+ "tls": true,
+ "url": "https://myuser--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/",
+ "scope": "zone",
+ "routingMethod": "shared"
+ }
+ ]
} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json
index a7e5b3918d8..3fba9b3c91c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json
@@ -1,23 +1,26 @@
{
- "metrics": [{
- "clusterId": "content/doc/",
- "metrics": {
- "resourceMemoryUsageAverage": 0.103482,
- "documentsReadyCount": 11430,
- "documentDiskUsage": 44021,
- "resourceDiskUsageAverage": 0.0168421,
- "documentsTotalCount": 11430,
- "documentsActiveCount": 11430
+ "metrics": [
+ {
+ "clusterId": "content/doc/",
+ "metrics": {
+ "resourceMemoryUsageAverage": 0.103482,
+ "documentsReadyCount": 11430.0,
+ "documentDiskUsage": 44021.0,
+ "resourceDiskUsageAverage": 0.0168421,
+ "documentsTotalCount": 11430.0,
+ "documentsActiveCount": 11430.0
+ }
+ },
+ {
+ "clusterId": "content/music/",
+ "metrics": {
+ "resourceMemoryUsageAverage": 0.00912,
+ "documentsReadyCount": 32000.0,
+ "documentDiskUsage": 90113.0,
+ "resourceDiskUsageAverage": 0.23912,
+ "documentsTotalCount": 32210.0,
+ "documentsActiveCount": 32210.0
+ }
}
- }, {
- "clusterId": "content/music/",
- "metrics": {
- "resourceMemoryUsageAverage": 0.00912,
- "documentsReadyCount": 32000,
- "documentDiskUsage": 90113,
- "resourceDiskUsageAverage": 0.23912,
- "documentsTotalCount": 32210,
- "documentsActiveCount": 32210
- }
- }]
-} \ No newline at end of file
+ ]
+}
diff --git a/dist/vespa.spec b/dist/vespa.spec
index b19d1c9a201..42039fae1d8 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -158,6 +158,7 @@ Requires: gdb
Requires: nc
Requires: net-tools
Requires: unzip
+Requires: zstd
%if 0%{?el7}
Requires: llvm7.0
Requires: vespa-icu >= 65.1.0-1
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java
index 797dffdef1f..dc2db50d3ab 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java
@@ -1,44 +1,31 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.dockerapi;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.github.dockerjava.api.model.CpuStatsConfig;
-import com.github.dockerjava.api.model.MemoryStatsConfig;
-import com.github.dockerjava.api.model.StatisticNetworksConfig;
-import com.github.dockerjava.api.model.Statistics;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Map;
-import java.util.Optional;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
+import java.util.Objects;
/**
- * Wrapper class for {@link com.github.dockerjava.api.model.Statistics} to prevent leaking from docker-java library.
+ * CPU, memory and network statistics collected from a container.
*
* @author freva
*/
+// TODO: Move this to node-admin when docker-api module can be removed
public class ContainerStats {
+
private final Map<String, NetworkStats> networkStatsByInterface;
private final MemoryStats memoryStats;
private final CpuStats cpuStats;
- ContainerStats(Statistics statistics) {
- // Network stats are null when container uses host network
- this.networkStatsByInterface = Optional.ofNullable(statistics.getNetworks()).orElseGet(Map::of)
- .entrySet().stream()
- .collect(Collectors.toMap(
- Map.Entry::getKey,
- e -> new NetworkStats(e.getValue()),
- (u, v) -> { throw new IllegalStateException(); },
- TreeMap::new));
- this.memoryStats = new MemoryStats(statistics.getMemoryStats());
- this.cpuStats = new CpuStats(statistics.getCpuStats());
+ public ContainerStats(Map<String, NetworkStats> networkStatsByInterface, MemoryStats memoryStats, CpuStats cpuStats) {
+ this.networkStatsByInterface = new LinkedHashMap<>(Objects.requireNonNull(networkStatsByInterface));
+ this.memoryStats = Objects.requireNonNull(memoryStats);
+ this.cpuStats = Objects.requireNonNull(cpuStats);
}
public Map<String, NetworkStats> getNetworks() {
- return networkStatsByInterface;
+ return Collections.unmodifiableMap(networkStatsByInterface);
}
public MemoryStats getMemoryStats() {
@@ -49,7 +36,22 @@ public class ContainerStats {
return cpuStats;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ContainerStats that = (ContainerStats) o;
+ return networkStatsByInterface.equals(that.networkStatsByInterface) && memoryStats.equals(that.memoryStats) && cpuStats.equals(that.cpuStats);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkStatsByInterface, memoryStats, cpuStats);
+ }
+
+ /** Statistics for network usage */
public static class NetworkStats {
+
private final long rxBytes;
private final long rxDropped;
private final long rxErrors;
@@ -57,40 +59,109 @@ public class ContainerStats {
private final long txDropped;
private final long txErrors;
- private NetworkStats(StatisticNetworksConfig statisticNetworksConfig) {
- this.rxBytes = statisticNetworksConfig.getRxBytes();
- this.rxDropped = statisticNetworksConfig.getRxDropped();
- this.rxErrors = statisticNetworksConfig.getRxErrors();
- this.txBytes = statisticNetworksConfig.getTxBytes();
- this.txDropped = statisticNetworksConfig.getTxDropped();
- this.txErrors = statisticNetworksConfig.getTxErrors();
+ public NetworkStats(long rxBytes, long rxDropped, long rxErrors, long txBytes, long txDropped, long txErrors) {
+ this.rxBytes = rxBytes;
+ this.rxDropped = rxDropped;
+ this.rxErrors = rxErrors;
+ this.txBytes = txBytes;
+ this.txDropped = txDropped;
+ this.txErrors = txErrors;
}
+ /** Returns received bytes */
public long getRxBytes() { return this.rxBytes; }
+
+ /** Returns received bytes, which was dropped */
public long getRxDropped() { return this.rxDropped; }
+
+ /** Returns received errors */
public long getRxErrors() { return this.rxErrors; }
+
+ /** Returns transmitted bytes */
public long getTxBytes() { return this.txBytes; }
+
+ /** Returns transmitted bytes, which was dropped */
public long getTxDropped() { return this.txDropped; }
+
+ /** Returns transmission errors */
public long getTxErrors() { return this.txErrors; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NetworkStats that = (NetworkStats) o;
+ return rxBytes == that.rxBytes && rxDropped == that.rxDropped && rxErrors == that.rxErrors && txBytes == that.txBytes && txDropped == that.txDropped && txErrors == that.txErrors;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rxBytes, rxDropped, rxErrors, txBytes, txDropped, txErrors);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkStats{" +
+ "rxBytes=" + rxBytes +
+ ", rxDropped=" + rxDropped +
+ ", rxErrors=" + rxErrors +
+ ", txBytes=" + txBytes +
+ ", txDropped=" + txDropped +
+ ", txErrors=" + txErrors +
+ '}';
+ }
+
}
- public class MemoryStats {
+ /** Statistics for memory usage */
+ public static class MemoryStats {
+
private final long cache;
private final long usage;
private final long limit;
- private MemoryStats(MemoryStatsConfig memoryStats) {
- this.cache = memoryStats.getStats().getCache();
- this.usage = memoryStats.getUsage();
- this.limit = memoryStats.getLimit();
+ public MemoryStats(long cache, long usage, long limit) {
+ this.cache = cache;
+ this.usage = usage;
+ this.limit = limit;
}
+ /** Returns memory used by cache in bytes */
public long getCache() { return this.cache; }
+
+ /** Returns memory usage in bytes */
public long getUsage() { return this.usage; }
+
+ /** Returns memory limit in bytes */
public long getLimit() { return this.limit; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MemoryStats that = (MemoryStats) o;
+ return cache == that.cache && usage == that.usage && limit == that.limit;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cache, usage, limit);
+ }
+
+ @Override
+ public String toString() {
+ return "MemoryStats{" +
+ "cache=" + cache +
+ ", usage=" + usage +
+ ", limit=" + limit +
+ '}';
+ }
+
}
- public class CpuStats {
+ /** Statistics for CPU usage */
+ public static class CpuStats {
+
private final int onlineCpus;
private final long systemCpuUsage;
private final long totalUsage;
@@ -99,15 +170,15 @@ public class ContainerStats {
private final long throttlingActivePeriods;
private final long throttledPeriods;
- public CpuStats(CpuStatsConfig cpuStats) {
- // Added in 1.27
- this.onlineCpus = cpuStats.getCpuUsage().getPercpuUsage().size();
- this.systemCpuUsage = cpuStats.getSystemCpuUsage();
- this.totalUsage = cpuStats.getCpuUsage().getTotalUsage();
- this.usageInKernelMode = cpuStats.getCpuUsage().getUsageInKernelmode();
- this.throttledTime = cpuStats.getThrottlingData().getThrottledTime();
- this.throttlingActivePeriods = cpuStats.getThrottlingData().getPeriods();
- this.throttledPeriods = cpuStats.getThrottlingData().getThrottledPeriods();
+ public CpuStats(int onlineCpus, long systemCpuUsage, long totalUsage, long usageInKernelMode,
+ long throttledTime, long throttlingActivePeriods, long throttledPeriods) {
+ this.onlineCpus = onlineCpus;
+ this.systemCpuUsage = systemCpuUsage;
+ this.totalUsage = totalUsage;
+ this.usageInKernelMode = usageInKernelMode;
+ this.throttledTime = throttledTime;
+ this.throttlingActivePeriods = throttlingActivePeriods;
+ this.throttledPeriods = throttledPeriods;
}
public int getOnlineCpus() { return this.onlineCpus; }
@@ -129,15 +200,33 @@ public class ContainerStats {
/** Number of periods this container hit the throttling limit */
public long getThrottledPeriods() { return throttledPeriods; }
- }
- // For testing only, create ContainerStats from JSON returned by docker daemon stats API
- public static ContainerStats fromJson(String json) {
- try {
- Statistics statistics = new ObjectMapper().readValue(json, Statistics.class);
- return new ContainerStats(statistics);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CpuStats cpuStats = (CpuStats) o;
+ return onlineCpus == cpuStats.onlineCpus && systemCpuUsage == cpuStats.systemCpuUsage && totalUsage == cpuStats.totalUsage && usageInKernelMode == cpuStats.usageInKernelMode && throttledTime == cpuStats.throttledTime && throttlingActivePeriods == cpuStats.throttlingActivePeriods && throttledPeriods == cpuStats.throttledPeriods;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(onlineCpus, systemCpuUsage, totalUsage, usageInKernelMode, throttledTime, throttlingActivePeriods, throttledPeriods);
}
+
+ @Override
+ public String toString() {
+ return "CpuStats{" +
+ "onlineCpus=" + onlineCpus +
+ ", systemCpuUsage=" + systemCpuUsage +
+ ", totalUsage=" + totalUsage +
+ ", usageInKernelMode=" + usageInKernelMode +
+ ", throttledTime=" + throttledTime +
+ ", throttlingActivePeriods=" + throttlingActivePeriods +
+ ", throttledPeriods=" + throttledPeriods +
+ '}';
+ }
+
}
+
}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java
index a45855764ed..630efb7990f 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.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.hosted.dockerapi;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
@@ -33,6 +34,8 @@ import com.yahoo.vespa.hosted.dockerapi.metrics.Gauge;
import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
@@ -43,6 +46,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -198,8 +202,7 @@ public class DockerEngine implements ContainerEngine {
try {
DockerStatsCallback statsCallback = dockerClient.statsCmd(containerName.asString()).exec(new DockerStatsCallback());
statsCallback.awaitCompletion(5, TimeUnit.SECONDS);
-
- return statsCallback.stats.map(ContainerStats::new);
+ return statsCallback.stats.map(DockerEngine::containerStatsFrom);
} catch (NotFoundException ignored) {
return Optional.empty();
} catch (RuntimeException | InterruptedException e) {
@@ -437,4 +440,39 @@ public class DockerEngine implements ContainerEngine {
return DockerClientImpl.getInstance(dockerClientConfig)
.withDockerCmdExecFactory(dockerFactory);
}
+
+ private static ContainerStats containerStatsFrom(Statistics statistics) {
+ return new ContainerStats(Optional.ofNullable(statistics.getNetworks()).orElseGet(Map::of)
+ .entrySet().stream()
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ e -> new ContainerStats.NetworkStats(e.getValue().getRxBytes(), e.getValue().getRxDropped(),
+ e.getValue().getRxErrors(), e.getValue().getTxBytes(),
+ e.getValue().getTxDropped(), e.getValue().getTxErrors()),
+ (u, v) -> {
+ throw new IllegalStateException();
+ },
+ TreeMap::new)),
+ new ContainerStats.MemoryStats(statistics.getMemoryStats().getStats().getCache(),
+ statistics.getMemoryStats().getUsage(),
+ statistics.getMemoryStats().getLimit()),
+ new ContainerStats.CpuStats(statistics.getCpuStats().getCpuUsage().getPercpuUsage().size(),
+ statistics.getCpuStats().getSystemCpuUsage(),
+ statistics.getCpuStats().getCpuUsage().getTotalUsage(),
+ statistics.getCpuStats().getCpuUsage().getUsageInKernelmode(),
+ statistics.getCpuStats().getThrottlingData().getThrottledTime(),
+ statistics.getCpuStats().getThrottlingData().getPeriods(),
+ statistics.getCpuStats().getThrottlingData().getThrottledPeriods()));
+ }
+
+ // For testing only, create ContainerStats from JSON returned by docker daemon stats API
+ public static ContainerStats statsFromJson(String json) {
+ try {
+ Statistics statistics = new ObjectMapper().readValue(json, Statistics.class);
+ return containerStatsFrom(statistics);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
}
diff --git a/document/abi-spec.json b/document/abi-spec.json
index df1fb840cbc..4a94d7e55de 100644
--- a/document/abi-spec.json
+++ b/document/abi-spec.json
@@ -3164,6 +3164,8 @@
"public void printXml(com.yahoo.document.serialization.XmlStream)",
"public void clear()",
"public void assign(java.lang.Object)",
+ "public void assignSerializedTensor(byte[])",
+ "public java.util.Optional getSerializedTensor()",
"public void assignTensor(java.util.Optional)",
"public void serialize(com.yahoo.document.Field, com.yahoo.document.serialization.FieldWriter)",
"public void deserialize(com.yahoo.document.Field, com.yahoo.document.serialization.FieldReader)",
@@ -4471,4 +4473,4 @@
"protected com.yahoo.document.update.ValueUpdate$ValueUpdateClassID valueUpdateClassID"
]
}
-} \ No newline at end of file
+}
diff --git a/document/src/main/java/com/yahoo/document/datatypes/TensorFieldValue.java b/document/src/main/java/com/yahoo/document/datatypes/TensorFieldValue.java
index 8e7dbd3512a..3177ec54465 100644
--- a/document/src/main/java/com/yahoo/document/datatypes/TensorFieldValue.java
+++ b/document/src/main/java/com/yahoo/document/datatypes/TensorFieldValue.java
@@ -6,8 +6,10 @@ import com.yahoo.document.TensorDataType;
import com.yahoo.document.serialization.FieldReader;
import com.yahoo.document.serialization.FieldWriter;
import com.yahoo.document.serialization.XmlStream;
+import com.yahoo.io.GrowableByteBuffer;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
+import com.yahoo.tensor.serialization.TypedBinaryFormat;
import java.util.Optional;
@@ -20,6 +22,8 @@ public class TensorFieldValue extends FieldValue {
private Optional<Tensor> tensor;
+ private Optional<byte[]> serializedTensor;
+
private Optional<TensorDataType> dataType;
/**
@@ -28,27 +32,49 @@ public class TensorFieldValue extends FieldValue {
* The tensor (and tensor type) can later be assigned with assignTensor().
*/
public TensorFieldValue() {
- this.dataType = Optional.empty();
- this.tensor = Optional.empty();
+ this.dataType = Optional.empty();
+ this.serializedTensor = Optional.empty();
+ this.tensor = Optional.empty();
}
/** Create an empty tensor field value for the given tensor type */
public TensorFieldValue(TensorType type) {
this.dataType = Optional.of(new TensorDataType(type));
+ this.serializedTensor = Optional.empty();
this.tensor = Optional.empty();
}
/** Create a tensor field value containing the given tensor */
public TensorFieldValue(Tensor tensor) {
this.dataType = Optional.of(new TensorDataType(tensor.type()));
+ this.serializedTensor = Optional.empty();
this.tensor = Optional.of(tensor);
}
+ private void lazyDeserialize() {
+ if (tensor.isEmpty() && serializedTensor.isPresent()) {
+ Tensor t = TypedBinaryFormat.decode(Optional.empty(), GrowableByteBuffer.wrap(serializedTensor.get()));
+ if (dataType.isEmpty()) {
+ this.dataType = Optional.of(new TensorDataType(t.type()));
+ this.tensor = Optional.of(t);
+ } else {
+ if (t.type().isAssignableTo(dataType.get().getTensorType())) {
+ this.tensor = Optional.of(t);
+ } else {
+ throw new IllegalArgumentException("Type mismatch: Cannot assign tensor of type " + t.type() +
+ " to field of type " + dataType.get());
+ }
+ }
+ }
+ }
+
public Optional<Tensor> getTensor() {
+ lazyDeserialize();
return tensor;
}
public Optional<TensorType> getTensorType() {
+ lazyDeserialize();
return dataType.isPresent() ? Optional.of(dataType.get().getTensorType()) : Optional.empty();
}
@@ -59,8 +85,9 @@ public class TensorFieldValue extends FieldValue {
@Override
public String toString() {
- if (tensor.isPresent()) {
- return tensor.get().toString();
+ var t = getTensor();
+ if (t.isPresent()) {
+ return t.get().toString();
} else {
return "null";
}
@@ -74,6 +101,7 @@ public class TensorFieldValue extends FieldValue {
@Override
public void clear() {
tensor = Optional.empty();
+ serializedTensor = Optional.empty();
}
@Override
@@ -90,12 +118,28 @@ public class TensorFieldValue extends FieldValue {
}
}
+ public void assignSerializedTensor(byte[] data) {
+ serializedTensor = Optional.of(data);
+ tensor = Optional.empty();
+ }
+
+ public Optional<byte[]> getSerializedTensor() {
+ if (serializedTensor.isPresent()) {
+ return serializedTensor;
+ } else if (tensor.isPresent()) {
+ serializedTensor = Optional.of(TypedBinaryFormat.encode(tensor.get()));
+ assert(serializedTensor.isPresent());
+ }
+ return serializedTensor;
+ }
+
/**
* Assigns the given tensor to this field value.
*
* The tensor type is also set from the given tensor if it was not set before.
*/
public void assignTensor(Optional<Tensor> tensor) {
+ this.serializedTensor = Optional.empty();
if (tensor.isPresent()) {
if (getTensorType().isPresent() &&
!tensor.get().type().isAssignableTo(getTensorType().get())) {
@@ -126,7 +170,7 @@ public class TensorFieldValue extends FieldValue {
TensorFieldValue other = (TensorFieldValue)o;
if ( ! getTensorType().equals(other.getTensorType())) return false;
- if ( ! tensor.equals(other.tensor)) return false;
+ if ( ! getTensor().equals(other.getTensor())) return false;
return true;
}
@@ -136,4 +180,3 @@ public class TensorFieldValue extends FieldValue {
}
}
-
diff --git a/document/src/main/java/com/yahoo/document/json/SingleDocumentParser.java b/document/src/main/java/com/yahoo/document/json/SingleDocumentParser.java
deleted file mode 100644
index 67508c61b23..00000000000
--- a/document/src/main/java/com/yahoo/document/json/SingleDocumentParser.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.document.json;
-
-import com.fasterxml.jackson.core.JsonFactory;
-import com.yahoo.document.DocumentOperation;
-import com.yahoo.document.DocumentPut;
-import com.yahoo.document.DocumentTypeManager;
-import com.yahoo.document.DocumentUpdate;
-import com.yahoo.vespaxmlparser.DocumentFeedOperation;
-import com.yahoo.vespaxmlparser.DocumentUpdateFeedOperation;
-import com.yahoo.vespaxmlparser.FeedOperation;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Parser that supports parsing PUT operation and UPDATE operation.
- *
- * @author dybis
- */
-public class SingleDocumentParser {
-
- private static final JsonFactory jsonFactory = new JsonFactory().disable(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES);
- private DocumentTypeManager docMan;
-
- public SingleDocumentParser(DocumentTypeManager docMan) {
- this.docMan = docMan;
- }
-
- public FeedOperation parsePut(InputStream inputStream, String docId) {
- return parse(inputStream, docId, DocumentOperationType.PUT);
- }
-
- public FeedOperation parseUpdate(InputStream inputStream, String docId) {
- return parse(inputStream, docId, DocumentOperationType.UPDATE);
- }
-
- private FeedOperation parse(InputStream inputStream, String docId, DocumentOperationType documentOperationType) {
- JsonReader reader = new JsonReader(docMan, inputStream, jsonFactory);
- DocumentOperation documentOperation = reader.readSingleDocument(documentOperationType, docId);
- try {
- inputStream.close();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- if (documentOperationType == DocumentOperationType.PUT) {
- return new DocumentFeedOperation(((DocumentPut) documentOperation).getDocument(), documentOperation.getCondition());
- } else {
- return new DocumentUpdateFeedOperation((DocumentUpdate) documentOperation, documentOperation.getCondition());
- }
- }
-
-}
diff --git a/document/src/main/java/com/yahoo/document/serialization/VespaDocumentDeserializer6.java b/document/src/main/java/com/yahoo/document/serialization/VespaDocumentDeserializer6.java
index 92b3b566b85..914f0ad8759 100644
--- a/document/src/main/java/com/yahoo/document/serialization/VespaDocumentDeserializer6.java
+++ b/document/src/main/java/com/yahoo/document/serialization/VespaDocumentDeserializer6.java
@@ -97,7 +97,7 @@ public class VespaDocumentDeserializer6 extends BufferSerializer implements Docu
final public DocumentTypeManager getDocumentTypeManager() { return manager; }
public void read(Document document) {
- read(null, document);
+ read(null, document);
}
@SuppressWarnings("deprecation")
@@ -243,8 +243,7 @@ public class VespaDocumentDeserializer6 extends BufferSerializer implements Docu
int encodedTensorLength = buf.getInt1_4Bytes();
if (encodedTensorLength > 0) {
byte[] encodedTensor = getBytes(null, encodedTensorLength);
- value.assign(TypedBinaryFormat.decode(value.getTensorType(),
- GrowableByteBuffer.wrap(encodedTensor)));
+ value.assignSerializedTensor(encodedTensor);
} else {
value.clear();
}
@@ -374,9 +373,9 @@ public class VespaDocumentDeserializer6 extends BufferSerializer implements Docu
Integer f_id = fieldIdsAndLengths.get(i).first;
Field structField = priType.getField(f_id);
if (structField != null) {
- FieldValue value = structField.getDataType().createFieldValue();
- value.deserialize(structField, this);
- primary.setFieldValue(structField, value);
+ FieldValue value = structField.getDataType().createFieldValue();
+ value.deserialize(structField, this);
+ primary.setFieldValue(structField, value);
}
//jump to beginning of next field:
position(posBefore + fieldIdsAndLengths.get(i).second.intValue());
diff --git a/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java b/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java
index 3fca853b4d1..1a5c5c8257b 100644
--- a/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java
+++ b/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java
@@ -290,10 +290,10 @@ public class VespaDocumentSerializer6 extends BufferSerializer implements Docume
@Override
public void write(FieldBase field, TensorFieldValue value) {
- if (value.getTensor().isPresent()) {
- byte[] encodedTensor = TypedBinaryFormat.encode(value.getTensor().get());
- buf.putInt1_4Bytes(encodedTensor.length);
- buf.put(encodedTensor);
+ var encodedTensor = value.getSerializedTensor();
+ if (encodedTensor.isPresent()) {
+ buf.putInt1_4Bytes(encodedTensor.get().length);
+ buf.put(encodedTensor.get());
} else {
buf.putInt1_4Bytes(0);
}
diff --git a/document/src/tests/documentselectparsertest.cpp b/document/src/tests/documentselectparsertest.cpp
index 0830c84fe39..7e61752cc1e 100644
--- a/document/src/tests/documentselectparsertest.cpp
+++ b/document/src/tests/documentselectparsertest.cpp
@@ -754,9 +754,9 @@ TEST_F(DocumentSelectParserTest, operators_5)
PARSE("\"foo\".hash() == 123", *_doc[0], False);
PARSEI("(234).hash() == 123", *_doc[0], False);
PARSE("now() > 1311862500", *_doc[8], True);
- PARSE("now() < 1611862500", *_doc[8], True);
+ PARSE("now() < 1911862500", *_doc[8], True);
PARSE("now() < 1311862500", *_doc[8], False);
- PARSE("now() > 1611862500", *_doc[8], False);
+ PARSE("now() > 1911862500", *_doc[8], False);
// Arithmetics
PARSEI("id.specific.hash() % 10 = 8", *_doc[0], True);
diff --git a/document/src/vespa/document/bucket/bucket.h b/document/src/vespa/document/bucket/bucket.h
index 44068e1c443..f189c2951c9 100644
--- a/document/src/vespa/document/bucket/bucket.h
+++ b/document/src/vespa/document/bucket/bucket.h
@@ -32,7 +32,7 @@ public:
vespalib::string toString() const;
struct hash {
- size_t operator () (const Bucket& b) const {
+ size_t operator () (const Bucket& b) const noexcept {
size_t hash1 = BucketId::hash()(b.getBucketId());
size_t hash2 = BucketSpace::hash()(b.getBucketSpace());
// Formula taken from std::hash_combine proposal
diff --git a/document/src/vespa/document/bucket/bucketid.cpp b/document/src/vespa/document/bucket/bucketid.cpp
index 668798d6c39..1b9cf1e5304 100644
--- a/document/src/vespa/document/bucket/bucketid.cpp
+++ b/document/src/vespa/document/bucket/bucketid.cpp
@@ -71,7 +71,7 @@ Initialize _initializeUsedMasks;
}
-void BucketId::initialize() {
+void BucketId::initialize() noexcept {
fillUsedMasks(BucketId::_usedMasks, BucketId::maxNumBits);
fillStripMasks(BucketId::_stripMasks, BucketId::maxNumBits);
}
@@ -91,7 +91,7 @@ void BucketId::throwFailedSetUsedBits(uint32_t used, uint32_t availBits) {
}
BucketId::Type
-BucketId::reverse(Type id)
+BucketId::reverse(Type id) noexcept
{
id = ((id & 0x5555555555555555l) << 1) | ((id & 0xaaaaaaaaaaaaaaaal) >> 1);
id = ((id & 0x3333333333333333l) << 2) | ((id & 0xccccccccccccccccl) >> 2);
@@ -100,7 +100,7 @@ BucketId::reverse(Type id)
}
BucketId::Type
-BucketId::keyToBucketId(Type key)
+BucketId::keyToBucketId(Type key) noexcept
{
Type retVal = reverse(key);
@@ -113,7 +113,7 @@ BucketId::keyToBucketId(Type key)
}
bool
-BucketId::contains(const BucketId& id) const
+BucketId::contains(const BucketId& id) const noexcept
{
if (id.getUsedBits() < getUsedBits()) {
return false;
diff --git a/document/src/vespa/document/bucket/bucketid.h b/document/src/vespa/document/bucket/bucketid.h
index b31f9080acc..675a0d23ebd 100644
--- a/document/src/vespa/document/bucket/bucketid.h
+++ b/document/src/vespa/document/bucket/bucketid.h
@@ -37,7 +37,7 @@ class BucketId
{
public:
struct hash {
- size_t operator () (const BucketId& g) const {
+ size_t operator () (const BucketId& g) const noexcept {
return g.getId();
}
};
@@ -55,23 +55,23 @@ public:
/** Create a bucket id using a set of bits from a raw unchecked value. */
BucketId(uint32_t useBits, Type id) noexcept : _id(createUsedBits(useBits, id)) { }
- bool operator<(const BucketId& id) const {
+ bool operator<(const BucketId& id) const noexcept {
return getId() < id.getId();
}
- bool operator==(const BucketId& id) const { return getId() == id.getId(); }
- bool operator!=(const BucketId& id) const { return getId() != id.getId(); }
+ bool operator==(const BucketId& id) const noexcept { return getId() == id.getId(); }
+ bool operator!=(const BucketId& id) const noexcept { return getId() != id.getId(); }
vespalib::string toString() const;
- bool valid() const {
+ bool valid() const noexcept {
return validUsedBits(getUsedBits());
}
- static bool validUsedBits(uint32_t usedBits) {
+ static bool validUsedBits(uint32_t usedBits) noexcept {
return (usedBits >= minNumBits) && (usedBits <= maxNumBits);
}
- bool isSet() const {
+ bool isSet() const noexcept {
return _id != 0u;
}
/**
@@ -79,14 +79,14 @@ public:
* verify that two different documents belong to the same bucket given some
* level of bucket splitting, use this to ignore the unused bits.
*/
- BucketId stripUnused() const { return BucketId(getUsedBits(), getId()); }
+ BucketId stripUnused() const noexcept { return BucketId(getUsedBits(), getId()); }
/**
* Checks whether the given bucket is contained within this bucket. That is,
* if it is the same bucket, or if it is a bucket using more bits, which is
* identical to this one if set to use as many bits as this one.
*/
- bool contains(const BucketId& id) const;
+ bool contains(const BucketId& id) const noexcept;
// Functions exposing internals we want to make users independent of
@@ -97,7 +97,7 @@ public:
static constexpr uint32_t maxNumBits = (8 * sizeof(Type) - CountBits);
static constexpr uint32_t minNumBits = 1u; // See comment above.
- uint32_t getUsedBits() const { return _id >> maxNumBits; }
+ uint32_t getUsedBits() const noexcept { return _id >> maxNumBits; }
void setUsedBits(uint32_t used) {
uint32_t availBits = maxNumBits;
@@ -113,22 +113,22 @@ public:
}
/** Get the bucket id value stripped of the bits that are not in use. */
- Type getId() const { return (_id & getStripMask()); }
+ Type getId() const noexcept { return (_id & getStripMask()); }
/**
* Get the bucket id value stripped of the count bits plus the bits that
* are not in use.
*/
- Type withoutCountBits() const { return (_id & getUsedMask()); }
+ Type withoutCountBits() const noexcept { return (_id & getUsedMask()); }
- Type getRawId() const { return _id; }
+ Type getRawId() const noexcept { return _id; }
/**
* Reverses the bits in the given number, except the countbits part.
* Used for sorting in the bucket database as we want related buckets
* to be sorted next to each other.
*/
- static Type bucketIdToKey(Type id) {
+ static Type bucketIdToKey(Type id) noexcept {
Type retVal = reverse(id);
Type usedCountLSB = id >> maxNumBits;
@@ -139,39 +139,39 @@ public:
return retVal;
}
- static Type keyToBucketId(Type key);
+ static Type keyToBucketId(Type key) noexcept ;
/**
* Reverses the bucket id bitwise, except the countbits part,
* and returns the value,
*/
- Type toKey() const { return bucketIdToKey(getId()); };
+ Type toKey() const noexcept { return bucketIdToKey(getId()); };
/**
* Reverses the order of the bits in the bucket id.
*/
- static Type reverse(Type id);
+ static Type reverse(Type id) noexcept;
/**
* Returns the value of the Nth bit, counted in the reverse order of the
* bucket id.
*/
- uint8_t getBit(uint32_t n) const {
+ uint8_t getBit(uint32_t n) const noexcept {
return (_id & ((Type)1 << n)) == 0 ? 0 : 1;
}
- static void initialize();
+ static void initialize() noexcept;
private:
static Type _usedMasks[maxNumBits+1];
static Type _stripMasks[maxNumBits+1];
Type _id;
- Type getUsedMask() const {
+ Type getUsedMask() const noexcept {
return _usedMasks[getUsedBits()];
}
- Type getStripMask() const {
+ Type getStripMask() const noexcept {
return _stripMasks[getUsedBits()];
}
diff --git a/documentapi/src/tests/policies/policies_test.cpp b/documentapi/src/tests/policies/policies_test.cpp
index 02bd6b297d0..c80e1221eba 100644
--- a/documentapi/src/tests/policies/policies_test.cpp
+++ b/documentapi/src/tests/policies/policies_test.cpp
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "testframe.h"
-
#include <vespa/documentapi/documentapi.h>
#include <vespa/documentapi/messagebus/policies/andpolicy.h>
#include <vespa/documentapi/messagebus/policies/contentpolicy.h>
@@ -25,6 +24,7 @@
#include <vespa/document/update/documentupdate.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/util/stringfmt.h>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("policies_test");
diff --git a/documentapi/src/vespa/documentapi/messagebus/policies/CMakeLists.txt b/documentapi/src/vespa/documentapi/messagebus/policies/CMakeLists.txt
index 83e1df02a24..82865ea5996 100644
--- a/documentapi/src/vespa/documentapi/messagebus/policies/CMakeLists.txt
+++ b/documentapi/src/vespa/documentapi/messagebus/policies/CMakeLists.txt
@@ -16,5 +16,7 @@ vespa_add_library(documentapi_documentapipolicies OBJECT
asyncinitializationpolicy.cpp
DEPENDS
)
+vespa_generate_config(documentapi_documentapipolicies ../../../../main/resources/configdefinitions/document-protocol-policies.def)
+install_config_definition(../../../../main/resources/configdefinitions/document-protocol-policies.def documentapi.messagebus.protocol.document-protocol-policies.def)
vespa_generate_config(documentapi_documentapipolicies ../../../../main/resources/configdefinitions/documentrouteselectorpolicy.def)
install_config_definition(../../../../main/resources/configdefinitions/documentrouteselectorpolicy.def documentapi.messagebus.protocol.documentrouteselectorpolicy.def)
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index 239cf8f0f23..23127cc12b5 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -19,6 +19,7 @@ vespa_define_module(
src/tests/eval/function
src/tests/eval/function_speed
src/tests/eval/gbdt
+ src/tests/eval/gen_spec
src/tests/eval/inline_operation
src/tests/eval/interpreted_function
src/tests/eval/multiply_add
@@ -64,6 +65,7 @@ vespa_define_module(
src/tests/instruction/mixed_simple_join_function
src/tests/instruction/pow_as_map_optimizer
src/tests/instruction/remove_trivial_dimension_optimizer
+ src/tests/instruction/sparse_dot_product_function
src/tests/instruction/sum_max_dot_product_function
src/tests/instruction/vector_from_doubles_function
src/tests/streamed/value
diff --git a/eval/src/tests/eval/compile_cache/compile_cache_test.cpp b/eval/src/tests/eval/compile_cache/compile_cache_test.cpp
index 5dea89b3a63..a0c71e2f756 100644
--- a/eval/src/tests/eval/compile_cache/compile_cache_test.cpp
+++ b/eval/src/tests/eval/compile_cache/compile_cache_test.cpp
@@ -1,5 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/eval/eval/llvm/compile_cache.h>
#include <vespa/eval/eval/key_gen.h>
#include <vespa/eval/eval/test/eval_spec.h>
@@ -7,7 +8,6 @@
#include <vespa/vespalib/util/threadstackexecutor.h>
#include <vespa/vespalib/util/blockingthreadstackexecutor.h>
#include <vespa/vespalib/util/stringfmt.h>
-#include <thread>
#include <set>
using namespace vespalib;
diff --git a/eval/src/tests/eval/fast_value/fast_value_test.cpp b/eval/src/tests/eval/fast_value/fast_value_test.cpp
index 279f17a1ead..9d29d8de660 100644
--- a/eval/src/tests/eval/fast_value/fast_value_test.cpp
+++ b/eval/src/tests/eval/fast_value/fast_value_test.cpp
@@ -3,7 +3,7 @@
#include <vespa/eval/eval/fast_value.hpp>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/value_codec.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/gtest/gtest.h>
using namespace vespalib;
@@ -142,29 +142,31 @@ TEST(FastValueBuilderTest, mixed_add_subspace_robustness) {
}
}
-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"})})
+GenSpec G() { return GenSpec(); }
+
+const std::vector<GenSpec> layouts = {
+ G(),
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"})
};
TEST(FastValueBuilderFactoryTest, fast_values_can_be_copied) {
auto factory = FastValueBuilderFactory::get();
for (const auto &layout: layouts) {
- TensorSpec expect = spec(layout, N());
- std::unique_ptr<Value> value = value_from_spec(expect, factory);
- std::unique_ptr<Value> copy = factory.copy(*value);
- TensorSpec actual = spec_from_value(*copy);
- EXPECT_EQ(actual, expect);
+ for (TensorSpec expect : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ std::unique_ptr<Value> value = value_from_spec(expect, factory);
+ std::unique_ptr<Value> copy = factory.copy(*value);
+ TensorSpec actual = spec_from_value(*copy);
+ EXPECT_EQ(actual, expect);
+ }
}
}
diff --git a/eval/src/tests/eval/gen_spec/CMakeLists.txt b/eval/src/tests/eval/gen_spec/CMakeLists.txt
new file mode 100644
index 00000000000..3613554f0a0
--- /dev/null
+++ b/eval/src/tests/eval/gen_spec/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_gen_spec_test_app TEST
+ SOURCES
+ gen_spec_test.cpp
+ DEPENDS
+ vespaeval
+ GTest::GTest
+)
+vespa_add_test(NAME eval_gen_spec_test_app COMMAND eval_gen_spec_test_app)
diff --git a/eval/src/tests/eval/gen_spec/gen_spec_test.cpp b/eval/src/tests/eval/gen_spec/gen_spec_test.cpp
new file mode 100644
index 00000000000..0d1a4744e42
--- /dev/null
+++ b/eval/src/tests/eval/gen_spec/gen_spec_test.cpp
@@ -0,0 +1,196 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/eval/eval/test/gen_spec.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using namespace vespalib::eval;
+using namespace vespalib::eval::test;
+
+//-----------------------------------------------------------------------------
+
+TEST(DimSpec, indexed_dimension) {
+ ValueType::Dimension ref("foo", 10);
+ DimSpec idx("foo", 10);
+ EXPECT_EQ(idx.type(), ref);
+ EXPECT_TRUE(ref.is_indexed());
+ EXPECT_EQ(idx.name(), "foo");
+ EXPECT_EQ(idx.size(), 10);
+ EXPECT_EQ(idx.label(3), TensorSpec::Label(size_t(3)));
+}
+
+TEST(DimSpec, mapped_dimension) {
+ ValueType::Dimension ref("foo");
+ DimSpec map("foo", {"a", "b", "c", "d"});
+ EXPECT_EQ(map.type(), ref);
+ EXPECT_TRUE(ref.is_mapped());
+ EXPECT_EQ(map.name(), "foo");
+ EXPECT_EQ(map.size(), 4);
+ EXPECT_EQ(map.label(2), TensorSpec::Label("c"));
+}
+
+TEST(DimSpec, simple_dictionary_creation) {
+ auto dict = DimSpec::make_dict(5, 1, "");
+ std::vector<vespalib::string> expect = {"0", "1", "2", "3", "4"};
+}
+
+TEST(DimSpec, advanced_dictionary_creation) {
+ auto dict = DimSpec::make_dict(5, 3, "str_");
+ std::vector<vespalib::string> expect = {"str_0", "str_3", "str_6", "str_9", "str_12"};
+}
+
+//-----------------------------------------------------------------------------
+
+TEST(GenSpec, default_spec) {
+ GenSpec spec;
+ EXPECT_TRUE(spec.dims().empty());
+ EXPECT_EQ(spec.cells(), CellType::DOUBLE);
+ auto seq = spec.seq();
+ for (size_t i = 0; i < 4096; ++i) {
+ EXPECT_EQ(seq(i), (i + 1.0));
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+TensorSpec scalar_1 = TensorSpec("double").add({}, 1.0);
+TensorSpec scalar_5 = TensorSpec("double").add({}, 5.0);
+
+TEST(GenSpec, scalar_double) {
+ EXPECT_EQ(GenSpec().gen(), scalar_1);
+ EXPECT_EQ(GenSpec().seq_bias(5.0).gen(), scalar_5);
+}
+
+TEST(GenSpec, not_scalar_float_just_yet) {
+ EXPECT_EQ(GenSpec().cells_float().gen(), scalar_1);
+ EXPECT_EQ(GenSpec().cells_float().seq_bias(5.0).gen(), scalar_5);
+}
+
+//-----------------------------------------------------------------------------
+
+TEST(Seq, seq_n) {
+ GenSpec::seq_t seq = GenSpec().seq_n().seq();
+ for (size_t i = 0; i < 4096; ++i) {
+ EXPECT_EQ(seq(i), (i + 1.0));
+ }
+}
+
+TEST(Seq, seq_bias) {
+ GenSpec::seq_t seq = GenSpec().seq_bias(13.0).seq();
+ for (size_t i = 0; i < 4096; ++i) {
+ EXPECT_EQ(seq(i), (i + 13.0));
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+GenSpec flt() { return GenSpec().cells_float(); }
+GenSpec dbl() { return GenSpec().cells_double(); }
+
+TEST(GenSpec, value_type) {
+ EXPECT_EQ(dbl().type().to_spec(), "double");
+ EXPECT_EQ(flt().type().to_spec(), "double"); // NB
+ EXPECT_EQ(dbl().idx("x", 10).type().to_spec(), "tensor(x[10])");
+ EXPECT_EQ(flt().idx("x", 10).type().to_spec(), "tensor<float>(x[10])");
+ EXPECT_EQ(dbl().map("y", {}).type().to_spec(), "tensor(y{})");
+ EXPECT_EQ(flt().map("y", {}).type().to_spec(), "tensor<float>(y{})");
+ EXPECT_EQ(dbl().idx("x", 10).map("y", {}).type().to_spec(), "tensor(x[10],y{})");
+ EXPECT_EQ(flt().idx("x", 10).map("y", {}).type().to_spec(), "tensor<float>(x[10],y{})");
+ EXPECT_EQ(dbl().map("y", 3, 1).idx("x", 10).type().to_spec(), "tensor(x[10],y{})");
+ EXPECT_EQ(flt().map("y", 3, 1, "str").idx("x", 10).type().to_spec(), "tensor<float>(x[10],y{})");
+}
+
+//-----------------------------------------------------------------------------
+
+TensorSpec basic_vector = TensorSpec("tensor(a[5])")
+ .add({{"a", 0}}, 1.0)
+ .add({{"a", 1}}, 2.0)
+ .add({{"a", 2}}, 3.0)
+ .add({{"a", 3}}, 4.0)
+ .add({{"a", 4}}, 5.0);
+
+TensorSpec float_vector = TensorSpec("tensor<float>(a[5])")
+ .add({{"a", 0}}, 1.0)
+ .add({{"a", 1}}, 2.0)
+ .add({{"a", 2}}, 3.0)
+ .add({{"a", 3}}, 4.0)
+ .add({{"a", 4}}, 5.0);
+
+TensorSpec custom_vector = TensorSpec("tensor(a[5])")
+ .add({{"a", 0}}, 5.0)
+ .add({{"a", 1}}, 4.0)
+ .add({{"a", 2}}, 3.0)
+ .add({{"a", 3}}, 2.0)
+ .add({{"a", 4}}, 1.0);
+
+TEST(GenSpec, generating_basic_vector) {
+ EXPECT_EQ(GenSpec().idx("a", 5).gen(), basic_vector);
+}
+
+TEST(GenSpec, generating_float_vector) {
+ EXPECT_EQ(GenSpec().idx("a", 5).cells_float().gen(), float_vector);
+}
+
+TEST(GenSpec, generating_custom_vector) {
+ GenSpec::seq_t my_seq = [](size_t idx) noexcept { return (5.0 - idx); };
+ EXPECT_EQ(GenSpec().idx("a", 5).seq(my_seq).gen(), custom_vector);
+}
+
+//-----------------------------------------------------------------------------
+
+TensorSpec basic_map = TensorSpec("tensor(a{})")
+ .add({{"a", "0"}}, 1.0)
+ .add({{"a", "1"}}, 2.0)
+ .add({{"a", "2"}}, 3.0);
+
+TensorSpec custom_map = TensorSpec("tensor(a{})")
+ .add({{"a", "s0"}}, 1.0)
+ .add({{"a", "s5"}}, 2.0)
+ .add({{"a", "s10"}}, 3.0);
+
+TEST(GenSpec, generating_basic_map) {
+ EXPECT_EQ(GenSpec().map("a", 3).gen(), basic_map);
+ EXPECT_EQ(GenSpec().map("a", 3, 1).gen(), basic_map);
+ EXPECT_EQ(GenSpec().map("a", 3, 1, "").gen(), basic_map);
+ EXPECT_EQ(GenSpec().map("a", {"0", "1", "2"}).gen(), basic_map);
+}
+
+TEST(GenSpec, generating_custom_map) {
+ EXPECT_EQ(GenSpec().map("a", 3, 5, "s").gen(), custom_map);
+ EXPECT_EQ(GenSpec().map("a", {"s0", "s5", "s10"}).gen(), custom_map);
+}
+
+//-----------------------------------------------------------------------------
+
+TensorSpec basic_mixed = TensorSpec("tensor(a{},b[1],c{},d[3])")
+ .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 0}}, 1.0)
+ .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 1}}, 2.0)
+ .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 2}}, 3.0)
+ .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 0}}, 4.0)
+ .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 1}}, 5.0)
+ .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 2}}, 6.0)
+ .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 0}}, 7.0)
+ .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 1}}, 8.0)
+ .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 2}}, 9.0);
+
+TensorSpec inverted_mixed = TensorSpec("tensor(a{},b[1],c{},d[3])")
+ .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 0}}, 1.0)
+ .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 0}}, 2.0)
+ .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 0}}, 3.0)
+ .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 1}}, 4.0)
+ .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 1}}, 5.0)
+ .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 1}}, 6.0)
+ .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 2}}, 7.0)
+ .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 2}}, 8.0)
+ .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 2}}, 9.0);
+
+TEST(GenSpec, generating_basic_mixed) {
+ EXPECT_EQ(GenSpec().map("a", 3).idx("b", 1).map("c", 1).idx("d", 3).gen(), basic_mixed);
+}
+
+TEST(GenSpec, generating_inverted_mixed) {
+ EXPECT_EQ(GenSpec().idx("d", 3).map("c", 1).idx("b", 1).map("a", 3).gen(), inverted_mixed);
+}
+
+//-----------------------------------------------------------------------------
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/eval/reference_operations/reference_operations_test.cpp b/eval/src/tests/eval/reference_operations/reference_operations_test.cpp
index 0495923018e..3fcca5e34d8 100644
--- a/eval/src/tests/eval/reference_operations/reference_operations_test.cpp
+++ b/eval/src/tests/eval/reference_operations/reference_operations_test.cpp
@@ -1,13 +1,11 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/eval/eval/test/reference_operations.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
#include <vespa/vespalib/gtest/gtest.h>
#include <iostream>
using namespace vespalib;
using namespace vespalib::eval;
-using namespace vespalib::eval::test;
TensorSpec dense_2d_some_cells(bool square) {
return TensorSpec("tensor(a[3],d[5])")
diff --git a/eval/src/tests/eval/simple_value/simple_value_test.cpp b/eval/src/tests/eval/simple_value/simple_value_test.cpp
index 3a653b75172..c1301bf6b1a 100644
--- a/eval/src/tests/eval/simple_value/simple_value_test.cpp
+++ b/eval/src/tests/eval/simple_value/simple_value_test.cpp
@@ -5,7 +5,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -23,45 +23,36 @@ using Handle = SharedStringRepo::Handle;
vespalib::string as_str(string_id label) { return Handle::string_from_id(label); }
-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"})})
+GenSpec G() { return GenSpec(); }
+
+const std::vector<GenSpec> layouts = {
+ G(),
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("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"})})
+const std::vector<GenSpec> join_layouts = {
+ G(), G(),
+ G().idx("x", 5), G().idx("x", 5),
+ G().idx("x", 5), G().idx("y", 5),
+ G().idx("x", 5), G().idx("x", 5).idx("y", 5),
+ G().idx("y", 3), G().idx("x", 2).idx("z", 3),
+ G().idx("x", 3).idx("y", 5), G().idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b"}),
+ G().map("x", {"a","b","c"}), G().map("y", {"foo","bar","baz"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}).map("y", {"foo","bar","baz"}),
+ G().map("x", {"a","b"}).map("y", {"foo","bar","baz"}), G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b"}).map("y", {"foo","bar","baz"}), G().map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}), G().map("y", {"foo","bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5), G().idx("y", 5).map("z", {"i","j","k","l"})
+
};
TensorSpec simple_value_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
@@ -76,20 +67,26 @@ TensorSpec simple_value_join(const TensorSpec &a, const TensorSpec &b, join_fun_
TEST(SimpleValueTest, simple_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, SimpleValueBuilderFactory::get());
- TensorSpec actual = spec_from_value(*value);
- EXPECT_EQ(actual, expect);
+ for (TensorSpec expect : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ std::unique_ptr<Value> value = value_from_spec(expect, SimpleValueBuilderFactory::get());
+ TensorSpec actual = spec_from_value(*value);
+ EXPECT_EQ(actual, expect);
+ }
}
}
TEST(SimpleValueTest, simple_values_can_be_copied) {
for (const auto &layout: layouts) {
- TensorSpec expect = spec(layout, N());
- std::unique_ptr<Value> value = value_from_spec(expect, SimpleValueBuilderFactory::get());
- std::unique_ptr<Value> copy = SimpleValueBuilderFactory::get().copy(*value);
- TensorSpec actual = spec_from_value(*copy);
- EXPECT_EQ(actual, expect);
+ for (TensorSpec expect : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ std::unique_ptr<Value> value = value_from_spec(expect, SimpleValueBuilderFactory::get());
+ std::unique_ptr<Value> copy = SimpleValueBuilderFactory::get().copy(*value);
+ TensorSpec actual = spec_from_value(*copy);
+ EXPECT_EQ(actual, expect);
+ }
}
}
@@ -126,16 +123,26 @@ TEST(SimpleValueTest, simple_value_can_be_built_and_inspected) {
EXPECT_EQ(result["bb"], 3);
}
+GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; };
+
TEST(SimpleValueTest, new_generic_join_works_for_simple_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::Div::f}) {
- SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
- auto expect = ReferenceOperations::join(lhs, rhs, fun);
- auto actual = simple_value_join(lhs, rhs, fun);
- EXPECT_EQ(actual, expect);
+ const auto l = join_layouts[i].cpy().seq(N_16ths);
+ const auto r = join_layouts[i + 1].cpy().seq(N_16ths);
+ for (TensorSpec lhs : { l.cpy().cells_float().gen(),
+ l.cpy().cells_double().gen() })
+ {
+ for (TensorSpec rhs : { r.cpy().cells_float().gen(),
+ r.cpy().cells_double().gen() })
+ {
+ 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 = ReferenceOperations::join(lhs, rhs, fun);
+ auto actual = simple_value_join(lhs, rhs, fun);
+ EXPECT_EQ(actual, expect);
+ }
+ }
}
}
}
diff --git a/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp b/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp
index 23bd16cb721..dd21b663fa9 100644
--- a/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp
+++ b/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp
@@ -8,7 +8,7 @@
#include <vespa/eval/instruction/dense_cell_range_function.h>
#include <vespa/eval/instruction/dense_lambda_peek_function.h>
#include <vespa/eval/instruction/fast_rename_optimizer.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/eval/eval/tensor_nodes.h>
@@ -25,15 +25,15 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add("a", spec(1))
- .add("b", spec(2))
- .add("x3", spec({x(3)}, N()))
- .add("x3f", spec(float_cells({x(3)}), N()))
- .add("x3m", spec({x({"0", "1", "2"})}, N()))
- .add("x3y5", spec({x(3), y(5)}, N()))
- .add("x3y5f", spec(float_cells({x(3), y(5)}), N()))
- .add("x15", spec({x(15)}, N()))
- .add("x15f", spec(float_cells({x(15)}), N()));
+ .add("a", GenSpec().seq_bias(1).gen())
+ .add("b", GenSpec().seq_bias(2).gen())
+ .add("x3", GenSpec().idx("x", 3).gen())
+ .add("x3f", GenSpec().idx("x", 3).cells_float().gen())
+ .add("x3m", GenSpec().map("x", 3).gen())
+ .add("x3y5", GenSpec().idx("x", 3).idx("y", 5).gen())
+ .add("x3y5f", GenSpec().idx("x", 3).idx("y", 5).cells_float().gen())
+ .add("x15", GenSpec().idx("x", 15).gen())
+ .add("x15f", GenSpec().idx("x", 15).cells_float().gen());
}
EvalFixture::ParamRepo param_repo = make_params();
diff --git a/eval/src/tests/eval/value_codec/value_codec_test.cpp b/eval/src/tests/eval/value_codec/value_codec_test.cpp
index 2b03cffe730..110b58c27de 100644
--- a/eval/src/tests/eval/value_codec/value_codec_test.cpp
+++ b/eval/src/tests/eval/value_codec/value_codec_test.cpp
@@ -2,7 +2,7 @@
#include <iostream>
#include <vespa/eval/eval/simple_value.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/eval/value_codec.h>
#include <vespa/vespalib/data/memory.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -15,28 +15,30 @@ using namespace vespalib::eval::test;
const ValueBuilderFactory &factory = SimpleValueBuilderFactory::get();
-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"})})
+GenSpec G() { return GenSpec(); }
+
+const std::vector<GenSpec> layouts = {
+ G(),
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"})
};
TEST(ValueCodecTest, simple_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, factory);
- TensorSpec actual = spec_from_value(*value);
- EXPECT_EQ(actual, expect);
+ for (TensorSpec expect : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ std::unique_ptr<Value> value = value_from_spec(expect, factory);
+ TensorSpec actual = spec_from_value(*value);
+ EXPECT_EQ(actual, expect);
+ }
}
}
@@ -66,8 +68,8 @@ TEST(ValueCodecTest, simple_values_can_be_built_using_tensor_spec) {
.add({{"w", "yyy"}, {"x", 1}, {"y", "yyy"}, {"z", 0}}, 0.0)
.add({{"w", "yyy"}, {"x", 1}, {"y", "yyy"}, {"z", 1}}, 4.0);
Value::UP full_tensor = value_from_spec(full_spec, factory);
- EXPECT_EQUAL(full_spec, spec_from_value(*tensor));
- EXPECT_EQUAL(full_spec, spec_from_value(*full_tensor));
+ EXPECT_EQ(full_spec, spec_from_value(*tensor));
+ EXPECT_EQ(full_spec, spec_from_value(*full_tensor));
};
//-----------------------------------------------------------------------------
@@ -333,11 +335,11 @@ TEST(ValueCodecTest, bad_sparse_tensors_are_caught) {
bad.encode_default(data_default);
bad.encode_with_double(data_double);
bad.encode_with_float(data_float);
- EXPECT_EXCEPTION(decode_value(data_default, factory), vespalib::IllegalStateException,
+ VESPA_EXPECT_EXCEPTION(decode_value(data_default, factory), vespalib::IllegalStateException,
"serialized input claims 12345678 blocks of size 1*8, but only");
- EXPECT_EXCEPTION(decode_value(data_double, factory), vespalib::IllegalStateException,
+ VESPA_EXPECT_EXCEPTION(decode_value(data_double, factory), vespalib::IllegalStateException,
"serialized input claims 12345678 blocks of size 1*8, but only");
- EXPECT_EXCEPTION(decode_value(data_float, factory), vespalib::IllegalStateException,
+ VESPA_EXPECT_EXCEPTION(decode_value(data_float, factory), vespalib::IllegalStateException,
"serialized input claims 12345678 blocks of size 1*4, but only");
}
@@ -386,11 +388,11 @@ TEST(ValueCodecTest, bad_dense_tensors_are_caught) {
bad.encode_default(data_default);
bad.encode_with_double(data_double);
bad.encode_with_float(data_float);
- EXPECT_EXCEPTION(decode_value(data_default, factory), vespalib::IllegalStateException,
+ VESPA_EXPECT_EXCEPTION(decode_value(data_default, factory), vespalib::IllegalStateException,
"serialized input claims 1 blocks of size 60000*8, but only");
- EXPECT_EXCEPTION(decode_value(data_double, factory), vespalib::IllegalStateException,
+ VESPA_EXPECT_EXCEPTION(decode_value(data_double, factory), vespalib::IllegalStateException,
"serialized input claims 1 blocks of size 60000*8, but only");
- EXPECT_EXCEPTION(decode_value(data_float, factory), vespalib::IllegalStateException,
+ VESPA_EXPECT_EXCEPTION(decode_value(data_float, factory), vespalib::IllegalStateException,
"serialized input claims 1 blocks of size 60000*4, but only");
}
diff --git a/eval/src/tests/instruction/add_trivial_dimension_optimizer/add_trivial_dimension_optimizer_test.cpp b/eval/src/tests/instruction/add_trivial_dimension_optimizer/add_trivial_dimension_optimizer_test.cpp
index 35195522adc..d2dccfde2fd 100644
--- a/eval/src/tests/instruction/add_trivial_dimension_optimizer/add_trivial_dimension_optimizer_test.cpp
+++ b/eval/src/tests/instruction/add_trivial_dimension_optimizer/add_trivial_dimension_optimizer_test.cpp
@@ -5,7 +5,7 @@
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/instruction/replace_type_function.h>
#include <vespa/eval/instruction/fast_rename_optimizer.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -20,11 +20,11 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add("x5", spec({x(5)}, N()))
- .add("x5f", spec(float_cells({x(5)}), N()))
- .add("x5y1", spec({x(5),y(1)}, N()))
- .add("y1z1", spec({y(1),z(1)}, N()))
- .add("x_m", spec({x({"a"})}, N()));
+ .add("x5", GenSpec().idx("x", 5).gen())
+ .add("x5f", GenSpec().idx("x", 5).cells_float().gen())
+ .add("x5y1", GenSpec().idx("x", 5).idx("y", 1).gen())
+ .add("y1z1", GenSpec().idx("y", 5).idx("z", 1).gen())
+ .add("x_m", GenSpec().map("x", {"a"}).gen());
}
EvalFixture::ParamRepo param_repo = make_params();
diff --git a/eval/src/tests/instruction/dense_replace_type_function/dense_replace_type_function_test.cpp b/eval/src/tests/instruction/dense_replace_type_function/dense_replace_type_function_test.cpp
index 988ca79a04a..5dcdbc5bab8 100644
--- a/eval/src/tests/instruction/dense_replace_type_function/dense_replace_type_function_test.cpp
+++ b/eval/src/tests/instruction/dense_replace_type_function/dense_replace_type_function_test.cpp
@@ -5,7 +5,7 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/eval/eval/interpreted_function.h>
#include <vespa/eval/instruction/replace_type_function.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
using namespace vespalib::eval::tensor_function;
using namespace vespalib::eval::test;
@@ -33,7 +33,7 @@ struct Fixture {
std::vector<TensorFunction::Child::CREF> children;
InterpretedFunction::State state;
Fixture()
- : my_value(value_from_spec(spec({x(10)}, N()), prod_factory)),
+ : my_value(value_from_spec(GenSpec().idx("x", 10).gen(), prod_factory)),
new_type(ValueType::from_spec("tensor(x[5],y[2])")),
mock_child(my_value->type()),
my_fun(new_type, mock_child),
diff --git a/eval/src/tests/instruction/fast_rename_optimizer/fast_rename_optimizer_test.cpp b/eval/src/tests/instruction/fast_rename_optimizer/fast_rename_optimizer_test.cpp
index dc90a5e54a1..e915a396ae7 100644
--- a/eval/src/tests/instruction/fast_rename_optimizer/fast_rename_optimizer_test.cpp
+++ b/eval/src/tests/instruction/fast_rename_optimizer/fast_rename_optimizer_test.cpp
@@ -4,7 +4,7 @@
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/instruction/replace_type_function.h>
#include <vespa/eval/instruction/fast_rename_optimizer.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -19,13 +19,13 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add("x5", spec({x(5)}, N()))
- .add("x5f", spec(float_cells({x(5)}), N()))
- .add("x_m", spec({x({"a", "b", "c"})}, N()))
- .add("xy_mm", spec({x({"a", "b", "c"}),y({"d","e"})}, N()))
- .add("x5y3z_m", spec({x(5),y(3),z({"a","b"})}, N()))
- .add("x5yz_m", spec({x(5),y({"a","b"}),z({"d","e"})}, N()))
- .add("x5y3", spec({x(5),y(3)}, N()));
+ .add("x5", GenSpec().idx("x", 5).gen())
+ .add("x5f", GenSpec().idx("x", 5).cells_float().gen())
+ .add("x_m", GenSpec().map("x", {"a", "b", "c"}).gen())
+ .add("xy_mm", GenSpec().map("x", {"a", "b", "c"}).map("y", {"d","e"}).gen())
+ .add("x5y3z_m", GenSpec().idx("x", 5).idx("y", 3).map("z", {"a","b"}).gen())
+ .add("x5yz_m", GenSpec().idx("x", 5).map("y", {"a","b"}).map("z", {"d","e"}).gen())
+ .add("x5y3", GenSpec().idx("x", 5).idx("y", 3).gen());
}
EvalFixture::ParamRepo param_repo = make_params();
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 bc8ea84744f..17e012b8e33 100644
--- a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
+++ b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
@@ -7,7 +7,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -18,53 +18,48 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-std::vector<Layout> concat_layouts = {
- {}, {},
- {}, {y(5)},
- float_cells({y(5)}), {},
- {}, float_cells({y(5)}),
- {y(5)}, {},
- {y(2)}, {y(3)},
- {y(2)}, {x(3)},
- {x(2)}, {z(3)},
- {x(2),y(3)}, {x(2),y(3)},
- {x(2),y(3)}, {x(2),y(4)},
- {y(3),z(5)}, {y(3),z(5)},
- {y(3),z(5)}, {y(4),z(5)},
- {x(2),y(3),z(5)}, {x(2),y(3),z(5)},
- {x(2),y(3),z(5)}, {x(2),y(4),z(5)},
- {x(2),y(3),z({"a","b"})}, {x(2),y(3),z({"b","c"})},
- {x(2),y(3),z({"a","b"})}, {x(2),y(4),z({"b","c"})},
- {y(5)}, {y(2),x(5)},
- {x(3)}, {y(2),z(3)},
- {y(2)}, {y(3),x(5),z(2)},
- {y(2),x(5),z(2)}, {y(3),x(5),z(2)},
- {y(3),x(5)}, {x(5),z(7)},
- float_cells({y(3),x(5)}), {x(5),z(7)},
- float_cells({y(3),x(5)}), {},
- {y(3),x(5)}, float_cells({x(5),z(7)}),
- float_cells({y(3),x(5)}), float_cells({x(5),z(7)}),
- {x({"a","b","c"})}, {x({"a","b","c"})},
- {x({"a","b","c"})}, {x({"a","b"})},
- {x({"a","b","c"})}, {x({"b","c","d"})},
- float_cells({x({"a","b","c"})}), {x({"b","c","d"})},
- {x({"a","b","c"})}, float_cells({x({"b","c","d"})}),
- float_cells({x({"a","b","c"})}), float_cells({z({"foo","bar","baz"})}),
- {x({"a","b","c"})}, {x({"a","b","c"}),z({"foo","bar","baz"})},
- {x({"a","b"}),z({"foo","bar","baz"})}, {x({"a","b","c"}),z({"foo","bar"})},
- {x({"a","b","c"}),y(3)}, {y(2)},
- {x({"a","b","c"}),y(3)}, {z(5)},
- {x({"a","b","c"}),y(3)}, {y(2),z(5)},
- {x({"a","b","c"}),y(3)}, {y(2)},
- {x({"a","b","c"}),y(3),z(5)}, {z(5)},
- {y(2)}, {x({"a","b","c"}),y(3)},
- {z(5)}, {x({"a","b","c"}),y(3)},
- {y(2),z(5)}, {x({"a","b","c"}),y(3)},
- {y(2)}, {x({"a","b","c"}),y(3)},
- {z(5)}, {x({"a","b","c"}),y(3),z(5)},
- {y(2),z(5)}, {x({"a","b","c"}),y(3),z(5)},
- {y(2),x({"a","b","c"})}, {y(3),x({"b","c","d"})},
- {y(2),x({"a","b"})}, {y(3),z({"c","d"})}
+GenSpec G() { return GenSpec(); }
+
+GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; };
+
+const std::vector<GenSpec> concat_layouts = {
+ G(), G(),
+ G(), G().idx("y", 5),
+ G().idx("y", 5), G(),
+ G().idx("y", 2), G().idx("y", 3),
+ G().idx("y", 2), G().idx("x", 3),
+ G().idx("x", 2), G().idx("z", 3),
+ G().idx("x", 2).idx("y", 3), G().idx("x", 2).idx("y", 3),
+ G().idx("x", 2).idx("y", 3), G().idx("x", 2).idx("y", 4),
+ G().idx("y", 3).idx("z", 5), G().idx("y", 3).idx("z", 5),
+ G().idx("y", 3).idx("z", 5), G().idx("y", 4).idx("z", 5),
+ G().idx("x", 2).idx("y", 3).idx("z", 5), G().idx("x", 2).idx("y", 3).idx("z", 5),
+ G().idx("x", 2).idx("y", 3).idx("z", 5), G().idx("x", 2).idx("y", 4).idx("z", 5),
+ G().idx("x", 2).idx("y", 3).map("z", {"a","b"}), G().idx("x", 2).idx("y", 3).map("z", {"b","c"}),
+ G().idx("x", 2).idx("y", 3).map("z", {"a","b"}), G().idx("x", 2).idx("y", 4).map("z", {"b","c"}),
+ G().idx("y", 5), G().idx("x", 5).idx("y", 2),
+ G().idx("x", 3), G().idx("y", 2).idx("z", 3),
+ G().idx("y", 2), G().idx("x", 5).idx("y", 3).idx("z", 2),
+ G().idx("x", 5).idx("y", 2).idx("z", 2), G().idx("x", 5).idx("y", 3).idx("z", 2),
+ G().idx("x", 5).idx("y", 3), G().idx("x", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"b","c","d"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}).map("z", {"foo","bar","baz"}),
+ G().map("x", {"a","b"}).map("z", {"foo","bar","baz"}), G().map("x", {"a","b","c"}).map("z", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).idx("y", 3), G().idx("y", 2),
+ G().map("x", {"a","b","c"}).idx("y", 3), G().idx("z", 5),
+ G().map("x", {"a","b","c"}).idx("y", 3), G().idx("y", 2).idx("z", 5),
+ G().map("x", {"a","b","c"}).idx("y", 3), G().idx("y", 2),
+ G().map("x", {"a","b","c"}).idx("y", 3).idx("z", 5), G().idx("z", 5),
+ G().idx("y", 2), G().map("x", {"a","b","c"}).idx("y", 3),
+ G().idx("z", 5), G().map("x", {"a","b","c"}).idx("y", 3),
+ G().idx("y", 2).idx("z", 5), G().map("x", {"a","b","c"}).idx("y", 3),
+ G().idx("y", 2), G().map("x", {"a","b","c"}).idx("y", 3),
+ G().idx("z", 5), G().map("x", {"a","b","c"}).idx("y", 3).idx("z", 5),
+ G().idx("y", 2).idx("z", 5), G().map("x", {"a","b","c"}).idx("y", 3).idx("z", 5),
+ G().map("x", {"a","b","c"}).idx("y", 2), G().map("x", {"b","c","d"}).idx("y", 3),
+ G().map("x", {"a","b"}).idx("y", 2), G().idx("y", 3).map("z", {"c","d"})
};
TensorSpec perform_generic_concat(const TensorSpec &a, const TensorSpec &b,
@@ -81,12 +76,20 @@ TensorSpec perform_generic_concat(const TensorSpec &a, const TensorSpec &b,
void test_generic_concat_with(const ValueBuilderFactory &factory) {
ASSERT_TRUE((concat_layouts.size() % 2) == 0);
for (size_t i = 0; i < concat_layouts.size(); i += 2) {
- 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 = perform_generic_concat(lhs, rhs, "y", factory);
- auto expect = ReferenceOperations::concat(lhs, rhs, "y");
- EXPECT_EQ(actual, expect);
+ const auto l = concat_layouts[i];
+ const auto r = concat_layouts[i+1].cpy().seq(N_16ths);
+ for (TensorSpec lhs : { l.cpy().cells_float().gen(),
+ l.cpy().cells_double().gen() })
+ {
+ for (TensorSpec rhs : { r.cpy().cells_float().gen(),
+ r.cpy().cells_double().gen() })
+ {
+ 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 = 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 00af75e4d83..fcf4618d592 100644
--- a/eval/src/tests/instruction/generic_create/generic_create_test.cpp
+++ b/eval/src/tests/instruction/generic_create/generic_create_test.cpp
@@ -6,7 +6,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <stdlib.h>
@@ -19,18 +19,17 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-std::vector<Layout> create_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"})})
+GenSpec G() { return GenSpec(); }
+
+const std::vector<GenSpec> create_layouts = {
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"})
};
TensorSpec remove_each(const TensorSpec &a, size_t n) {
@@ -91,16 +90,19 @@ TensorSpec perform_generic_create(const TensorSpec &a, const ValueBuilderFactory
}
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);
- auto expect = reference_create(full).normalize();
- EXPECT_EQ(actual, expect);
- for (size_t n : {2, 3, 4, 5}) {
- TensorSpec partial = remove_each(full, n);
- actual = perform_generic_create(partial, factory);
- expect = reference_create(partial).normalize();
+ for (const auto &layout : create_layouts) {
+ for (TensorSpec full : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ auto actual = perform_generic_create(full, factory);
+ auto expect = reference_create(full).normalize();
EXPECT_EQ(actual, expect);
+ for (size_t n : {2, 3, 4, 5}) {
+ TensorSpec partial = remove_each(full, n);
+ actual = perform_generic_create(partial, factory);
+ expect = reference_create(partial).normalize();
+ 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 8eca3cad763..f724cdf1024 100644
--- a/eval/src/tests/instruction/generic_join/generic_join_test.cpp
+++ b/eval/src/tests/instruction/generic_join/generic_join_test.cpp
@@ -6,7 +6,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -17,33 +17,25 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-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"})}),
- {x({"a","b","c"}),y(5)}, float_cells({y(5)}),
- {y(5)}, float_cells({x({"a","b","c"}),y(5)}),
- {x({}),y(5)}, float_cells({y(5)})
+GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; };
+
+GenSpec G() { return GenSpec().seq(N_16ths); }
+
+const std::vector<GenSpec> join_layouts = {
+ G(), G(),
+ G().idx("x", 5), G().idx("x", 5),
+ G().idx("x", 5), G().idx("y", 5),
+ G().idx("x", 5), G().idx("x", 5).idx("y", 5),
+ G().idx("y", 3), G().idx("x", 2).idx("z", 3),
+ G().idx("x", 3).idx("y", 5), G().idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b"}),
+ G().map("x", {"a","b","c"}), G().map("y", {"foo","bar","baz"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}).map("y", {"foo","bar","baz"}),
+ G().map("x", {"a","b"}).map("y", {"foo","bar","baz"}), G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b"}).map("y", {"foo","bar","baz"}), G().map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}), G().map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5), G().idx("y", 5).map("z", {"i","j","k","l"})
};
bool join_address(const TensorSpec::Address &a, const TensorSpec::Address &b, TensorSpec::Address &addr) {
@@ -113,15 +105,23 @@ TEST(GenericJoinTest, dense_join_plan_can_be_executed) {
TEST(GenericJoinTest, generic_join_works_for_simple_and_fast_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::Div::f}) {
- SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
- 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);
- EXPECT_EQ(fast, expect);
+ const auto &l = join_layouts[i];
+ const auto &r = join_layouts[i+1];
+ for (TensorSpec lhs : { l.cpy().cells_float().gen(),
+ l.cpy().cells_double().gen() })
+ {
+ for (TensorSpec rhs : { r.cpy().cells_float().gen(),
+ r.cpy().cells_double().gen() })
+ {
+ 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 = 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);
+ EXPECT_EQ(fast, 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 687b6aa60ac..8e39fa68072 100644
--- a/eval/src/tests/instruction/generic_map/generic_map_test.cpp
+++ b/eval/src/tests/instruction/generic_map/generic_map_test.cpp
@@ -6,7 +6,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -17,18 +17,20 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-std::vector<Layout> map_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"})})
+GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; };
+
+GenSpec G() { return GenSpec().seq(N_16ths); }
+
+const std::vector<GenSpec> map_layouts = {
+ G(),
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"})
};
TensorSpec perform_generic_map(const TensorSpec &a, map_fun_t func, const ValueBuilderFactory &factory)
@@ -40,14 +42,16 @@ TensorSpec perform_generic_map(const TensorSpec &a, map_fun_t func, const ValueB
}
void test_generic_map_with(const ValueBuilderFactory &factory) {
- for (const auto & layout : map_layouts) {
- TensorSpec lhs = spec(layout, Div16(N()));
- 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 = ReferenceOperations::map(lhs, func);
- auto actual = perform_generic_map(lhs, func, factory);
- EXPECT_EQ(actual, expect);
+ for (const auto &layout : map_layouts) {
+ for (TensorSpec lhs : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ 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 = ReferenceOperations::map(lhs, func);
+ auto actual = perform_generic_map(lhs, func, factory);
+ 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 60a27e6f6e9..d5f7bc071f6 100644
--- a/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
+++ b/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
@@ -6,7 +6,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <optional>
@@ -18,20 +18,22 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-std::vector<Layout> merge_layouts = {
- {}, {},
- {x(5)}, {x(5)},
- {x(3),y(5)}, {x(3),y(5)},
- float_cells({x(3),y(5)}), {x(3),y(5)},
- {x(3),y(5)}, float_cells({x(3),y(5)}),
- {x({"a","b","c"})}, {x({"a","b","c"})},
- {x({"a","b","c"})}, {x({"c","d","e"})},
- {x({"a","c","e"})}, {x({"b","c","d"})},
- {x({"b","c","d"})}, {x({"a","c","e"})},
- {x({"a","b","c"})}, {x({"c","d"})},
- {x({"a","b"}),y({"foo","bar","baz"})}, {x({"b","c"}),y({"any","foo","bar"})},
- {x(3),y({"foo", "bar"})}, {x(3),y({"baz", "bar"})},
- {x({"a","b","c"}),y(5)}, {x({"b","c","d"}),y(5)}
+GenSpec G() { return GenSpec(); }
+
+GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; };
+
+const std::vector<GenSpec> merge_layouts = {
+ G(), G(),
+ G().idx("x", 5), G().idx("x", 5),
+ G().idx("x", 3).idx("y", 5), G().idx("x", 3).idx("y", 5),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"c","d","e"}),
+ G().map("x", {"a","c","e"}), G().map("x", {"b","c","d"}),
+ G().map("x", {"b","c","d"}), G().map("x", {"a","c","e"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"c","d"}),
+ G().map("x", {"a","b"}).map("y", {"foo","bar","baz"}), G().map("x", {"b","c"}).map("y", {"any","foo","bar"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}), G().idx("x", 3).map("y", {"baz", "bar"}),
+ G().map("x", {"a","b","c"}).idx("y", 5), G().map("x", {"b","c","d"}).idx("y", 5)
};
TensorSpec perform_generic_merge(const TensorSpec &a, const TensorSpec &b, join_fun_t fun, const ValueBuilderFactory &factory) {
@@ -46,13 +48,21 @@ TensorSpec perform_generic_merge(const TensorSpec &a, const TensorSpec &b, join_
void test_generic_merge_with(const ValueBuilderFactory &factory) {
ASSERT_TRUE((merge_layouts.size() % 2) == 0);
for (size_t i = 0; i < merge_layouts.size(); i += 2) {
- TensorSpec lhs = spec(merge_layouts[i], N());
- 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 = ReferenceOperations::merge(lhs, rhs, fun);
- auto actual = perform_generic_merge(lhs, rhs, fun, factory);
- EXPECT_EQ(actual, expect);
+ const auto l = merge_layouts[i];
+ const auto r = merge_layouts[i+1].cpy().seq(N_16ths);
+ for (TensorSpec lhs : { l.cpy().cells_float().gen(),
+ l.cpy().cells_double().gen() })
+ {
+ for (TensorSpec rhs : { r.cpy().cells_float().gen(),
+ r.cpy().cells_double().gen() })
+ {
+ 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 = ReferenceOperations::merge(lhs, rhs, fun);
+ auto actual = perform_generic_merge(lhs, rhs, fun, factory);
+ 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 6841215038a..c80e8a1296b 100644
--- a/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp
+++ b/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp
@@ -7,7 +7,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/overload.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -22,17 +22,16 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-std::vector<Layout> peek_layouts = {
- {x(4)},
- {x(4),y(5)},
- {x(4),y(5),z(3)},
- float_cells({x(4),y(5),z(3)}),
- {x({"-1","0","2"})},
- {x({"-1","0","2"}),y({"-2","0","1"}),z({"-2","-1","0","1","2"})},
- float_cells({x({"-1","0","2"}),y({"-2","0","1"})}),
- {x(4),y({"-2","0","1"}),z(3)},
- {x({"-1","0","2"}),y(5),z({"-2","-1","0","1","2"})},
- float_cells({x({"-1","0","2"}),y(5),z({"-2","-1","0","1","2"})})
+GenSpec G() { return GenSpec(); }
+
+const std::vector<GenSpec> peek_layouts = {
+ G().idx("x", 4),
+ G().idx("x", 4).idx("y", 5),
+ G().idx("x", 4).idx("y", 5).idx("z", 3),
+ G().map("x", {"-1","0","2"}),
+ G().map("x", {"-1","0","2"}).map("y", {"-2","0","1"}).map("z", {"-2","-1","0","1","2"}),
+ G().idx("x", 4).map("y", {"-2","0","1"}).idx("z", 3),
+ G().map("x", {"-1","0","2"}).idx("y", 5).map("z", {"-2","-1","0","1","2"})
};
using PeekSpec = GenericPeek::SpecMap;
@@ -194,12 +193,15 @@ void fill_dims_and_check(const TensorSpec &input,
}
void test_generic_peek_with(const ValueBuilderFactory &factory) {
- for (const auto & layout : peek_layouts) {
- TensorSpec input = spec(layout, N());
- ValueType input_type = ValueType::from_spec(input.type());
- const auto &dims = input_type.dimensions();
- PeekSpec spec;
- fill_dims_and_check(input, spec, dims, factory);
+ for (const auto &layout : peek_layouts) {
+ for (TensorSpec input : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ ValueType input_type = ValueType::from_spec(input.type());
+ const auto &dims = input_type.dimensions();
+ PeekSpec spec;
+ fill_dims_and_check(input, spec, dims, factory);
+ }
}
}
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 9e2090fa968..2c5baf234c4 100644
--- a/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
+++ b/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
@@ -6,7 +6,7 @@
#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/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <optional>
@@ -18,22 +18,23 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-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({})},
- {x({}),y(10)},
- {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"})}),
- {x(3),y({}),z(7)}
+GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; };
+
+GenSpec G() { return GenSpec().seq(N_16ths); }
+
+const std::vector<GenSpec> layouts = {
+ G(),
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {}),
+ G().map("x", {}).idx("y", 10),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {}).idx("z", 7)
};
TensorSpec perform_generic_reduce(const TensorSpec &a, Aggr aggr, const std::vector<vespalib::string> &dims,
@@ -68,19 +69,23 @@ TEST(GenericReduceTest, sparse_reduce_plan_can_be_created) {
}
void test_generic_reduce_with(const ValueBuilderFactory &factory) {
- for (const Layout &layout: layouts) {
- TensorSpec input = spec(layout, Div16(N()));
- SCOPED_TRACE(fmt("tensor type: %s, num_cells: %zu", input.type().c_str(), input.cells().size()));
- for (Aggr aggr: {Aggr::SUM, Aggr::AVG, Aggr::MIN, Aggr::MAX}) {
- SCOPED_TRACE(fmt("aggregator: %s", AggrNames::name_of(aggr)->c_str()));
- for (const Domain &domain: layout) {
- auto expect = ReferenceOperations::reduce(input, aggr, {domain.dimension}).normalize();
- auto actual = perform_generic_reduce(input, aggr, {domain.dimension}, factory);
+ for (const auto &layout: layouts) {
+ for (TensorSpec input : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ SCOPED_TRACE(fmt("tensor type: %s, num_cells: %zu", input.type().c_str(), input.cells().size()));
+ for (Aggr aggr: {Aggr::SUM, Aggr::AVG, Aggr::MIN, Aggr::MAX}) {
+ SCOPED_TRACE(fmt("aggregator: %s", AggrNames::name_of(aggr)->c_str()));
+ auto t = layout.type();
+ for (const auto & dim: t.dimensions()) {
+ auto expect = ReferenceOperations::reduce(input, aggr, {dim.name}).normalize();
+ auto actual = perform_generic_reduce(input, aggr, {dim.name}, factory);
+ EXPECT_EQ(actual, expect);
+ }
+ auto expect = ReferenceOperations::reduce(input, aggr, {}).normalize();
+ auto actual = perform_generic_reduce(input, aggr, {}, factory);
EXPECT_EQ(actual, expect);
}
- auto expect = ReferenceOperations::reduce(input, aggr, {}).normalize();
- auto actual = perform_generic_reduce(input, aggr, {}, factory);
- 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 20d155822b5..f0c2241202e 100644
--- a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
+++ b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
@@ -5,7 +5,7 @@
#include <vespa/eval/eval/value_codec.h>
#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/gen_spec.h>
#include <vespa/eval/eval/test/reference_operations.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -17,18 +17,17 @@ using namespace vespalib::eval::test;
using vespalib::make_string_short::fmt;
-std::vector<Layout> rename_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"})})
+GenSpec G() { return GenSpec(); }
+
+const std::vector<GenSpec> rename_layouts = {
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"})
};
struct FromTo {
@@ -110,18 +109,20 @@ TensorSpec perform_generic_rename(const TensorSpec &a,
}
void test_generic_rename_with(const ValueBuilderFactory &factory) {
- for (const auto & layout : rename_layouts) {
- TensorSpec lhs = spec(layout, N());
- ValueType lhs_type = ValueType::from_spec(lhs.type());
- // printf("lhs_type: %s\n", lhs_type.to_spec().c_str());
- for (const auto & from_to : rename_from_to) {
- ValueType renamed_type = lhs_type.rename(from_to.from, from_to.to);
- 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 = ReferenceOperations::rename(lhs, from_to.from, from_to.to);
- auto actual = perform_generic_rename(lhs, from_to, factory);
- EXPECT_EQ(actual, expect);
+ for (const auto &layout : rename_layouts) {
+ for (TensorSpec lhs : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ ValueType lhs_type = ValueType::from_spec(lhs.type());
+ for (const auto & from_to : rename_from_to) {
+ ValueType renamed_type = lhs_type.rename(from_to.from, from_to.to);
+ 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 = ReferenceOperations::rename(lhs, from_to.from, from_to.to);
+ auto actual = perform_generic_rename(lhs, from_to, factory);
+ EXPECT_EQ(actual, expect);
+ }
}
}
}
diff --git a/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp b/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp
index a3fbb3ed529..e6a256a493b 100644
--- a/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp
+++ b/eval/src/tests/instruction/join_with_number/join_with_number_function_test.cpp
@@ -4,8 +4,7 @@
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
-#include <vespa/eval/eval/test/param_variants.h>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/instruction/join_with_number_function.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -36,13 +35,12 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
auto repo = EvalFixture::ParamRepo()
- .add("a", spec(1.5))
- .add("number", spec(2.5))
- .add("dense", spec({y(5)}, N()))
- .add_matrix("x", 3, "y", 5);
-
- add_variants(repo, "mixed", {x({"a"}),y(5),z({"d","e"})}, N());
- add_variants(repo, "sparse", {x({"a","b","c"}),z({"d","e","f"})}, N());
+ .add("a", GenSpec().seq_bias(1.5).gen())
+ .add("number", GenSpec().seq_bias(2.5).gen())
+ .add("dense", GenSpec().idx("y", 5).gen())
+ .add_variants("x3y5", GenSpec().idx("x", 3).idx("y", 5))
+ .add_variants("mixed", GenSpec().map("x", {"a"}).idx("y", 5).map("z", {"d","e"}))
+ .add_variants("sparse", GenSpec().map("x", {"a","b","c"}).map("z", {"d","e","f"}));
return repo;
}
@@ -81,22 +79,22 @@ void verify_not_optimized(const vespalib::string &expr) {
TEST("require that dense number join can be optimized") {
TEST_DO(verify_optimized("x3y5+a", Primary::LHS, false));
TEST_DO(verify_optimized("a+x3y5", Primary::RHS, false));
- TEST_DO(verify_optimized("x3y5f*a", Primary::LHS, false));
- TEST_DO(verify_optimized("a*x3y5f", Primary::RHS, false));
+ TEST_DO(verify_optimized("x3y5_f*a", Primary::LHS, false));
+ TEST_DO(verify_optimized("a*x3y5_f", Primary::RHS, false));
}
TEST("require that dense number join can be inplace") {
TEST_DO(verify_optimized("@x3y5*a", Primary::LHS, true));
TEST_DO(verify_optimized("a*@x3y5", Primary::RHS, true));
- TEST_DO(verify_optimized("@x3y5f+a", Primary::LHS, true));
- TEST_DO(verify_optimized("a+@x3y5f", Primary::RHS, true));
+ TEST_DO(verify_optimized("@x3y5_f+a", Primary::LHS, true));
+ TEST_DO(verify_optimized("a+@x3y5_f", Primary::RHS, true));
}
TEST("require that asymmetric operations work") {
TEST_DO(verify_optimized("x3y5/a", Primary::LHS, false));
TEST_DO(verify_optimized("a/x3y5", Primary::RHS, false));
- TEST_DO(verify_optimized("x3y5f-a", Primary::LHS, false));
- TEST_DO(verify_optimized("a-x3y5f", Primary::RHS, false));
+ TEST_DO(verify_optimized("x3y5_f-a", Primary::LHS, false));
+ TEST_DO(verify_optimized("a-x3y5_f", Primary::RHS, false));
}
TEST("require that sparse number join can be optimized") {
diff --git a/eval/src/tests/instruction/mixed_inner_product_function/mixed_inner_product_function_test.cpp b/eval/src/tests/instruction/mixed_inner_product_function/mixed_inner_product_function_test.cpp
index fbe71f3ed63..6b549b4d4d4 100644
--- a/eval/src/tests/instruction/mixed_inner_product_function/mixed_inner_product_function_test.cpp
+++ b/eval/src/tests/instruction/mixed_inner_product_function/mixed_inner_product_function_test.cpp
@@ -3,7 +3,7 @@
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/instruction/dense_dot_product_function.h>
#include <vespa/eval/instruction/dense_matmul_function.h>
#include <vespa/eval/instruction/dense_multi_matmul_function.h>
@@ -22,34 +22,25 @@ using namespace vespalib::eval::test;
const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
-struct MyVecSeq : Sequence {
- double bias;
- double operator[](size_t i) const override { return (i + bias); }
- MyVecSeq(double cellBias) : bias(cellBias) {}
-};
-
-std::function<double(size_t)> my_vec_gen(double cellBias) {
- return [=] (size_t i) noexcept { return i + cellBias; };
-}
//-----------------------------------------------------------------------------
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add_vector("x", 3, my_vec_gen(2.0))
- .add_vector("x", 3, my_vec_gen(13.25))
- .add_vector("y", 3, my_vec_gen(4.0))
- .add_vector("z", 3, my_vec_gen(0.25))
- .add_matrix("x", 3, "y", 1, my_vec_gen(5.0))
- .add_matrix("x", 1, "y", 3, my_vec_gen(6.0))
- .add_matrix("x", 3, "y", 3, my_vec_gen(1.5))
- .add_matrix("x", 3, "z", 3, my_vec_gen(2.5))
- .add_cube("x", 3, "y", 3, "z", 3, my_vec_gen(-4.0))
- .add("mix_x3zm", spec({x(3),z({"c","d"})}, MyVecSeq(0.5)))
- .add("mix_y3zm", spec({y(3),z({"c","d"})}, MyVecSeq(3.5)))
- .add("mix_x3zm_f", spec(float_cells({x(3),z({"c","d"})}), MyVecSeq(0.5)))
- .add("mix_y3zm_f", spec(float_cells({y(3),z({"c","d"})}), MyVecSeq(3.5)))
- .add("mix_x3y3zm", spec({x(3),y(3),z({"c","d"})}, MyVecSeq(0.0)))
+ .add_variants("x3", GenSpec().idx("x", 3).seq_bias(2.0))
+ .add_variants("x3$2", GenSpec().idx("x", 3).seq_bias(13.25))
+ .add_variants("y3", GenSpec().idx("y", 3).seq_bias(4.0))
+ .add_variants("z3", GenSpec().idx("z", 3).seq_bias(0.25))
+ .add_variants("x3y3", GenSpec().idx("x", 3).idx("y", 3).seq_bias(5.0))
+ .add_variants("x1y3", GenSpec().idx("x", 1).idx("y", 3).seq_bias(6.0))
+ .add_variants("x3y1", GenSpec().idx("x", 3).idx("y", 1).seq_bias(1.5))
+ .add_variants("x3z3", GenSpec().idx("x", 3).idx("z", 3).seq_bias(2.5))
+ .add_variants("x3y3z3", GenSpec().idx("x", 3).idx("y", 3).idx("z", 3).seq_bias(-4.0))
+ .add("mix_x3zm", GenSpec().idx("x", 3).map("z", {"c","d"}).seq_bias(0.5).gen())
+ .add("mix_y3zm", GenSpec().idx("y", 3).map("z", {"c","d"}).seq_bias(3.5).gen())
+ .add("mix_x3zm_f", GenSpec().idx("x", 3).map("z", {"c","d"}).cells_float().seq_bias(0.5).gen())
+ .add("mix_y3zm_f", GenSpec().idx("y", 3).map("z", {"c","d"}).cells_float().seq_bias(3.5).gen())
+ .add("mix_x3y3zm", GenSpec().idx("x", 3).idx("y", 3).map("z", {"c","d"}).seq_bias(0.0).gen())
;
}
@@ -101,35 +92,35 @@ TEST(MixedInnerProduct, use_dense_optimizers_when_possible) {
TEST(MixedInnerProduct, trigger_optimizer_when_possible) {
assert_mixed_optimized("reduce(x3 * mix_x3zm,sum,x)");
- assert_mixed_optimized("reduce(x3f * mix_x3zm,sum,x)");
+ assert_mixed_optimized("reduce(x3_f * mix_x3zm,sum,x)");
assert_mixed_optimized("reduce(x3 * mix_x3zm_f,sum,x)");
- assert_mixed_optimized("reduce(x3f * mix_x3zm_f,sum,x)");
+ assert_mixed_optimized("reduce(x3_f * mix_x3zm_f,sum,x)");
assert_mixed_optimized("reduce(x3$2 * mix_x3zm,sum,x)");
- assert_mixed_optimized("reduce(x3f$2 * mix_x3zm,sum,x)");
+ assert_mixed_optimized("reduce(x3$2_f * mix_x3zm,sum,x)");
assert_mixed_optimized("reduce(y3 * mix_y3zm,sum,y)");
- assert_mixed_optimized("reduce(y3f * mix_y3zm,sum,y)");
+ assert_mixed_optimized("reduce(y3_f * mix_y3zm,sum,y)");
assert_mixed_optimized("reduce(y3 * mix_y3zm_f,sum,y)");
- assert_mixed_optimized("reduce(y3f * mix_y3zm_f,sum,y)");
+ assert_mixed_optimized("reduce(y3_f * mix_y3zm_f,sum,y)");
assert_mixed_optimized("reduce(x3y1 * mix_x3zm,sum,x)");
- assert_mixed_optimized("reduce(x3y1f * mix_x3zm,sum,x)");
+ assert_mixed_optimized("reduce(x3y1_f * mix_x3zm,sum,x)");
assert_mixed_optimized("reduce(x3y1 * mix_x3zm,sum,x,y)");
- assert_mixed_optimized("reduce(x3y1f * mix_x3zm,sum,x,y)");
+ assert_mixed_optimized("reduce(x3y1_f * mix_x3zm,sum,x,y)");
assert_mixed_optimized("reduce(x1y3 * mix_y3zm,sum,y)");
- assert_mixed_optimized("reduce(x1y3f * mix_y3zm,sum,y)");
+ assert_mixed_optimized("reduce(x1y3_f * mix_y3zm,sum,y)");
assert_mixed_optimized("reduce(x1y3 * x1y3,sum,y)");
- assert_mixed_optimized("reduce(x1y3 * x1y3f,sum,y)");
- assert_mixed_optimized("reduce(x1y3f * x1y3,sum,y)");
- assert_mixed_optimized("reduce(x1y3f * x1y3f,sum,y)");
+ assert_mixed_optimized("reduce(x1y3 * x1y3_f,sum,y)");
+ assert_mixed_optimized("reduce(x1y3_f * x1y3,sum,y)");
+ assert_mixed_optimized("reduce(x1y3_f * x1y3_f,sum,y)");
assert_mixed_optimized("reduce(x1y3 * mix_y3zm,sum,y)");
- assert_mixed_optimized("reduce(x1y3f * mix_y3zm,sum,y)");
+ assert_mixed_optimized("reduce(x1y3_f * mix_y3zm,sum,y)");
assert_mixed_optimized("reduce(mix_x3zm * x3,sum,x)");
- assert_mixed_optimized("reduce(mix_x3zm * x3f,sum,x)");
+ assert_mixed_optimized("reduce(mix_x3zm * x3_f,sum,x)");
assert_mixed_optimized("reduce(mix_x3zm * x3y1,sum,x)");
- assert_mixed_optimized("reduce(mix_x3zm * x3y1f,sum,x)");
+ assert_mixed_optimized("reduce(mix_x3zm * x3y1_f,sum,x)");
assert_mixed_optimized("reduce(mix_y3zm * y3,sum,y)");
- assert_mixed_optimized("reduce(mix_y3zm * y3f,sum,y)");
+ assert_mixed_optimized("reduce(mix_y3zm * y3_f,sum,y)");
assert_mixed_optimized("reduce(mix_y3zm * x1y3,sum,y)");
- assert_mixed_optimized("reduce(mix_y3zm * x1y3f,sum,y)");
+ assert_mixed_optimized("reduce(mix_y3zm * x1y3_f,sum,y)");
}
TEST(MixedInnerProduct, should_not_trigger_optimizer_for_other_cases) {
diff --git a/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp b/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp
index 3caebea7298..3a7d1368f03 100644
--- a/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp
+++ b/eval/src/tests/instruction/mixed_map_function/mixed_map_function_test.cpp
@@ -3,7 +3,7 @@
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/instruction/mixed_map_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/gtest/gtest.h>
using namespace vespalib;
@@ -15,13 +15,11 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add("a", spec(1.5))
- .add("b", spec(2.5))
- .add("sparse", spec({x({"a"})}, N()))
- .add("mixed", spec({x({"a"}),y(5)}, N()))
- .add_mutable("@sparse", spec({x({"a"})}, N()))
- .add_mutable("@mixed", spec({x({"a"}),y(5)}, N()))
- .add_matrix("x", 5, "y", 3);
+ .add("a", GenSpec().seq_bias(1.5).gen())
+ .add("b", GenSpec().seq_bias(2.5).gen())
+ .add_variants("sparse", GenSpec().map("x", {"a"}))
+ .add_variants("mixed", GenSpec().map("x", {"a"}).idx("y", 5))
+ .add_variants("x5y3", GenSpec().idx("x", 5).idx("y", 3));
}
EvalFixture::ParamRepo param_repo = make_params();
@@ -53,12 +51,12 @@ void verify_not_optimized(const vespalib::string &expr) {
TEST(MapTest, dense_map_is_optimized) {
verify_optimized("map(x5y3,f(x)(x+10))", false);
- verify_optimized("map(x5y3f,f(x)(x+10))", false);
+ verify_optimized("map(x5y3_f,f(x)(x+10))", false);
}
TEST(MapTest, simple_dense_map_can_be_inplace) {
verify_optimized("map(@x5y3,f(x)(x+10))", true);
- verify_optimized("map(@x5y3f,f(x)(x+10))", true);
+ verify_optimized("map(@x5y3_f,f(x)(x+10))", true);
}
TEST(MapTest, scalar_map_is_not_optimized) {
diff --git a/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp b/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp
index 9c891adf179..105ae22e06e 100644
--- a/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp
+++ b/eval/src/tests/instruction/mixed_simple_join_function/mixed_simple_join_function_test.cpp
@@ -4,7 +4,7 @@
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/instruction/mixed_simple_join_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -45,24 +45,24 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add("a", spec(1.5))
- .add("b", spec(2.5))
- .add("sparse", spec({x({"a", "b", "c"})}, N()))
- .add("mixed", spec({x({"a", "b", "c"}),y(5),z(3)}, N()))
- .add("empty_mixed", spec({x({}),y(5),z(3)}, N()))
- .add_mutable("@mixed", spec({x({"a", "b", "c"}),y(5),z(3)}, N()))
- .add_cube("a", 1, "b", 1, "c", 1)
- .add_cube("x", 1, "y", 1, "z", 1)
- .add_cube("x", 3, "y", 5, "z", 3)
- .add_vector("z", 3)
- .add_dense({{"c", 5}, {"d", 1}})
- .add_dense({{"b", 1}, {"c", 5}})
- .add_matrix("x", 3, "y", 5, [](size_t idx) noexcept { return double((idx * 2) + 3); })
- .add_matrix("x", 3, "y", 5, [](size_t idx) noexcept { return double((idx * 3) + 2); })
- .add_vector("y", 5, [](size_t idx) noexcept { return double((idx * 2) + 3); })
- .add_vector("y", 5, [](size_t idx) noexcept { return double((idx * 3) + 2); })
- .add_matrix("y", 5, "z", 3, [](size_t idx) noexcept { return double((idx * 2) + 3); })
- .add_matrix("y", 5, "z", 3, [](size_t idx) noexcept { return double((idx * 3) + 2); });
+ .add("a", GenSpec().seq_bias(1.5).gen())
+ .add("b", GenSpec().seq_bias(2.5).gen())
+ .add("sparse", GenSpec().map("x", {"a", "b", "c"}).gen())
+ .add("mixed", GenSpec().map("x", {"a", "b", "c"}).idx("y", 5).idx("z", 3).gen())
+ .add("empty_mixed", GenSpec().map("x", {}).idx("y", 5).idx("z", 3).gen())
+ .add_mutable("@mixed", GenSpec().map("x", {"a", "b", "c"}).idx("y", 5).idx("z", 3).gen())
+ .add_variants("a1b1c1", GenSpec().idx("a", 1).idx("b", 1).idx("c", 1))
+ .add_variants("x1y1z1", GenSpec().idx("x", 1).idx("y", 1).idx("z", 1))
+ .add_variants("x3y5z3", GenSpec().idx("x", 3).idx("y", 5).idx("z", 3))
+ .add_variants("z3", GenSpec().idx("z", 3))
+ .add_variants("c5d1", GenSpec().idx("c", 5).idx("d", 1))
+ .add_variants("b1c5", GenSpec().idx("b", 1).idx("c", 5))
+ .add_variants("x3y5", GenSpec().idx("x", 3).idx("y", 5).seq([](size_t idx) noexcept { return double((idx * 2) + 3); }))
+ .add_variants("x3y5$2", GenSpec().idx("x", 3).idx("y", 5).seq([](size_t idx) noexcept { return double((idx * 3) + 2); }))
+ .add_variants("y5", GenSpec().idx("y", 5).seq([](size_t idx) noexcept { return double((idx * 2) + 3); }))
+ .add_variants("y5$2", GenSpec().idx("y", 5).seq([](size_t idx) noexcept { return double((idx * 3) + 2); }))
+ .add_variants("y5z3", GenSpec().idx("y", 5).idx("z", 3).seq([](size_t idx) noexcept { return double((idx * 2) + 3); }))
+ .add_variants("y5z3$2", GenSpec().idx("y", 5).idx("z", 3).seq([](size_t idx) noexcept { return double((idx * 3) + 2); }));
}
EvalFixture::ParamRepo param_repo = make_params();
@@ -149,12 +149,12 @@ vespalib::string adjust_param(const vespalib::string &str, bool float_cells, boo
if (mut_cells) {
result = "@" + result;
}
- if (float_cells) {
- result += "f";
- }
if (is_rhs) {
result += "$2";
}
+ if (float_cells) {
+ result += "_f";
+ }
return result;
}
diff --git a/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp b/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp
index b4bf9ec5ef6..fe32a59bb78 100644
--- a/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp
+++ b/eval/src/tests/instruction/pow_as_map_optimizer/pow_as_map_optimizer_test.cpp
@@ -3,7 +3,7 @@
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/gtest/gtest.h>
using namespace vespalib::eval::operation;
@@ -16,11 +16,11 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add("a", spec(1.5))
- .add("b", spec(2.5))
- .add("sparse", spec({x({"a","b"})}, N()))
- .add("mixed", spec({x({"a"}),y(5)}, N()))
- .add_matrix("x", 5, "y", 3);
+ .add("a", GenSpec().seq_bias(1.5).gen())
+ .add("b", GenSpec().seq_bias(2.5).gen())
+ .add("sparse", GenSpec().map("x", {"a","b"}).gen())
+ .add("mixed", GenSpec().map("x", {"a"}).idx("y", 5).gen())
+ .add_variants("x5y3", GenSpec().idx("x", 5).idx("y", 3));
}
EvalFixture::ParamRepo param_repo = make_params();
@@ -55,9 +55,9 @@ TEST(PowAsMapTest, squared_dense_tensor_is_optimized) {
verify_optimized("pow(x5y3,2.0)", Square::f);
verify_optimized("join(x5y3,2.0,f(x,y)(x^y))", Square::f);
verify_optimized("join(x5y3,2.0,f(x,y)(pow(x,y)))", Square::f);
- verify_optimized("join(x5y3f,2.0,f(x,y)(pow(x,y)))", Square::f);
+ verify_optimized("join(x5y3_f,2.0,f(x,y)(pow(x,y)))", Square::f);
verify_optimized("join(@x5y3,2.0,f(x,y)(pow(x,y)))", Square::f, true);
- verify_optimized("join(@x5y3f,2.0,f(x,y)(pow(x,y)))", Square::f, true);
+ verify_optimized("join(@x5y3_f,2.0,f(x,y)(pow(x,y)))", Square::f, true);
}
TEST(PowAsMapTest, cubed_dense_tensor_is_optimized) {
@@ -65,9 +65,9 @@ TEST(PowAsMapTest, cubed_dense_tensor_is_optimized) {
verify_optimized("pow(x5y3,3.0)", Cube::f);
verify_optimized("join(x5y3,3.0,f(x,y)(x^y))", Cube::f);
verify_optimized("join(x5y3,3.0,f(x,y)(pow(x,y)))", Cube::f);
- verify_optimized("join(x5y3f,3.0,f(x,y)(pow(x,y)))", Cube::f);
+ verify_optimized("join(x5y3_f,3.0,f(x,y)(pow(x,y)))", Cube::f);
verify_optimized("join(@x5y3,3.0,f(x,y)(pow(x,y)))", Cube::f, true);
- verify_optimized("join(@x5y3f,3.0,f(x,y)(pow(x,y)))", Cube::f, true);
+ verify_optimized("join(@x5y3_f,3.0,f(x,y)(pow(x,y)))", Cube::f, true);
}
TEST(PowAsMapTest, hypercubed_dense_tensor_is_not_optimized) {
diff --git a/eval/src/tests/instruction/remove_trivial_dimension_optimizer/remove_trivial_dimension_optimizer_test.cpp b/eval/src/tests/instruction/remove_trivial_dimension_optimizer/remove_trivial_dimension_optimizer_test.cpp
index 4de7e85074d..794725a8257 100644
--- a/eval/src/tests/instruction/remove_trivial_dimension_optimizer/remove_trivial_dimension_optimizer_test.cpp
+++ b/eval/src/tests/instruction/remove_trivial_dimension_optimizer/remove_trivial_dimension_optimizer_test.cpp
@@ -4,7 +4,7 @@
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/instruction/replace_type_function.h>
#include <vespa/eval/instruction/fast_rename_optimizer.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -19,10 +19,10 @@ const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
EvalFixture::ParamRepo make_params() {
return EvalFixture::ParamRepo()
- .add("x1y5z1", spec({x(1),y(5),z(1)}, N()))
- .add("x1y5z1f", spec(float_cells({x(1),y(5),z(1)}), N()))
- .add("x1y1z1", spec({x(1),y(1),z(1)}, N()))
- .add("x1y5z_m", spec({x(1),y(5),z({"a"})}, N()));
+ .add("x1y5z1", GenSpec().idx("x", 1).idx("y", 5).idx("z", 1).gen())
+ .add("x1y5z1f", GenSpec().idx("x", 1).idx("y", 5).idx("z", 1).cells_float().gen())
+ .add("x1y1z1", GenSpec().idx("x", 1).idx("y", 1).idx("z", 1).gen())
+ .add("x1y5z_m", GenSpec().idx("x", 1).idx("y", 5).map("z", {"a"}).gen());
}
EvalFixture::ParamRepo param_repo = make_params();
diff --git a/eval/src/tests/instruction/sparse_dot_product_function/CMakeLists.txt b/eval/src/tests/instruction/sparse_dot_product_function/CMakeLists.txt
new file mode 100644
index 00000000000..076f1d79796
--- /dev/null
+++ b/eval/src/tests/instruction/sparse_dot_product_function/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_sparse_dot_product_function_test_app TEST
+ SOURCES
+ sparse_dot_product_function_test.cpp
+ DEPENDS
+ vespaeval
+ GTest::GTest
+)
+vespa_add_test(NAME eval_sparse_dot_product_function_test_app COMMAND eval_sparse_dot_product_function_test_app)
diff --git a/eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp b/eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp
new file mode 100644
index 00000000000..65eab2778aa
--- /dev/null
+++ b/eval/src/tests/instruction/sparse_dot_product_function/sparse_dot_product_function_test.cpp
@@ -0,0 +1,85 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/eval/eval/fast_value.h>
+#include <vespa/eval/eval/simple_value.h>
+#include <vespa/eval/instruction/sparse_dot_product_function.h>
+#include <vespa/eval/eval/test/eval_fixture.h>
+#include <vespa/eval/eval/test/gen_spec.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using namespace vespalib::eval;
+using namespace vespalib::eval::test;
+
+const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
+const ValueBuilderFactory &test_factory = SimpleValueBuilderFactory::get();
+
+//-----------------------------------------------------------------------------
+
+EvalFixture::ParamRepo make_params() {
+ return EvalFixture::ParamRepo()
+ .add("v1_x", GenSpec().map("x", 32, 1).seq_bias(3.0).gen())
+ .add("v1_x_f", GenSpec().map("x", 32, 1).seq_bias(3.0).cells_float().gen())
+ .add("v2_x", GenSpec().map("x", 16, 2).seq_bias(7.0).gen())
+ .add("v2_x_f", GenSpec().map("x", 16, 2).seq_bias(7.0).cells_float().gen())
+ .add("v3_y", GenSpec().map("y", 10, 1).gen())
+ .add("v4_xd", GenSpec().idx("x", 10).gen())
+ .add("m1_xy", GenSpec().map("x", 32, 1).map("y", 16, 2).seq_bias(3.0).gen())
+ .add("m2_xy", GenSpec().map("x", 16, 2).map("y", 32, 1).seq_bias(7.0).gen())
+ .add("m3_xym", GenSpec().map("x", 8, 1).idx("y", 5).gen());
+}
+EvalFixture::ParamRepo param_repo = make_params();
+
+void assert_optimized(const vespalib::string &expr) {
+ EvalFixture fast_fixture(prod_factory, expr, param_repo, true);
+ EvalFixture test_fixture(test_factory, expr, param_repo, true);
+ EvalFixture slow_fixture(prod_factory, expr, param_repo, false);
+ EXPECT_EQ(fast_fixture.result(), EvalFixture::ref(expr, param_repo));
+ EXPECT_EQ(test_fixture.result(), EvalFixture::ref(expr, param_repo));
+ EXPECT_EQ(slow_fixture.result(), EvalFixture::ref(expr, param_repo));
+ EXPECT_EQ(fast_fixture.find_all<SparseDotProductFunction>().size(), 1u);
+ EXPECT_EQ(test_fixture.find_all<SparseDotProductFunction>().size(), 1u);
+ EXPECT_EQ(slow_fixture.find_all<SparseDotProductFunction>().size(), 0u);
+}
+
+void assert_not_optimized(const vespalib::string &expr) {
+ EvalFixture fast_fixture(prod_factory, expr, param_repo, true);
+ EXPECT_EQ(fast_fixture.result(), EvalFixture::ref(expr, param_repo));
+ EXPECT_EQ(fast_fixture.find_all<SparseDotProductFunction>().size(), 0u);
+}
+
+//-----------------------------------------------------------------------------
+
+TEST(SparseDotProduct, expression_can_be_optimized)
+{
+ assert_optimized("reduce(v1_x*v2_x,sum,x)");
+ assert_optimized("reduce(v2_x*v1_x,sum)");
+ assert_optimized("reduce(v1_x*v2_x_f,sum)");
+ assert_optimized("reduce(v1_x_f*v2_x,sum)");
+ assert_optimized("reduce(v1_x_f*v2_x_f,sum)");
+}
+
+TEST(SparseDotProduct, multi_dimensional_expression_can_be_optimized)
+{
+ assert_optimized("reduce(m1_xy*m2_xy,sum,x,y)");
+ assert_optimized("reduce(m1_xy*m2_xy,sum)");
+}
+
+TEST(SparseDotProduct, embedded_dot_product_is_not_optimized)
+{
+ assert_not_optimized("reduce(m1_xy*v1_x,sum,x)");
+ assert_not_optimized("reduce(v1_x*m1_xy,sum,x)");
+}
+
+TEST(SparseDotProduct, similar_expressions_are_not_optimized)
+{
+ assert_not_optimized("reduce(m1_xy*v1_x,sum)");
+ assert_not_optimized("reduce(v1_x*v3_y,sum)");
+ assert_not_optimized("reduce(v2_x*v1_x,max)");
+ assert_not_optimized("reduce(v2_x+v1_x,sum)");
+ assert_not_optimized("reduce(v4_xd*v4_xd,sum)");
+ assert_not_optimized("reduce(m3_xym*m3_xym,sum)");
+}
+
+//-----------------------------------------------------------------------------
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp b/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp
index 4b89f30d879..1013c98b424 100644
--- a/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp
+++ b/eval/src/tests/instruction/sum_max_dot_product_function/sum_max_dot_product_function_test.cpp
@@ -3,7 +3,7 @@
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
-#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/instruction/sum_max_dot_product_function.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -13,12 +13,6 @@ using namespace vespalib::eval::test;
const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
-struct MyVecSeq : Sequence {
- double bias;
- double operator[](size_t i) const override { return (i + bias); }
- MyVecSeq(double cellBias) : bias(cellBias) {}
-};
-
//-----------------------------------------------------------------------------
vespalib::string main_expr = "reduce(reduce(reduce(a*b,sum,z),max,y),sum,x)";
@@ -34,7 +28,7 @@ void assert_optimized(const TensorSpec &a, const TensorSpec &b, size_t dp_size)
auto info = fast_fixture.find_all<SumMaxDotProductFunction>();
ASSERT_EQ(info.size(), 1u);
EXPECT_TRUE(info[0]->result_is_mutable());
- EXPECT_EQUAL(info[0]->dp_size(), dp_size);
+ EXPECT_EQ(info[0]->dp_size(), dp_size);
}
void assert_not_optimized(const TensorSpec &a, const TensorSpec &b, const vespalib::string &expr = main_expr) {
@@ -51,10 +45,23 @@ void assert_not_optimized(const TensorSpec &a, const TensorSpec &b, const vespal
//-----------------------------------------------------------------------------
-auto query = spec(float_cells({x({"0", "1", "2"}),z(5)}), MyVecSeq(0.5));
-auto document = spec(float_cells({y({"0", "1", "2", "3", "4", "5"}),z(5)}), MyVecSeq(2.5));
-auto empty_query = spec(float_cells({x({}),z(5)}), MyVecSeq(0.5));
-auto empty_document = spec(float_cells({y({}),z(5)}), MyVecSeq(2.5));
+GenSpec QueGen(size_t x_size, size_t z_size) { return GenSpec().cells_float().map("x", x_size).idx("z", z_size).seq_bias(0.5); }
+
+GenSpec DocGen(size_t y_size, size_t z_size) { return GenSpec().cells_float().map("y", y_size).idx("z", z_size).seq_bias(2.5); }
+
+GenSpec Que() { return QueGen(3, 5); }
+GenSpec Doc() { return DocGen(6, 5); }
+
+GenSpec QueEmptyX() { return QueGen(0, 5); }
+GenSpec DocEmptyX() { return DocGen(0, 5); }
+
+GenSpec QueTrivialZ() { return QueGen(3, 1); }
+GenSpec DocTrivialZ() { return DocGen(6, 1); }
+
+auto query = Que().gen();
+auto document = Doc().gen();
+auto empty_query = QueEmptyX().gen();
+auto empty_document = DocEmptyX().gen();
TEST(SumMaxDotProduct, expressions_can_be_optimized)
{
@@ -66,24 +73,24 @@ TEST(SumMaxDotProduct, expressions_can_be_optimized)
}
TEST(SumMaxDotProduct, double_cells_are_not_optimized) {
- auto double_query = spec({x({"0", "1", "2"}),z(5)}, MyVecSeq(0.5));
- auto double_document = spec({y({"0", "1", "2", "3", "4", "5"}),z(5)}, MyVecSeq(2.5));
+ auto double_query = Que().cells_double().gen();
+ auto double_document = Doc().cells_double().gen();
assert_not_optimized(query, double_document);
assert_not_optimized(double_query, document);
assert_not_optimized(double_query, double_document);
}
TEST(SumMaxDotProduct, trivial_dot_product_is_not_optimized) {
- auto trivial_query = spec(float_cells({x({"0", "1", "2"}),z(1)}), MyVecSeq(0.5));
- auto trivial_document = spec(float_cells({y({"0", "1", "2", "3", "4", "5"}),z(1)}), MyVecSeq(2.5));
+ auto trivial_query = QueTrivialZ().gen();
+ auto trivial_document = DocTrivialZ().gen();
assert_not_optimized(trivial_query, trivial_document);
}
TEST(SumMaxDotProduct, additional_dimensions_are_not_optimized) {
- auto extra_sparse_query = spec(float_cells({Domain("a", {"0"}),x({"0", "1", "2"}),z(5)}), MyVecSeq(0.5));
- auto extra_dense_query = spec(float_cells({Domain("a", 1),x({"0", "1", "2"}),z(5)}), MyVecSeq(0.5));
- auto extra_sparse_document = spec(float_cells({Domain("a", {"0"}),y({"0", "1", "2", "3", "4", "5"}),z(5)}), MyVecSeq(2.5));
- auto extra_dense_document = spec(float_cells({Domain("a", 1),y({"0", "1", "2", "3", "4", "5"}),z(5)}), MyVecSeq(2.5));
+ auto extra_sparse_query = Que().map("a", 1).gen();
+ auto extra_dense_query = Que().idx("a", 1).gen();
+ auto extra_sparse_document = Doc().map("a", 1).gen();
+ auto extra_dense_document = Doc().idx("a", 1).gen();
vespalib::string extra_sum_expr = "reduce(reduce(reduce(a*b,sum,z),max,y),sum,a,x)";
vespalib::string extra_max_expr = "reduce(reduce(reduce(a*b,sum,z),max,a,y),sum,x)";
assert_not_optimized(extra_sparse_query, document);
@@ -97,8 +104,8 @@ TEST(SumMaxDotProduct, additional_dimensions_are_not_optimized) {
}
TEST(SumMaxDotProduct, more_dense_variants_are_not_optimized) {
- auto dense_query = spec(float_cells({x(3),z(5)}), MyVecSeq(0.5));
- auto dense_document = spec(float_cells({y(5),z(5)}), MyVecSeq(2.5));
+ auto dense_query = GenSpec().cells_float().idx("x", 3).idx("z", 5).seq_bias(0.5).gen();
+ auto dense_document = GenSpec().cells_float().idx("y", 5).idx("z", 5).seq_bias(2.5).gen();
assert_not_optimized(dense_query, document);
assert_not_optimized(query, dense_document);
assert_not_optimized(dense_query, dense_document);
diff --git a/eval/src/tests/streamed/value/streamed_value_test.cpp b/eval/src/tests/streamed/value/streamed_value_test.cpp
index 2f91c3b9390..7aaa8cdebbc 100644
--- a/eval/src/tests/streamed/value/streamed_value_test.cpp
+++ b/eval/src/tests/streamed/value/streamed_value_test.cpp
@@ -5,7 +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/tensor_model.hpp>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/gtest/gtest.h>
@@ -23,45 +23,36 @@ using Handle = SharedStringRepo::Handle;
vespalib::string as_str(string_id label) { return Handle::string_from_id(label); }
-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"})})
+GenSpec G() { return GenSpec(); }
+
+const std::vector<GenSpec> layouts = {
+ G(),
+ G().idx("x", 3),
+ G().idx("x", 3).idx("y", 5),
+ G().idx("x", 3).idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5).map("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"})})
+const std::vector<GenSpec> join_layouts = {
+ G(), G(),
+ G().idx("x", 5), G().idx("x", 5),
+ G().idx("x", 5), G().idx("y", 5),
+ G().idx("x", 5), G().idx("x", 5).idx("y", 5),
+ G().idx("y", 3), G().idx("x", 2).idx("z", 3),
+ G().idx("x", 3).idx("y", 5), G().idx("y", 5).idx("z", 7),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b"}),
+ G().map("x", {"a","b","c"}), G().map("y", {"foo","bar","baz"}),
+ G().map("x", {"a","b","c"}), G().map("x", {"a","b","c"}).map("y", {"foo","bar","baz"}),
+ G().map("x", {"a","b"}).map("y", {"foo","bar","baz"}), G().map("x", {"a","b","c"}).map("y", {"foo","bar"}),
+ G().map("x", {"a","b"}).map("y", {"foo","bar","baz"}), G().map("y", {"foo","bar"}).map("z", {"i","j","k","l"}),
+ G().idx("x", 3).map("y", {"foo", "bar"}), G().map("y", {"foo","bar"}).idx("z", 7),
+ G().map("x", {"a","b","c"}).idx("y", 5), G().idx("y", 5).map("z", {"i","j","k","l"})
+
};
TensorSpec streamed_value_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
@@ -76,20 +67,26 @@ TensorSpec streamed_value_join(const TensorSpec &a, const TensorSpec &b, join_fu
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);
+ for (TensorSpec expect : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ std::unique_ptr<Value> value = value_from_spec(expect, StreamedValueBuilderFactory::get());
+ TensorSpec actual = spec_from_value(*value);
+ EXPECT_EQ(actual, expect);
+ }
}
}
TEST(StreamedValueTest, streamed_values_can_be_copied) {
for (const auto &layout: layouts) {
- TensorSpec expect = spec(layout, N());
- std::unique_ptr<Value> value = value_from_spec(expect, StreamedValueBuilderFactory::get());
- std::unique_ptr<Value> copy = StreamedValueBuilderFactory::get().copy(*value);
- TensorSpec actual = spec_from_value(*copy);
- EXPECT_EQ(actual, expect);
+ for (TensorSpec expect : { layout.cpy().cells_float().gen(),
+ layout.cpy().cells_double().gen() })
+ {
+ std::unique_ptr<Value> value = value_from_spec(expect, StreamedValueBuilderFactory::get());
+ std::unique_ptr<Value> copy = StreamedValueBuilderFactory::get().copy(*value);
+ TensorSpec actual = spec_from_value(*copy);
+ EXPECT_EQ(actual, expect);
+ }
}
}
@@ -126,16 +123,26 @@ TEST(StreamedValueTest, streamed_value_can_be_built_and_inspected) {
EXPECT_EQ(result["bb"], 3);
}
+GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; };
+
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 = ReferenceOperations::join(lhs, rhs, fun);
- auto actual = streamed_value_join(lhs, rhs, fun);
- EXPECT_EQ(actual, expect);
+ const auto l = join_layouts[i].cpy().seq(N_16ths);
+ const auto r = join_layouts[i + 1].cpy().seq(N_16ths);
+ for (TensorSpec lhs : { l.cpy().cells_float().gen(),
+ l.cpy().cells_double().gen() })
+ {
+ for (TensorSpec rhs : { r.cpy().cells_float().gen(),
+ r.cpy().cells_double().gen() })
+ {
+ 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 = ReferenceOperations::join(lhs, rhs, fun);
+ auto actual = streamed_value_join(lhs, rhs, fun);
+ EXPECT_EQ(actual, expect);
+ }
+ }
}
}
}
diff --git a/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp b/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp
index 3345d7dc8ee..000794aca7d 100644
--- a/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp
+++ b/eval/src/tests/tensor/instruction_benchmark/instruction_benchmark.cpp
@@ -43,6 +43,7 @@
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/vespalib/data/smart_buffer.h>
+#include <vespa/eval/eval/test/gen_spec.h>
#include <optional>
#include <algorithm>
@@ -60,51 +61,7 @@ template <typename T> using CREF = std::reference_wrapper<const T>;
//-----------------------------------------------------------------------------
-struct D {
- vespalib::string name;
- bool mapped;
- size_t size;
- size_t stride;
- static D map(const vespalib::string &name_in, size_t size_in, size_t stride_in) { return D{name_in, true, size_in, stride_in}; }
- static D idx(const vespalib::string &name_in, size_t size_in) { return D{name_in, false, size_in, 1}; }
- operator ValueType::Dimension() const {
- if (mapped) {
- return ValueType::Dimension(name);
- } else {
- return ValueType::Dimension(name, size);
- }
- }
- TensorSpec::Label operator()(size_t idx) const {
- if (mapped) {
- // need plain number as string for dynamic sparse peek
- return TensorSpec::Label(fmt("%zu", idx));
- } else {
- return TensorSpec::Label(idx);
- }
- }
-};
-
-void add_cells(TensorSpec &spec, double &seq, TensorSpec::Address addr) {
- spec.add(addr, seq);
- seq += 1.0;
-}
-
-template <typename ...Ds> void add_cells(TensorSpec &spec, double &seq, TensorSpec::Address addr, const D &d, const Ds &...ds) {
- for (size_t i = 0, idx = 0; i < d.size; ++i, idx += d.stride) {
- addr.insert_or_assign(d.name, d(idx));
- add_cells(spec, seq, addr, ds...);
- }
-}
-
-template <typename ...Ds> TensorSpec make_spec(double seq, const Ds &...ds) {
- TensorSpec spec(ValueType::tensor_type({ds...}, CellType::FLOAT).to_spec());
- add_cells(spec, seq, TensorSpec::Address(), ds...);
- return spec;
-}
-
-TensorSpec make_vector(const D &d1, double seq) { return make_spec(seq, d1); }
-TensorSpec make_matrix(const D &d1, const D &d2, double seq) { return make_spec(seq, d1, d2); }
-TensorSpec make_cube(const D &d1, const D &d2, const D &d3, double seq) { return make_spec(seq, d1, d2, d3); }
+test::GenSpec GS(double bias) { return test::GenSpec().cells_float().seq_bias(bias); }
//-----------------------------------------------------------------------------
@@ -609,7 +566,7 @@ void benchmark_tensor_create(const vespalib::string &desc, const TensorSpec &pro
ASSERT_FALSE(proto_type.is_error());
std::vector<CREF<TensorSpec>> stack_spec;
for (const auto &cell: proto.cells()) {
- stack_spec.emplace_back(stash.create<TensorSpec>(make_spec(cell.second)));
+ stack_spec.emplace_back(stash.create<TensorSpec>(GS(cell.second).gen()));
}
std::vector<EvalOp::UP> list;
for (const Impl &impl: impl_list) {
@@ -645,7 +602,7 @@ void benchmark_tensor_peek(const vespalib::string &desc, const TensorSpec &lhs,
stack_spec.emplace_back(lhs);
if (peek_spec.is_dynamic) {
for (const auto &entry: peek_spec.spec) {
- stack_spec.emplace_back(stash.create<TensorSpec>(make_spec(double(entry.second))));
+ stack_spec.emplace_back(stash.create<TensorSpec>(GS(double(entry.second)).gen()));
}
}
std::vector<EvalOp::UP> list;
@@ -660,10 +617,10 @@ void benchmark_tensor_peek(const vespalib::string &desc, const TensorSpec &lhs,
//-----------------------------------------------------------------------------
TEST(MakeInputTest, print_some_test_input) {
- auto number = make_spec(5.0);
- auto sparse = make_vector(D::map("x", 5, 3), 1.0);
- auto dense = make_vector(D::idx("x", 5), 10.0);
- auto mixed = make_cube(D::map("x", 3, 7), D::idx("y", 2), D::idx("z", 2), 100.0);
+ auto number = GS(5.0).gen();
+ auto sparse = GS(1.0).map("x", 5, 3).gen();
+ auto dense = GS(10.0).idx("x", 5).gen();
+ auto mixed = GS(100.0).map("x", 3, 7).idx("y", 2).idx("z", 2).gen();
fprintf(stderr, "--------------------------------------------------------\n");
fprintf(stderr, "simple number: %s\n", number.to_string().c_str());
fprintf(stderr, "sparse vector: %s\n", sparse.to_string().c_str());
@@ -728,197 +685,197 @@ void benchmark_encode_decode(const vespalib::string &desc, const TensorSpec &pro
// relevant for the overall performance of the tensor implementation.
TEST(EncodeDecodeBench, encode_decode_dense) {
- auto proto = make_matrix(D::idx("a", 64), D::idx("b", 64), 1.0);
+ auto proto = GS(1.0).idx("a", 64).idx("b", 64).gen();
benchmark_encode_decode("dense tensor", proto);
}
TEST(EncodeDecodeBench, encode_decode_sparse) {
- auto proto = make_matrix(D::map("a", 64, 1), D::map("b", 64, 1), 1.0);
+ auto proto = GS(1.0).map("a", 64, 1).map("b", 64, 1).gen();
benchmark_encode_decode("sparse tensor", proto);
}
TEST(EncodeDecodeBench, encode_decode_mixed) {
- auto proto = make_matrix(D::map("a", 64, 1), D::idx("b", 64), 1.0);
+ auto proto = GS(1.0).map("a", 64, 1).idx("b", 64).gen();
benchmark_encode_decode("mixed tensor", proto);
}
//-----------------------------------------------------------------------------
TEST(DenseConcat, small_vectors) {
- auto lhs = make_vector(D::idx("x", 10), 1.0);
- auto rhs = make_vector(D::idx("x", 10), 2.0);
+ auto lhs = GS(1.0).idx("x", 10).gen();
+ auto rhs = GS(2.0).idx("x", 10).gen();
benchmark_concat("small dense vector append concat", lhs, rhs, "x");
}
TEST(DenseConcat, cross_vectors) {
- auto lhs = make_vector(D::idx("x", 10), 1.0);
- auto rhs = make_vector(D::idx("x", 10), 2.0);
+ auto lhs = GS(1.0).idx("x", 10).gen();
+ auto rhs = GS(2.0).idx("x", 10).gen();
benchmark_concat("small dense vector cross concat", lhs, rhs, "y");
}
TEST(DenseConcat, cube_and_vector) {
- auto lhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::idx("c", 16), 1.0);
- auto rhs = make_vector(D::idx("a", 16), 42.0);
+ auto lhs = GS(1.0).idx("a", 16).idx("b", 16).idx("c", 16).gen();
+ auto rhs = GS(42.0).idx("a", 16).gen();
benchmark_concat("cube vs vector concat", lhs, rhs, "a");
}
TEST(SparseConcat, small_vectors) {
- auto lhs = make_vector(D::map("x", 10, 1), 1.0);
- auto rhs = make_vector(D::map("x", 10, 2), 2.0);
+ auto lhs = GS(1.0).map("x", 10, 1).gen();
+ auto rhs = GS(2.0).map("x", 10, 2).gen();
benchmark_concat("small sparse concat", lhs, rhs, "y");
}
TEST(MixedConcat, mixed_vs_dense) {
- auto lhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::map("c", 16, 1), 1.0);
- auto rhs = make_matrix(D::idx("a", 16), D::idx("b", 16), 2.0);
+ auto lhs = GS(1.0).idx("a", 16).idx("b", 16).map("c", 16, 1).gen();
+ auto rhs = GS(2.0).idx("a", 16).idx("b", 16).gen();
benchmark_concat("mixed dense concat a", lhs, rhs, "a");
}
TEST(MixedConcat, large_mixed_a) {
- auto lhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::map("c", 16, 1), 1.0);
- auto rhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::map("c", 16, 2), 2.0);
+ auto lhs = GS(1.0).idx("a", 16).idx("b", 16).map("c", 16, 1).gen();
+ auto rhs = GS(2.0).idx("a", 16).idx("b", 16).map("c", 16, 2).gen();
benchmark_concat("mixed append concat a", lhs, rhs, "a");
}
TEST(MixedConcat, large_mixed_b) {
- auto lhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::map("c", 16, 1), 1.0);
- auto rhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::map("c", 16, 2), 2.0);
+ auto lhs = GS(1.0).idx("a", 16).idx("b", 16).map("c", 16, 1).gen();
+ auto rhs = GS(2.0).idx("a", 16).idx("b", 16).map("c", 16, 2).gen();
benchmark_concat("mixed append concat b", lhs, rhs, "b");
}
//-----------------------------------------------------------------------------
TEST(NumberJoin, plain_op2) {
- auto lhs = make_spec(2.0);
- auto rhs = make_spec(3.0);
+ auto lhs = GS(2.0).gen();
+ auto rhs = GS(3.0).gen();
benchmark_join("simple numbers multiply", lhs, rhs, operation::Mul::f);
}
//-----------------------------------------------------------------------------
TEST(DenseJoin, small_vectors) {
- auto lhs = make_vector(D::idx("x", 10), 1.0);
- auto rhs = make_vector(D::idx("x", 10), 2.0);
+ auto lhs = GS(1.0).idx("x", 10).gen();
+ auto rhs = GS(2.0).idx("x", 10).gen();
benchmark_join("small dense vector multiply", lhs, rhs, operation::Mul::f);
}
TEST(DenseJoin, full_overlap) {
- auto lhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::idx("c", 16), 1.0);
- auto rhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::idx("c", 16), 2.0);
+ auto lhs = GS(1.0).idx("a", 16).idx("b", 16).idx("c", 16).gen();
+ auto rhs = GS(2.0).idx("a", 16).idx("b", 16).idx("c", 16).gen();
benchmark_join("dense full overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(DenseJoin, partial_overlap) {
- auto lhs = make_cube(D::idx("a", 8), D::idx("c", 8), D::idx("d", 8), 1.0);
- auto rhs = make_cube(D::idx("b", 8), D::idx("c", 8), D::idx("d", 8), 2.0);
+ auto lhs = GS(1.0).idx("a", 8).idx("c", 8).idx("d", 8).gen();
+ auto rhs = GS(2.0).idx("b", 8).idx("c", 8).idx("d", 8).gen();
benchmark_join("dense partial overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(DenseJoin, subset_overlap) {
- auto lhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::idx("c", 16), 1.0);
- auto rhs_inner = make_matrix(D::idx("b", 16), D::idx("c", 16), 2.0);
- auto rhs_outer = make_matrix(D::idx("a", 16), D::idx("b", 16), 3.0);
+ auto lhs = GS(1.0).idx("a", 16).idx("b", 16).idx("c", 16).gen();
+ auto rhs_inner = GS(2.0).idx("b", 16).idx("c", 16).gen();
+ auto rhs_outer = GS(3.0).idx("a", 16).idx("b", 16).gen();
benchmark_join("dense subset overlap inner multiply", lhs, rhs_inner, operation::Mul::f);
benchmark_join("dense subset overlap outer multiply", lhs, rhs_outer, operation::Mul::f);
}
TEST(DenseJoin, no_overlap) {
- auto lhs = make_cube(D::idx("a", 4), D::idx("e", 4), D::idx("f", 4), 1.0);
- auto rhs = make_cube(D::idx("b", 4), D::idx("c", 4), D::idx("d", 4), 2.0);
+ auto lhs = GS(1.0).idx("a", 4).idx("e", 4).idx("f", 4).gen();
+ auto rhs = GS(2.0).idx("b", 4).idx("c", 4).idx("d", 4).gen();
benchmark_join("dense no overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(DenseJoin, simple_expand) {
- auto lhs = make_cube(D::idx("a", 5), D::idx("b", 4), D::idx("c", 4), 1.0);
- auto rhs = make_cube(D::idx("d", 4), D::idx("e", 4), D::idx("f", 5), 2.0);
+ auto lhs = GS(1.0).idx("a", 5).idx("b", 4).idx("c", 4).gen();
+ auto rhs = GS(2.0).idx("d", 4).idx("e", 4).idx("f", 5).gen();
benchmark_join("dense simple expand multiply", lhs, rhs, operation::Mul::f);
}
TEST(DenseJoin, multiply_by_number) {
- auto lhs = make_spec(3.0);
- auto rhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::idx("c", 16), 2.0);
+ auto lhs = GS(3.0).gen();
+ auto rhs = GS(2.0).idx("a", 16).idx("b", 16).idx("c", 16).gen();
benchmark_join("dense cube multiply by number", lhs, rhs, operation::Mul::f);
}
//-----------------------------------------------------------------------------
TEST(SparseJoin, small_vectors) {
- auto lhs = make_vector(D::map("x", 10, 1), 1.0);
- auto rhs = make_vector(D::map("x", 10, 2), 2.0);
+ auto lhs = GS(1.0).map("x", 10, 1).gen();
+ auto rhs = GS(2.0).map("x", 10, 2).gen();
benchmark_join("small sparse vector multiply", lhs, rhs, operation::Mul::f);
}
TEST(SparseJoin, large_vectors) {
- auto lhs = make_vector(D::map("x", 1800, 1), 1.0);
- auto rhs = make_vector(D::map("x", 1000, 2), 2.0);
+ auto lhs = GS(1.0).map("x", 1800, 1).gen();
+ auto rhs = GS(2.0).map("x", 1000, 2).gen();
benchmark_join("large sparse vector multiply", lhs, rhs, operation::Mul::f);
}
TEST(SparseJoin, full_overlap) {
- auto lhs = make_cube(D::map("a", 16, 1), D::map("b", 16, 1), D::map("c", 16, 1), 1.0);
- auto rhs = make_cube(D::map("a", 16, 2), D::map("b", 16, 2), D::map("c", 16, 2), 2.0);
+ auto lhs = GS(1.0).map("a", 16, 1).map("b", 16, 1).map("c", 16, 1).gen();
+ auto rhs = GS(2.0).map("a", 16, 2).map("b", 16, 2).map("c", 16, 2).gen();
benchmark_join("sparse full overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(SparseJoin, full_overlap_big_vs_small) {
- auto lhs = make_cube(D::map("a", 16, 1), D::map("b", 16, 1), D::map("c", 16, 1), 1.0);
- auto rhs = make_cube(D::map("a", 2, 1), D::map("b", 2, 1), D::map("c", 2, 1), 2.0);
+ auto lhs = GS(1.0).map("a", 16, 1).map("b", 16, 1).map("c", 16, 1).gen();
+ auto rhs = GS(2.0).map("a", 2, 1).map("b", 2, 1).map("c", 2, 1).gen();
benchmark_join("sparse full overlap big vs small multiply", lhs, rhs, operation::Mul::f);
}
TEST(SparseJoin, partial_overlap) {
- auto lhs = make_cube(D::map("a", 8, 1), D::map("c", 8, 1), D::map("d", 8, 1), 1.0);
- auto rhs = make_cube(D::map("b", 8, 2), D::map("c", 8, 2), D::map("d", 8, 2), 2.0);
+ auto lhs = GS(1.0).map("a", 8, 1).map("c", 8, 1).map("d", 8, 1).gen();
+ auto rhs = GS(2.0).map("b", 8, 2).map("c", 8, 2).map("d", 8, 2).gen();
benchmark_join("sparse partial overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(SparseJoin, no_overlap) {
- auto lhs = make_cube(D::map("a", 4, 1), D::map("e", 4, 1), D::map("f", 4, 1), 1.0);
- auto rhs = make_cube(D::map("b", 4, 1), D::map("c", 4, 1), D::map("d", 4, 1), 2.0);
+ auto lhs = GS(1.0).map("a", 4, 1).map("e", 4, 1).map("f", 4, 1).gen();
+ auto rhs = GS(2.0).map("b", 4, 1).map("c", 4, 1).map("d", 4, 1).gen();
benchmark_join("sparse no overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(SparseJoin, multiply_by_number) {
- auto lhs = make_spec(3.0);
- auto rhs = make_cube(D::map("a", 16, 2), D::map("b", 16, 2), D::map("c", 16, 2), 2.0);
+ auto lhs = GS(3.0).gen();
+ auto rhs = GS(2.0).map("a", 16, 2).map("b", 16, 2).map("c", 16, 2).gen();
benchmark_join("sparse multiply by number", lhs, rhs, operation::Mul::f);
}
//-----------------------------------------------------------------------------
TEST(MixedJoin, full_overlap) {
- auto lhs = make_cube(D::map("a", 16, 1), D::map("b", 16, 1), D::idx("c", 16), 1.0);
- auto rhs = make_cube(D::map("a", 16, 2), D::map("b", 16, 2), D::idx("c", 16), 2.0);
+ auto lhs = GS(1.0).map("a", 16, 1).map("b", 16, 1).idx("c", 16).gen();
+ auto rhs = GS(2.0).map("a", 16, 2).map("b", 16, 2).idx("c", 16).gen();
benchmark_join("mixed full overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(MixedJoin, partial_sparse_overlap) {
- auto lhs = make_cube(D::map("a", 8, 1), D::map("c", 8, 1), D::idx("d", 8), 1.0);
- auto rhs = make_cube(D::map("b", 8, 2), D::map("c", 8, 2), D::idx("d", 8), 2.0);
+ auto lhs = GS(1.0).map("a", 8, 1).map("c", 8, 1).idx("d", 8).gen();
+ auto rhs = GS(2.0).map("b", 8, 2).map("c", 8, 2).idx("d", 8).gen();
benchmark_join("mixed partial sparse overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(MixedJoin, no_overlap) {
- auto lhs = make_cube(D::map("a", 4, 1), D::map("e", 4, 1), D::idx("f", 4), 1.0);
- auto rhs = make_cube(D::map("b", 4, 1), D::map("c", 4, 1), D::idx("d", 4), 2.0);
+ auto lhs = GS(1.0).map("a", 4, 1).map("e", 4, 1).idx("f", 4).gen();
+ auto rhs = GS(2.0).map("b", 4, 1).map("c", 4, 1).idx("d", 4).gen();
benchmark_join("mixed no overlap multiply", lhs, rhs, operation::Mul::f);
}
TEST(MixedJoin, multiply_by_number) {
- auto lhs = make_spec(3.0);
- auto rhs = make_cube(D::map("a", 16, 2), D::map("b", 16, 2), D::idx("c", 16), 2.0);
+ auto lhs = GS(3.0).gen();
+ auto rhs = GS(2.0).map("a", 16, 2).map("b", 16, 2).idx("c", 16).gen();
benchmark_join("mixed multiply by number", lhs, rhs, operation::Mul::f);
}
//-----------------------------------------------------------------------------
TEST(ReduceBench, number_reduce) {
- auto lhs = make_spec(1.0);
+ auto lhs = GS(1.0).gen();
benchmark_reduce("number reduce", lhs, Aggr::SUM, {});
}
TEST(ReduceBench, dense_reduce) {
- auto lhs = make_cube(D::idx("a", 16), D::idx("b", 16), D::idx("c", 16), 1.0);
+ auto lhs = GS(1.0).idx("a", 16).idx("b", 16).idx("c", 16).gen();
benchmark_reduce("dense reduce inner", lhs, Aggr::SUM, {"c"});
benchmark_reduce("dense reduce middle", lhs, Aggr::SUM, {"b"});
benchmark_reduce("dense reduce outer", lhs, Aggr::SUM, {"a"});
@@ -929,7 +886,7 @@ TEST(ReduceBench, dense_reduce) {
}
TEST(ReduceBench, sparse_reduce) {
- auto lhs = make_cube(D::map("a", 16, 1), D::map("b", 16, 1), D::map("c", 16, 1), 1.0);
+ auto lhs = GS(1.0).map("a", 16, 1).map("b", 16, 1).map("c", 16, 1).gen();
benchmark_reduce("sparse reduce inner", lhs, Aggr::SUM, {"c"});
benchmark_reduce("sparse reduce middle", lhs, Aggr::SUM, {"b"});
benchmark_reduce("sparse reduce outer", lhs, Aggr::SUM, {"a"});
@@ -940,8 +897,8 @@ TEST(ReduceBench, sparse_reduce) {
}
TEST(ReduceBench, mixed_reduce) {
- auto lhs = make_spec(1.0, D::map("a", 4, 1), D::map("b", 4, 1), D::map("c", 4, 1),
- D::idx("d", 4), D::idx("e", 4), D::idx("f", 4));
+ auto lhs = GS(1.0).map("a", 4, 1).map("b", 4, 1).map("c", 4, 1)
+ .idx("d", 4).idx("e", 4).idx("f", 4).gen();
benchmark_reduce("mixed reduce middle dense", lhs, Aggr::SUM, {"e"});
benchmark_reduce("mixed reduce middle sparse", lhs, Aggr::SUM, {"b"});
benchmark_reduce("mixed reduce middle sparse/dense", lhs, Aggr::SUM, {"b", "e"});
@@ -953,87 +910,87 @@ TEST(ReduceBench, mixed_reduce) {
//-----------------------------------------------------------------------------
TEST(RenameBench, dense_rename) {
- auto lhs = make_matrix(D::idx("a", 64), D::idx("b", 64), 1.0);
+ auto lhs = GS(1.0).idx("a", 64).idx("b", 64).gen();
benchmark_rename("dense transpose", lhs, {"a", "b"}, {"b", "a"});
}
TEST(RenameBench, sparse_rename) {
- auto lhs = make_matrix(D::map("a", 64, 1), D::map("b", 64, 1), 1.0);
+ auto lhs = GS(1.0).map("a", 64, 1).map("b", 64, 1).gen();
benchmark_rename("sparse transpose", lhs, {"a", "b"}, {"b", "a"});
}
TEST(RenameBench, mixed_rename) {
- auto lhs = make_spec(1.0, D::map("a", 8, 1), D::map("b", 8, 1), D::idx("c", 8), D::idx("d", 8));
+ auto lhs = GS(1.0).map("a", 8, 1).map("b", 8, 1).idx("c", 8).idx("d", 8).gen();
benchmark_rename("mixed multi-transpose", lhs, {"a", "b", "c", "d"}, {"b", "a", "d", "c"});
}
//-----------------------------------------------------------------------------
TEST(MergeBench, dense_merge) {
- auto lhs = make_matrix(D::idx("a", 64), D::idx("b", 64), 1.0);
- auto rhs = make_matrix(D::idx("a", 64), D::idx("b", 64), 2.0);
+ auto lhs = GS(1.0).idx("a", 64).idx("b", 64).gen();
+ auto rhs = GS(2.0).idx("a", 64).idx("b", 64).gen();
benchmark_merge("dense merge", lhs, rhs, operation::Max::f);
}
TEST(MergeBench, sparse_merge_big_small) {
- auto lhs = make_matrix(D::map("a", 64, 1), D::map("b", 64, 1), 1.0);
- auto rhs = make_matrix(D::map("a", 8, 1), D::map("b", 8, 1), 2.0);
+ auto lhs = GS(1.0).map("a", 64, 1).map("b", 64, 1).gen();
+ auto rhs = GS(2.0).map("a", 8, 1).map("b", 8, 1).gen();
benchmark_merge("sparse merge big vs small", lhs, rhs, operation::Max::f);
}
TEST(MergeBench, sparse_merge_minimal_overlap) {
- auto lhs = make_matrix(D::map("a", 64, 11), D::map("b", 32, 11), 1.0);
- auto rhs = make_matrix(D::map("a", 32, 13), D::map("b", 64, 13), 2.0);
+ auto lhs = GS(1.0).map("a", 64, 11).map("b", 32, 11).gen();
+ auto rhs = GS(2.0).map("a", 32, 13).map("b", 64, 13).gen();
benchmark_merge("sparse merge minimal overlap", lhs, rhs, operation::Max::f);
}
TEST(MergeBench, mixed_merge) {
- auto lhs = make_matrix(D::map("a", 64, 1), D::idx("b", 64), 1.0);
- auto rhs = make_matrix(D::map("a", 64, 2), D::idx("b", 64), 2.0);
+ auto lhs = GS(1.0).map("a", 64, 1).idx("b", 64).gen();
+ auto rhs = GS(2.0).map("a", 64, 2).idx("b", 64).gen();
benchmark_merge("mixed merge", lhs, rhs, operation::Max::f);
}
//-----------------------------------------------------------------------------
TEST(MapBench, number_map) {
- auto lhs = make_spec(1.75);
+ auto lhs = GS(1.75).gen();
benchmark_map("number map", lhs, operation::Floor::f);
}
TEST(MapBench, dense_map) {
- auto lhs = make_matrix(D::idx("a", 64), D::idx("b", 64), 1.75);
+ auto lhs = GS(1.75).idx("a", 64).idx("b", 64).gen();
benchmark_map("dense map", lhs, operation::Floor::f);
}
TEST(MapBench, sparse_map_small) {
- auto lhs = make_matrix(D::map("a", 4, 1), D::map("b", 4, 1), 1.75);
+ auto lhs = GS(1.75).map("a", 4, 1).map("b", 4, 1).gen();
benchmark_map("sparse map small", lhs, operation::Floor::f);
}
TEST(MapBench, sparse_map_big) {
- auto lhs = make_matrix(D::map("a", 64, 1), D::map("b", 64, 1), 1.75);
+ auto lhs = GS(1.75).map("a", 64, 1).map("b", 64, 1).gen();
benchmark_map("sparse map big", lhs, operation::Floor::f);
}
TEST(MapBench, mixed_map) {
- auto lhs = make_matrix(D::map("a", 64, 1), D::idx("b", 64), 1.75);
+ auto lhs = GS(1.75).map("a", 64, 1).idx("b", 64).gen();
benchmark_map("mixed map", lhs, operation::Floor::f);
}
//-----------------------------------------------------------------------------
TEST(TensorCreateBench, create_dense) {
- auto proto = make_matrix(D::idx("a", 32), D::idx("b", 32), 1.0);
+ auto proto = GS(1.0).idx("a", 32).idx("b", 32).gen();
benchmark_tensor_create("dense tensor create", proto);
}
TEST(TensorCreateBench, create_sparse) {
- auto proto = make_matrix(D::map("a", 32, 1), D::map("b", 32, 1), 1.0);
+ auto proto = GS(1.0).map("a", 32, 1).map("b", 32, 1).gen();
benchmark_tensor_create("sparse tensor create", proto);
}
TEST(TensorCreateBench, create_mixed) {
- auto proto = make_matrix(D::map("a", 32, 1), D::idx("b", 32), 1.0);
+ auto proto = GS(1.0).map("a", 32, 1).idx("b", 32).gen();
benchmark_tensor_create("mixed tensor create", proto);
}
@@ -1041,7 +998,7 @@ TEST(TensorCreateBench, create_mixed) {
TEST(TensorLambdaBench, simple_lambda) {
auto type = ValueType::from_spec("tensor<float>(a[64],b[64])");
- auto p0 = make_spec(3.5);
+ auto p0 = GS(3.5).gen();
auto function = Function::parse({"a", "b", "p0"}, "(a*64+b)*p0");
ASSERT_FALSE(function->has_error());
benchmark_tensor_lambda("simple tensor lambda", type, p0, *function);
@@ -1049,7 +1006,7 @@ TEST(TensorLambdaBench, simple_lambda) {
TEST(TensorLambdaBench, complex_lambda) {
auto type = ValueType::from_spec("tensor<float>(a[64],b[64])");
- auto p0 = make_vector(D::idx("x", 3), 1.0);
+ auto p0 = GS(1.0).idx("x", 3).gen();
auto function = Function::parse({"a", "b", "p0"}, "(a*64+b)*reduce(p0,sum)");
ASSERT_FALSE(function->has_error());
benchmark_tensor_lambda("complex tensor lambda", type, p0, *function);
@@ -1058,7 +1015,7 @@ TEST(TensorLambdaBench, complex_lambda) {
//-----------------------------------------------------------------------------
TEST(TensorPeekBench, dense_peek) {
- auto lhs = make_matrix(D::idx("a", 64), D::idx("b", 64), 1.0);
+ auto lhs = GS(1.0).idx("a", 64).idx("b", 64).gen();
benchmark_tensor_peek("dense peek cell verbatim", lhs, verbatim_peek().add("a", 1).add("b", 2));
benchmark_tensor_peek("dense peek cell dynamic", lhs, dynamic_peek().add("a", 1).add("b", 2));
benchmark_tensor_peek("dense peek vector verbatim", lhs, verbatim_peek().add("a", 1));
@@ -1066,7 +1023,7 @@ TEST(TensorPeekBench, dense_peek) {
}
TEST(TensorPeekBench, sparse_peek) {
- auto lhs = make_matrix(D::map("a", 64, 1), D::map("b", 64, 1), 1.0);
+ auto lhs = GS(1.0).map("a", 64, 1).map("b", 64, 1).gen();
benchmark_tensor_peek("sparse peek cell verbatim", lhs, verbatim_peek().add("a", 1).add("b", 2));
benchmark_tensor_peek("sparse peek cell dynamic", lhs, dynamic_peek().add("a", 1).add("b", 2));
benchmark_tensor_peek("sparse peek vector verbatim", lhs, verbatim_peek().add("a", 1));
@@ -1074,7 +1031,7 @@ TEST(TensorPeekBench, sparse_peek) {
}
TEST(TensorPeekBench, mixed_peek) {
- auto lhs = make_spec(1.0, D::map("a", 8, 1), D::map("b", 8, 1), D::idx("c", 8), D::idx("d", 8));
+ auto lhs = GS(1.0).map("a", 8, 1).map("b", 8, 1).idx("c", 8).idx("d", 8).gen();
benchmark_tensor_peek("mixed peek cell verbatim", lhs, verbatim_peek().add("a", 1).add("b", 2).add("c", 3).add("d", 4));
benchmark_tensor_peek("mixed peek cell dynamic", lhs, dynamic_peek().add("a", 1).add("b", 2).add("c", 3).add("d", 4));
benchmark_tensor_peek("mixed peek dense verbatim", lhs, verbatim_peek().add("a", 1).add("b", 2));
diff --git a/eval/src/vespa/eval/eval/optimize_tensor_function.cpp b/eval/src/vespa/eval/eval/optimize_tensor_function.cpp
index 2e8c89f88fc..25612b8d5fd 100644
--- a/eval/src/vespa/eval/eval/optimize_tensor_function.cpp
+++ b/eval/src/vespa/eval/eval/optimize_tensor_function.cpp
@@ -5,6 +5,7 @@
#include "simple_value.h"
#include <vespa/eval/instruction/dense_dot_product_function.h>
+#include <vespa/eval/instruction/sparse_dot_product_function.h>
#include <vespa/eval/instruction/mixed_inner_product_function.h>
#include <vespa/eval/instruction/sum_max_dot_product_function.h>
#include <vespa/eval/instruction/dense_xw_product_function.h>
@@ -31,11 +32,7 @@ namespace vespalib::eval {
namespace {
-const TensorFunction &optimize_for_factory(const ValueBuilderFactory &factory, const TensorFunction &expr, Stash &stash) {
- if (&factory == &SimpleValueBuilderFactory::get()) {
- // never optimize simple value evaluation
- return expr;
- }
+const TensorFunction &optimize_for_factory(const ValueBuilderFactory &, const TensorFunction &expr, Stash &stash) {
using Child = TensorFunction::Child;
Child root(expr);
{
@@ -47,6 +44,7 @@ const TensorFunction &optimize_for_factory(const ValueBuilderFactory &factory, c
const Child &child = nodes.back().get();
child.set(SumMaxDotProductFunction::optimize(child.get(), stash));
child.set(DenseDotProductFunction::optimize(child.get(), stash));
+ child.set(SparseDotProductFunction::optimize(child.get(), stash));
child.set(DenseXWProductFunction::optimize(child.get(), stash));
child.set(DenseMatMulFunction::optimize(child.get(), stash));
child.set(DenseMultiMatMulFunction::optimize(child.get(), stash));
diff --git a/eval/src/vespa/eval/eval/test/CMakeLists.txt b/eval/src/vespa/eval/eval/test/CMakeLists.txt
index 2e9b50da5e6..e82b85d1890 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
+ gen_spec.cpp
reference_evaluation.cpp
reference_operations.cpp
tensor_conformance.cpp
diff --git a/eval/src/vespa/eval/eval/test/eval_fixture.cpp b/eval/src/vespa/eval/eval/test/eval_fixture.cpp
index 58d8905baf3..966954b9026 100644
--- a/eval/src/vespa/eval/eval/test/eval_fixture.cpp
+++ b/eval/src/vespa/eval/eval/test/eval_fixture.cpp
@@ -28,7 +28,10 @@ NodeTypes get_types(const Function &function, const ParamRepo &param_repo) {
std::vector<ValueType> param_types;
for (size_t i = 0; i < function.num_params(); ++i) {
auto pos = param_repo.map.find(function.param_name(i));
- ASSERT_TRUE(pos != param_repo.map.end());
+ if (pos == param_repo.map.end()) {
+ TEST_STATE(fmt("param name: '%s'", function.param_name(i).data()).c_str());
+ ASSERT_TRUE(pos != param_repo.map.end());
+ }
param_types.push_back(ValueType::from_spec(pos->second.value.type()));
ASSERT_TRUE(!param_types.back().is_error());
}
@@ -181,6 +184,23 @@ EvalFixture::ParamRepo::add_dense(const std::vector<std::pair<vespalib::string,
return *this;
}
+// produce 4 variants: float/double * mutable/const
+EvalFixture::ParamRepo &
+EvalFixture::ParamRepo::add_variants(const vespalib::string &name_base,
+ const GenSpec &spec)
+{
+ auto name_f = name_base + "_f";
+ auto name_m = "@" + name_base;
+ auto name_m_f = "@" + name_base + "_f";
+ auto dbl_ts = spec.cpy().cells_double().gen();
+ auto flt_ts = spec.cpy().cells_float().gen();
+ add(name_base, dbl_ts);
+ add(name_f, flt_ts);
+ add_mutable(name_m, dbl_ts);
+ add_mutable(name_m_f, flt_ts);
+ return *this;
+}
+
void
EvalFixture::detect_param_tampering(const ParamRepo &param_repo, bool allow_mutable) const
{
diff --git a/eval/src/vespa/eval/eval/test/eval_fixture.h b/eval/src/vespa/eval/eval/test/eval_fixture.h
index dc49cf7e4dc..44adaca3298 100644
--- a/eval/src/vespa/eval/eval/test/eval_fixture.h
+++ b/eval/src/vespa/eval/eval/test/eval_fixture.h
@@ -10,6 +10,7 @@
#include <vespa/vespalib/util/stash.h>
#include <set>
#include <functional>
+#include "gen_spec.h"
namespace vespalib::eval::test {
@@ -40,6 +41,10 @@ public:
ParamRepo &add_matrix(const char *d1, size_t s1, const char *d2, size_t s2, gen_fun_t gen = gen_N);
ParamRepo &add_cube(const char *d1, size_t s1, const char *d2, size_t s2, const char *d3, size_t s3, gen_fun_t gen = gen_N);
ParamRepo &add_dense(const std::vector<std::pair<vespalib::string, size_t> > &dims, gen_fun_t gen = gen_N);
+
+ // produce 4 variants: float/double * mutable/const
+ ParamRepo &add_variants(const vespalib::string &name_base,
+ const GenSpec &spec);
~ParamRepo() {}
};
diff --git a/eval/src/vespa/eval/eval/test/gen_spec.cpp b/eval/src/vespa/eval/eval/test/gen_spec.cpp
new file mode 100644
index 00000000000..c20e9005318
--- /dev/null
+++ b/eval/src/vespa/eval/eval/test/gen_spec.cpp
@@ -0,0 +1,63 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "gen_spec.h"
+#include <vespa/eval/eval/string_stuff.h>
+#include <vespa/vespalib/util/stringfmt.h>
+
+using vespalib::make_string_short::fmt;
+
+namespace vespalib::eval::test {
+
+DimSpec::~DimSpec() = default;
+
+std::vector<vespalib::string>
+DimSpec::make_dict(size_t size, size_t stride, const vespalib::string &prefix)
+{
+ std::vector<vespalib::string> dict;
+ for (size_t i = 0; i < size; ++i) {
+ dict.push_back(fmt("%s%zu", prefix.c_str(), i * stride));
+ }
+ return dict;
+}
+
+GenSpec::GenSpec(GenSpec &&other) = default;
+GenSpec::GenSpec(const GenSpec &other) = default;
+GenSpec &GenSpec::operator=(GenSpec &&other) = default;
+GenSpec &GenSpec::operator=(const GenSpec &other) = default;
+
+GenSpec::~GenSpec() = default;
+
+ValueType
+GenSpec::type() const
+{
+ std::vector<ValueType::Dimension> dim_types;
+ for (const auto &dim: _dims) {
+ dim_types.push_back(dim.type());
+ }
+ auto type = ValueType::tensor_type(dim_types, _cells);
+ assert(!type.is_error());
+ return type;
+}
+
+TensorSpec
+GenSpec::gen() const
+{
+ size_t idx = 0;
+ TensorSpec::Address addr;
+ TensorSpec result(type().to_spec());
+ std::function<void(size_t)> add_cells = [&](size_t dim_idx) {
+ if (dim_idx == _dims.size()) {
+ result.add(addr, _seq(idx++));
+ } else {
+ const auto &dim = _dims[dim_idx];
+ for (size_t i = 0; i < dim.size(); ++i) {
+ addr.insert_or_assign(dim.name(), dim.label(i));
+ add_cells(dim_idx + 1);
+ }
+ }
+ };
+ add_cells(0);
+ return result;
+}
+
+} // namespace
diff --git a/eval/src/vespa/eval/eval/test/gen_spec.h b/eval/src/vespa/eval/eval/test/gen_spec.h
new file mode 100644
index 00000000000..36bbd554125
--- /dev/null
+++ b/eval/src/vespa/eval/eval/test/gen_spec.h
@@ -0,0 +1,109 @@
+// 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/tensor_spec.h>
+#include <vespa/eval/eval/value_type.h>
+#include <functional>
+#include <cassert>
+
+namespace vespalib::eval::test {
+
+/**
+ * Type and labels for a single dimension of a TensorSpec to be
+ * generated. Dimensions are specified independent of each other for
+ * simplicity. All dense subspaces will be padded during conversion to
+ * actual values, which means that indexed dimensions are inherently
+ * independent already. Using different labels for the same mapped
+ * dimension for different tensors should enable us to exhibit
+ * sufficient levels of partial overlap.
+ **/
+class DimSpec
+{
+private:
+ vespalib::string _name;
+ size_t _size;
+ std::vector<vespalib::string> _dict;
+public:
+ DimSpec(const vespalib::string &name, size_t size) noexcept
+ : _name(name), _size(size), _dict()
+ {
+ assert(_size);
+ }
+ DimSpec(const vespalib::string &name, std::vector<vespalib::string> dict) noexcept
+ : _name(name), _size(), _dict(std::move(dict))
+ {
+ assert(!_size);
+ }
+ ~DimSpec();
+ static std::vector<vespalib::string> make_dict(size_t size, size_t stride, const vespalib::string &prefix);
+ ValueType::Dimension type() const {
+ return _size ? ValueType::Dimension{_name, uint32_t(_size)} : ValueType::Dimension{_name};
+ }
+ const vespalib::string &name() const { return _name; }
+ size_t size() const {
+ return _size ? _size : _dict.size();
+ }
+ TensorSpec::Label label(size_t idx) const {
+ assert(idx < size());
+ return _size ? TensorSpec::Label{idx} : TensorSpec::Label{_dict[idx]};
+ }
+};
+
+/**
+ * Specification defining how to generate a TensorSpec. Typically used
+ * to generate complex values for testing and benchmarking.
+ **/
+class GenSpec
+{
+public:
+ using seq_t = std::function<double(size_t)>;
+private:
+ std::vector<DimSpec> _dims;
+ CellType _cells;
+ seq_t _seq;
+
+ static double default_seq(size_t idx) { return (idx + 1.0); }
+public:
+ GenSpec() : _dims(), _cells(CellType::DOUBLE), _seq(default_seq) {}
+ GenSpec(GenSpec &&other);
+ GenSpec(const GenSpec &other);
+ GenSpec &operator=(GenSpec &&other);
+ GenSpec &operator=(const GenSpec &other);
+ ~GenSpec();
+ std::vector<DimSpec> dims() const { return _dims; }
+ CellType cells() const { return _cells; }
+ seq_t seq() const { return _seq; }
+ GenSpec cpy() const { return *this; }
+ GenSpec &idx(const vespalib::string &name, size_t size) {
+ _dims.emplace_back(name, size);
+ return *this;
+ }
+ GenSpec &map(const vespalib::string &name, size_t size, size_t stride = 1, const vespalib::string &prefix = "") {
+ _dims.emplace_back(name, DimSpec::make_dict(size, stride, prefix));
+ return *this;
+ }
+ GenSpec &map(const vespalib::string &name, std::vector<vespalib::string> dict) {
+ _dims.emplace_back(name, std::move(dict));
+ return *this;
+ }
+ GenSpec &cells(CellType cell_type) {
+ _cells = cell_type;
+ return *this;
+ }
+ GenSpec &cells_double() { return cells(CellType::DOUBLE); }
+ GenSpec &cells_float() { return cells(CellType::FLOAT); }
+ GenSpec &seq(seq_t seq_in) {
+ _seq = seq_in;
+ return *this;
+ }
+ GenSpec &seq_n() { return seq(default_seq); }
+ GenSpec &seq_bias(double bias) {
+ seq_t fun = [bias](size_t idx) noexcept { return (idx + bias); };
+ return seq(fun);
+ }
+ ValueType type() const;
+ TensorSpec gen() const;
+};
+
+} // namespace
diff --git a/eval/src/vespa/eval/eval/test/param_variants.h b/eval/src/vespa/eval/eval/test/param_variants.h
deleted file mode 100644
index 41a43ebca08..00000000000
--- a/eval/src/vespa/eval/eval/test/param_variants.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "eval_fixture.h"
-#include "tensor_model.hpp"
-
-namespace vespalib::eval::test {
-
-// for testing of optimizers / tensor functions
-// we produce the same param three times:
-// as-is, with float cells, and tagged as mutable.
-void add_variants(EvalFixture::ParamRepo &repo,
- const vespalib::string &name_base,
- const Layout &base_layout,
- const Sequence &seq)
-{
- auto name_f = name_base + "_f";
- auto name_m = "@" + name_base;
- auto name_m_f = "@" + name_base + "_f";
- repo.add(name_base, spec(base_layout, seq));
- repo.add(name_f, spec(float_cells(base_layout), seq));
- repo.add_mutable(name_m, spec(base_layout, seq));
- repo.add_mutable(name_m_f, spec(float_cells(base_layout), seq));
-}
-
-} // namespace
diff --git a/eval/src/vespa/eval/instruction/CMakeLists.txt b/eval/src/vespa/eval/instruction/CMakeLists.txt
index 58d5290f5d9..cac69d23640 100644
--- a/eval/src/vespa/eval/instruction/CMakeLists.txt
+++ b/eval/src/vespa/eval/instruction/CMakeLists.txt
@@ -32,6 +32,7 @@ vespa_add_library(eval_instruction OBJECT
pow_as_map_optimizer.cpp
remove_trivial_dimension_optimizer.cpp
replace_type_function.cpp
+ sparse_dot_product_function.cpp
sum_max_dot_product_function.cpp
vector_from_doubles_function.cpp
)
diff --git a/eval/src/vespa/eval/instruction/generic_join.cpp b/eval/src/vespa/eval/instruction/generic_join.cpp
index abe29b8228c..6d6f86b7c4d 100644
--- a/eval/src/vespa/eval/instruction/generic_join.cpp
+++ b/eval/src/vespa/eval/instruction/generic_join.cpp
@@ -308,6 +308,17 @@ SparseJoinPlan::SparseJoinPlan(const ValueType &lhs_type, const ValueType &rhs_t
[](const auto &a, const auto &b){ return (a.name < b.name); });
}
+SparseJoinPlan::SparseJoinPlan(size_t num_mapped_dims)
+ : sources(num_mapped_dims, Source::BOTH), lhs_overlap(), rhs_overlap()
+{
+ lhs_overlap.reserve(num_mapped_dims);
+ rhs_overlap.reserve(num_mapped_dims);
+ for (size_t i = 0; i < num_mapped_dims; ++i) {
+ lhs_overlap.push_back(i);
+ rhs_overlap.push_back(i);
+ }
+}
+
bool
SparseJoinPlan::should_forward_lhs_index() const
{
diff --git a/eval/src/vespa/eval/instruction/generic_join.h b/eval/src/vespa/eval/instruction/generic_join.h
index 1fcfcf416cc..026a2938971 100644
--- a/eval/src/vespa/eval/instruction/generic_join.h
+++ b/eval/src/vespa/eval/instruction/generic_join.h
@@ -58,6 +58,7 @@ struct SparseJoinPlan {
bool should_forward_lhs_index() const;
bool should_forward_rhs_index() const;
SparseJoinPlan(const ValueType &lhs_type, const ValueType &rhs_type);
+ SparseJoinPlan(size_t num_mapped_dims); // full overlap plan
~SparseJoinPlan();
};
@@ -70,15 +71,14 @@ struct SparseJoinState {
const Value::Index &first_index;
const Value::Index &second_index;
const std::vector<size_t> &second_view_dims;
- std::vector<string_id> full_address;
- std::vector<string_id*> first_address;
- std::vector<const string_id*> address_overlap;
- std::vector<string_id*> second_only_address;
+ std::vector<string_id> full_address;
+ std::vector<string_id*> first_address;
+ std::vector<const string_id*> address_overlap;
+ std::vector<string_id*> second_only_address;
size_t lhs_subspace;
size_t rhs_subspace;
size_t &first_subspace;
size_t &second_subspace;
-
SparseJoinState(const SparseJoinPlan &plan, const Value::Index &lhs, const Value::Index &rhs);
~SparseJoinState();
};
diff --git a/eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp b/eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp
new file mode 100644
index 00000000000..93ae2856372
--- /dev/null
+++ b/eval/src/vespa/eval/instruction/sparse_dot_product_function.cpp
@@ -0,0 +1,111 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "sparse_dot_product_function.h"
+#include "generic_join.h"
+#include "detect_type.h"
+#include <vespa/eval/eval/fast_value.hpp>
+
+namespace vespalib::eval {
+
+using namespace tensor_function;
+using namespace operation;
+using namespace instruction;
+
+namespace {
+
+template <typename SCT, typename BCT>
+double my_fast_sparse_dot_product(const FastValueIndex &small_idx, const FastValueIndex &big_idx,
+ const SCT *small_cells, const BCT *big_cells)
+{
+ double result = 0.0;
+ small_idx.map.each_map_entry([&](auto small_subspace, auto hash) {
+ auto small_addr = small_idx.map.get_addr(small_subspace);
+ auto big_subspace = big_idx.map.lookup(small_addr, hash);
+ if (big_subspace != FastAddrMap::npos()) {
+ result += (small_cells[small_subspace] * big_cells[big_subspace]);
+ }
+ });
+ return result;
+}
+
+template <typename LCT, typename RCT>
+void my_sparse_dot_product_op(InterpretedFunction::State &state, uint64_t num_mapped_dims) {
+ const auto &lhs_idx = state.peek(1).index();
+ const auto &rhs_idx = state.peek(0).index();
+ const LCT *lhs_cells = state.peek(1).cells().typify<LCT>().cbegin();
+ const RCT *rhs_cells = state.peek(0).cells().typify<RCT>().cbegin();
+ if (auto indexes = detect_type<FastValueIndex>(lhs_idx, rhs_idx)) {
+#if __has_cpp_attribute(likely)
+ [[likely]];
+#endif
+ const auto &lhs_fast = indexes.get<0>();
+ const auto &rhs_fast = indexes.get<1>();
+ double result = (rhs_fast.map.size() < lhs_fast.map.size())
+ ? my_fast_sparse_dot_product(rhs_fast, lhs_fast, rhs_cells, lhs_cells)
+ : my_fast_sparse_dot_product(lhs_fast, rhs_fast, lhs_cells, rhs_cells);
+ state.pop_pop_push(state.stash.create<ScalarValue<double>>(result));
+ } else {
+#if __has_cpp_attribute(unlikely)
+ [[unlikely]];
+#endif
+ double result = 0.0;
+ SparseJoinPlan plan(num_mapped_dims);
+ SparseJoinState sparse(plan, lhs_idx, rhs_idx);
+ auto outer = sparse.first_index.create_view({});
+ auto inner = sparse.second_index.create_view(sparse.second_view_dims);
+ outer->lookup({});
+ while (outer->next_result(sparse.first_address, sparse.first_subspace)) {
+ inner->lookup(sparse.address_overlap);
+ if (inner->next_result(sparse.second_only_address, sparse.second_subspace)) {
+ result += (lhs_cells[sparse.lhs_subspace] * rhs_cells[sparse.rhs_subspace]);
+ }
+ }
+ state.pop_pop_push(state.stash.create<ScalarValue<double>>(result));
+ }
+}
+
+struct MyGetFun {
+ template <typename LCT, typename RCT>
+ static auto invoke() { return my_sparse_dot_product_op<LCT,RCT>; }
+};
+
+} // namespace <unnamed>
+
+SparseDotProductFunction::SparseDotProductFunction(const TensorFunction &lhs_in,
+ const TensorFunction &rhs_in)
+ : tensor_function::Op2(ValueType::make_type(CellType::DOUBLE, {}), lhs_in, rhs_in)
+{
+}
+
+InterpretedFunction::Instruction
+SparseDotProductFunction::compile_self(const ValueBuilderFactory &, Stash &) const
+{
+ auto op = typify_invoke<2,TypifyCellType,MyGetFun>(lhs().result_type().cell_type(), rhs().result_type().cell_type());
+ return InterpretedFunction::Instruction(op, lhs().result_type().count_mapped_dimensions());
+}
+
+bool
+SparseDotProductFunction::compatible_types(const ValueType &res, const ValueType &lhs, const ValueType &rhs)
+{
+ return (res.is_scalar() && (res.cell_type() == CellType::DOUBLE) &&
+ lhs.is_sparse() && (rhs.dimensions() == lhs.dimensions()));
+}
+
+const TensorFunction &
+SparseDotProductFunction::optimize(const TensorFunction &expr, Stash &stash)
+{
+ auto reduce = as<Reduce>(expr);
+ if (reduce && (reduce->aggr() == Aggr::SUM)) {
+ auto join = as<Join>(reduce->child());
+ if (join && (join->function() == Mul::f)) {
+ const TensorFunction &lhs = join->lhs();
+ const TensorFunction &rhs = join->rhs();
+ if (compatible_types(expr.result_type(), lhs.result_type(), rhs.result_type())) {
+ return stash.create<SparseDotProductFunction>(lhs, rhs);
+ }
+ }
+ }
+ return expr;
+}
+
+} // namespace
diff --git a/eval/src/vespa/eval/instruction/sparse_dot_product_function.h b/eval/src/vespa/eval/instruction/sparse_dot_product_function.h
new file mode 100644
index 00000000000..ccc7a61f5e8
--- /dev/null
+++ b/eval/src/vespa/eval/instruction/sparse_dot_product_function.h
@@ -0,0 +1,23 @@
+// 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/tensor_function.h>
+
+namespace vespalib::eval {
+
+/**
+ * Tensor function for a dot product between two sparse tensors.
+ */
+class SparseDotProductFunction : public tensor_function::Op2
+{
+public:
+ SparseDotProductFunction(const TensorFunction &lhs_in,
+ const TensorFunction &rhs_in);
+ InterpretedFunction::Instruction compile_self(const ValueBuilderFactory &factory, Stash &stash) const override;
+ bool result_is_mutable() const override { return true; }
+ static bool compatible_types(const ValueType &res, const ValueType &lhs, const ValueType &rhs);
+ static const TensorFunction &optimize(const TensorFunction &expr, Stash &stash);
+};
+
+} // namespace
diff --git a/fastos/src/vespa/fastos/ringbuffer.h b/fastos/src/vespa/fastos/ringbuffer.h
index 41c0af7385b..89b1bb84c9c 100644
--- a/fastos/src/vespa/fastos/ringbuffer.h
+++ b/fastos/src/vespa/fastos/ringbuffer.h
@@ -42,18 +42,6 @@ public:
_closed = false;
}
- FastOS_RingBufferData *GetData () { return _data; }
-
- void RepositionDataAt0 ()
- {
- uint8_t *src = &_data->_buffer[_dataIndex];
- uint8_t *dst = _data->_buffer;
-
- for(int i=0; i<_dataSize; i++)
- *dst++ = *src++;
- _dataIndex = 0;
- }
-
FastOS_RingBuffer (int bufferSize)
: _closed(false),
_data(0),
@@ -93,11 +81,6 @@ public:
_dataSize += bytes;
}
- int GetDataSize ()
- {
- return _dataSize;
- }
-
int GetWriteSpace ()
{
int spaceLeft = _bufferSize - _dataSize;
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 3a67ca3c374..23dd047cc4c 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -54,75 +54,62 @@ public class Flags {
"Takes effect on the next run of RetiredExpirer.",
HOSTNAME);
- public static final UnboundBooleanFlag WAIT_FOR_RESOURCES_IN_PREPARE = defineFeatureFlag(
- "wait-for-resources-in-prepare", false,
- List.of("freva"), "2021-01-21", "2021-04-01",
- "Whether deployment prepare should wait until all resources (parent hosts, LBs, etc.) are ready before returning",
- "Takes effect at redeployment.");
-
public static final UnboundDoubleFlag DEFAULT_TERM_WISE_LIMIT = defineDoubleFlag(
"default-term-wise-limit", 1.0,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Default limit for when to apply termwise query evaluation",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundStringFlag FEED_SEQUENCER_TYPE = defineStringFlag(
"feed-sequencer-type", "LATENCY",
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Selects type of sequenced executor used for feeding, valid values are LATENCY, ADAPTIVE, THROUGHPUT",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundStringFlag RESPONSE_SEQUENCER_TYPE = defineStringFlag(
"response-sequencer-type", "ADAPTIVE",
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Selects type of sequenced executor used for mbus responses, valid values are LATENCY, ADAPTIVE, THROUGHPUT",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundIntFlag RESPONSE_NUM_THREADS = defineIntFlag(
"response-num-threads", 2,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Number of threads used for mbus responses, default is 2, negative number = numcores/4",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag SKIP_COMMUNICATIONMANAGER_THREAD = defineFeatureFlag(
"skip-communicatiomanager-thread", false,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Should we skip the communicationmanager thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag SKIP_MBUS_REQUEST_THREAD = defineFeatureFlag(
"skip-mbus-request-thread", false,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Should we skip the mbus request thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag SKIP_MBUS_REPLY_THREAD = defineFeatureFlag(
"skip-mbus-reply-thread", false,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Should we skip the mbus reply thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
public static final UnboundBooleanFlag USE_THREE_PHASE_UPDATES = defineFeatureFlag(
"use-three-phase-updates", false,
- List.of("vekterli"), "2020-12-02", "2021-02-01",
+ List.of("vekterli"), "2020-12-02", "2021-03-01",
"Whether to enable the use of three-phase updates when bucket replicas are out of sync.",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
- public static final UnboundBooleanFlag USE_FAST_VALUE_TENSOR_IMPLEMENTATION = defineFeatureFlag(
- "use-fast-value-tensor-implementation", false,
- List.of("geirst"), "2020-12-02", "2021-02-01",
- "Whether to use FastValueBuilderFactory as the tensor implementation on all content nodes.",
- "Takes effect at restart of content node process",
- ZONE_ID, APPLICATION_ID);
-
public static final UnboundBooleanFlag TCP_ABORT_ON_OVERFLOW = defineFeatureFlag(
"tcp-abort-on-overflow", false,
List.of("andreer"), "2020-12-02", "2021-02-01",
@@ -132,14 +119,14 @@ public class Flags {
public static final UnboundStringFlag TLS_FOR_ZOOKEEPER_CLIENT_SERVER_COMMUNICATION = defineStringFlag(
"tls-for-zookeeper-client-server-communication", "OFF",
- List.of("hmusum"), "2020-12-02", "2021-02-01",
+ List.of("hmusum"), "2020-12-02", "2021-04-01",
"How to setup TLS for ZooKeeper client/server communication. Valid values are OFF, PORT_UNIFICATION, TLS_WITH_PORT_UNIFICATION, TLS_ONLY",
"Takes effect on restart of config server",
NODE_TYPE, HOSTNAME);
public static final UnboundBooleanFlag USE_TLS_FOR_ZOOKEEPER_CLIENT = defineFeatureFlag(
"use-tls-for-zookeeper-client", false,
- List.of("hmusum"), "2020-12-02", "2021-02-01",
+ List.of("hmusum"), "2020-12-02", "2021-04-01",
"Whether to use TLS for ZooKeeper clients",
"Takes effect on restart of process",
NODE_TYPE, HOSTNAME);
@@ -150,40 +137,22 @@ public class Flags {
"Whether endpoint certificates should be validated before use",
"Takes effect on the next deployment of the application");
- public static final UnboundStringFlag DELETE_UNUSED_ENDPOINT_CERTIFICATES = defineStringFlag(
- "delete-unused-endpoint-certificates", "disable",
- List.of("andreer"), "2020-12-02", "2021-02-01",
- "Whether the endpoint certificate maintainer should delete unused certificates in cameo/zk",
- "Takes effect on next scheduled run of maintainer - set to \"disable\", \"dryrun\" or \"enable\"");
-
- public static final UnboundBooleanFlag USE_ALTERNATIVE_ENDPOINT_CERTIFICATE_PROVIDER = defineFeatureFlag(
- "use-alternative-endpoint-certificate-provider", false,
- List.of("andreer"), "2020-12-02", "2021-02-01",
- "Whether to use an alternative CA when provisioning new certificates",
- "Takes effect only on initial application deployment - not on later certificate refreshes!");
-
public static final UnboundStringFlag YUM_DIST_HOST = defineStringFlag(
"yum-dist-host", "",
- List.of("aressem"), "2020-12-02", "2021-02-01",
+ List.of("aressem"), "2020-12-02", "2021-03-01",
"Override the default dist host for yum.",
"Takes effect on next tick or on host-admin restart (may vary where used).");
- public static final UnboundBooleanFlag ENDPOINT_CERT_IN_SHARED_ROUTING = defineFeatureFlag(
- "endpoint-cert-in-shared-routing", false,
- List.of("andreer"), "2020-12-02", "2021-02-01",
- "Whether to provision and use endpoint certs for apps in shared routing zones",
- "Takes effect on next deployment of the application", APPLICATION_ID);
-
public static final UnboundBooleanFlag PROVISION_TENANT_ROLES = defineFeatureFlag(
- "provision-application-roles", false,
- List.of("tokle"), "2020-12-02", "2021-08-01",
- "Whether application roles should be provisioned",
+ "provision-tenant-roles", false,
+ List.of("tokle"), "2020-12-02", "2021-04-01",
+ "Whether tenant roles should be provisioned",
"Takes effect on next deployment (controller)",
TENANT_ID);
public static final UnboundBooleanFlag TENANT_IAM_ROLE = defineFeatureFlag(
"application-iam-roles", false,
- List.of("tokle"), "2020-12-02", "2021-08-01",
+ List.of("tokle"), "2020-12-02", "2021-04-01",
"Allow separate iam roles when provisioning/assigning hosts",
"Takes effect immediately on new hosts, on next redeploy for applications",
TENANT_ID);
@@ -228,39 +197,25 @@ public class Flags {
public static final UnboundBooleanFlag USE_ACCESS_CONTROL_CLIENT_AUTHENTICATION = defineFeatureFlag(
"use-access-control-client-authentication", false,
- List.of("tokle"), "2020-12-02", "2021-02-01",
+ List.of("tokle"), "2020-12-02", "2021-03-01",
"Whether application container should set up client authentication on default port based on access control element",
"Takes effect on next internal redeployment",
APPLICATION_ID);
public static final UnboundBooleanFlag USE_ASYNC_MESSAGE_HANDLING_ON_SCHEDULE = defineFeatureFlag(
"async-message-handling-on-schedule", false,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"Optionally deliver async messages in own thread",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
- public static final UnboundIntFlag MERGE_CHUNK_SIZE = defineIntFlag(
- "merge-chunk-size", 0x2000000,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
- "Size of baldersheim buffer in service layer",
- "Takes effect at redeployment",
- ZONE_ID, APPLICATION_ID);
-
public static final UnboundDoubleFlag FEED_CONCURRENCY = defineDoubleFlag(
"feed-concurrency", 0.5,
- List.of("baldersheim"), "2020-12-02", "2021-02-01",
+ List.of("baldersheim"), "2020-12-02", "2022-01-01",
"How much concurrency should be allowed for feed",
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
- public static final UnboundBooleanFlag ENABLE_AUTOMATIC_REINDEXING = defineFeatureFlag(
- "enable-automatic-reindexing", true,
- List.of("bjorncs", "jonmv"), "2020-12-02", "2021-02-01",
- "Whether to automatically trigger reindexing from config change",
- "Takes effect on next internal redeployment",
- APPLICATION_ID);
-
public static final UnboundDoubleFlag REINDEXER_WINDOW_SIZE_INCREMENT = defineDoubleFlag(
"reindexer-window-size-increment", 0.2,
List.of("jonmv"), "2020-12-09", "2021-02-07",
@@ -277,7 +232,7 @@ public class Flags {
public static final UnboundBooleanFlag USE_POWER_OF_TWO_CHOICES_LOAD_BALANCING = defineFeatureFlag(
"use-power-of-two-choices-load-balancing", false,
- List.of("tokle"), "2020-12-02", "2021-02-01",
+ List.of("tokle"), "2020-12-02", "2021-02-15",
"Whether to use Power of two load balancing algorithm for application",
"Takes effect on next internal redeployment",
APPLICATION_ID);
@@ -308,12 +263,6 @@ public class Flags {
"Whether to enable zstd compression of jdisc access logs",
"Takes effect on (re)deployment");
- public static final UnboundBooleanFlag USE_ENDPOINT_CERTIFICATE_MAINTAINER = defineFeatureFlag(
- "use-endpoint-certificate-maintainer", false,
- List.of("andreer"), "2021-01-12", "2021-02-12",
- "Use EndpointCertificateMaintainer instead of EndpointCertificateManager cleanup thread to handle certificate refreshes and deletions",
- "Takes effect on next run of maintainer / next manager cleanup thread run");
-
public static final UnboundBooleanFlag ENABLE_FEED_BLOCK_IN_DISTRIBUTOR = defineFeatureFlag(
"enable-feed-block-in-distributor", false,
List.of("geirst"), "2021-01-27", "2021-04-01",
diff --git a/fnet/src/tests/connect/connect_test.cpp b/fnet/src/tests/connect/connect_test.cpp
index 2b4a2bbe9f0..50722e7d16e 100644
--- a/fnet/src/tests/connect/connect_test.cpp
+++ b/fnet/src/tests/connect/connect_test.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/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/fnet/transport.h>
#include <vespa/fnet/transport_thread.h>
#include <vespa/fnet/simplepacketstreamer.h>
@@ -13,7 +14,7 @@
using namespace vespalib;
-int short_time = 20; // ms
+constexpr vespalib::duration short_time = 20ms;
struct BlockingHostResolver : public AsyncResolver::HostResolver {
AsyncResolver::SimpleHostResolver resolver;
diff --git a/fnet/src/tests/frt/rpc/detach_return_invoke.cpp b/fnet/src/tests/frt/rpc/detach_return_invoke.cpp
index 68bd58d2f82..8d65c65f768 100644
--- a/fnet/src/tests/frt/rpc/detach_return_invoke.cpp
+++ b/fnet/src/tests/frt/rpc/detach_return_invoke.cpp
@@ -5,6 +5,7 @@
#include <vespa/fnet/frt/rpcrequest.h>
#include <vespa/fnet/frt/invoker.h>
#include <vespa/vespalib/util/stringfmt.h>
+#include <thread>
struct Receptor : public FRT_IRequestWait
{
diff --git a/fnet/src/tests/sync_execute/sync_execute.cpp b/fnet/src/tests/sync_execute/sync_execute.cpp
index 55a898496ae..d577b5b7e92 100644
--- a/fnet/src/tests/sync_execute/sync_execute.cpp
+++ b/fnet/src/tests/sync_execute/sync_execute.cpp
@@ -1,5 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/util/gate.h>
#include <vespa/fnet/transport.h>
#include <vespa/fnet/iexecutable.h>
#include <vespa/fastos/thread.h>
diff --git a/jrt/src/com/yahoo/jrt/CryptoEngine.java b/jrt/src/com/yahoo/jrt/CryptoEngine.java
index 6d1955d7f66..318ee333fee 100644
--- a/jrt/src/com/yahoo/jrt/CryptoEngine.java
+++ b/jrt/src/com/yahoo/jrt/CryptoEngine.java
@@ -1,10 +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.jrt;
-
-import com.yahoo.security.tls.AuthorizationMode;
import com.yahoo.security.tls.MixedMode;
-import com.yahoo.security.tls.ConfigFileBasedTlsContext;
import com.yahoo.security.tls.TlsContext;
import com.yahoo.security.tls.TransportSecurityUtils;
@@ -24,8 +21,7 @@ public interface CryptoEngine extends AutoCloseable {
if (!TransportSecurityUtils.isTransportSecurityEnabled()) {
return new NullCryptoEngine();
}
- AuthorizationMode mode = TransportSecurityUtils.getInsecureAuthorizationMode();
- TlsContext tlsContext = new ConfigFileBasedTlsContext(TransportSecurityUtils.getConfigFile().get(), mode);
+ TlsContext tlsContext = TransportSecurityUtils.getSystemTlsContext().get();
TlsCryptoEngine tlsCryptoEngine = new TlsCryptoEngine(tlsContext);
MixedMode mixedMode = TransportSecurityUtils.getInsecureMixedMode();
switch (mixedMode) {
diff --git a/messagebus/src/tests/context/context.cpp b/messagebus/src/tests/context/context.cpp
index 713e01cae73..dd734cbbb26 100644
--- a/messagebus/src/tests/context/context.cpp
+++ b/messagebus/src/tests/context/context.cpp
@@ -10,6 +10,7 @@
#include <vespa/messagebus/testlib/simplemessage.h>
#include <vespa/messagebus/testlib/testserver.h>
#include <vespa/vespalib/testkit/testapp.h>
+#include <thread>
using namespace mbus;
diff --git a/messagebus/src/tests/messagebus/messagebus.cpp b/messagebus/src/tests/messagebus/messagebus.cpp
index 367bfc997d0..259c24ad176 100644
--- a/messagebus/src/tests/messagebus/messagebus.cpp
+++ b/messagebus/src/tests/messagebus/messagebus.cpp
@@ -13,6 +13,7 @@
#include <vespa/messagebus/testlib/testserver.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/util/stringfmt.h>
+#include <thread>
using namespace mbus;
diff --git a/messagebus/src/tests/serviceaddress/serviceaddress.cpp b/messagebus/src/tests/serviceaddress/serviceaddress.cpp
index c5d07bc437a..0ecba42bf4e 100644
--- a/messagebus/src/tests/serviceaddress/serviceaddress.cpp
+++ b/messagebus/src/tests/serviceaddress/serviceaddress.cpp
@@ -4,6 +4,7 @@
#include <vespa/messagebus/testlib/slobrok.h>
#include <vespa/messagebus/testlib/testserver.h>
#include <vespa/messagebus/network/rpcservice.h>
+#include <thread>
using namespace mbus;
diff --git a/messagebus/src/tests/slobrok/slobrok.cpp b/messagebus/src/tests/slobrok/slobrok.cpp
index 439ee0b23b5..a9e6d0f4847 100644
--- a/messagebus/src/tests/slobrok/slobrok.cpp
+++ b/messagebus/src/tests/slobrok/slobrok.cpp
@@ -5,8 +5,8 @@
#include <vespa/slobrok/sbmirror.h>
#include <vespa/messagebus/network/rpcnetwork.h>
#include <vespa/messagebus/network/rpcnetworkparams.h>
-
#include <vespa/vespalib/util/host_name.h>
+#include <thread>
using slobrok::api::IMirrorAPI;
diff --git a/messagebus/src/tests/sourcesession/sourcesession.cpp b/messagebus/src/tests/sourcesession/sourcesession.cpp
index 3ffe0f8e19d..25d75aa180c 100644
--- a/messagebus/src/tests/sourcesession/sourcesession.cpp
+++ b/messagebus/src/tests/sourcesession/sourcesession.cpp
@@ -17,6 +17,7 @@
#include <vespa/messagebus/testlib/slobrok.h>
#include <vespa/messagebus/testlib/testserver.h>
#include <vespa/vespalib/testkit/testapp.h>
+#include <thread>
using namespace mbus;
diff --git a/messagebus/src/tests/throttling/throttling.cpp b/messagebus/src/tests/throttling/throttling.cpp
index a23e0b61550..e11aabfd0ba 100644
--- a/messagebus/src/tests/throttling/throttling.cpp
+++ b/messagebus/src/tests/throttling/throttling.cpp
@@ -13,6 +13,7 @@
#include <vespa/messagebus/testlib/simplemessage.h>
#include <vespa/messagebus/testlib/simplereply.h>
#include <vespa/messagebus/testlib/testserver.h>
+#include <thread>
using namespace mbus;
diff --git a/messagebus_test/src/tests/trace/trace.cpp b/messagebus_test/src/tests/trace/trace.cpp
index 1ab30303d2c..a5e9251f9cc 100644
--- a/messagebus_test/src/tests/trace/trace.cpp
+++ b/messagebus_test/src/tests/trace/trace.cpp
@@ -19,6 +19,7 @@
#include <vespa/messagebus/testlib/simplereply.h>
#include <vespa/messagebus/testlib/simpleprotocol.h>
#include <iostream>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("trace_test");
diff --git a/metrics-proxy/pom.xml b/metrics-proxy/pom.xml
index 8bf5a30e584..90d9f093da1 100644
--- a/metrics-proxy/pom.xml
+++ b/metrics-proxy/pom.xml
@@ -101,11 +101,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.json</groupId>
- <artifactId>json</artifactId>
- <scope>provided</scope>
- </dependency>
<!-- compile scope -->
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java
index 3cd9f526387..0e0511967a3 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java
@@ -9,13 +9,11 @@ import ai.vespa.metricsproxy.metric.model.MetricsPacket;
import ai.vespa.metricsproxy.metric.model.ServiceId;
import ai.vespa.metricsproxy.service.SystemPollerProvider;
import ai.vespa.metricsproxy.service.VespaServices;
+import com.fasterxml.jackson.databind.JsonNode;
import com.google.inject.Inject;
import com.yahoo.container.jdisc.state.CoredumpGatherer;
import com.yahoo.container.jdisc.state.FileWrapper;
import com.yahoo.container.jdisc.state.HostLifeGatherer;
-import com.yahoo.yolean.Exceptions;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Iterator;
@@ -54,10 +52,10 @@ public class NodeMetricGatherer {
List<MetricsPacket.Builder> metricPacketBuilders = new ArrayList<>();
metricPacketBuilders.addAll(gatherServiceHealthMetrics(vespaServices));
- JSONObject coredumpPacket = CoredumpGatherer.gatherCoredumpMetrics(fileWrapper);
+ JsonNode coredumpPacket = CoredumpGatherer.gatherCoredumpMetrics(fileWrapper);
addObjectToBuilders(metricPacketBuilders, coredumpPacket);
if (SystemPollerProvider.runningOnLinux()) {
- JSONObject packet = HostLifeGatherer.getHostLifePacket(fileWrapper);
+ JsonNode packet = HostLifeGatherer.getHostLifePacket(fileWrapper);
addObjectToBuilders(metricPacketBuilders, packet);
}
@@ -69,24 +67,20 @@ public class NodeMetricGatherer {
).collect(Collectors.toList());
}
- protected static void addObjectToBuilders(List<MetricsPacket.Builder> builders, JSONObject object) {
- try {
- MetricsPacket.Builder builder = new MetricsPacket.Builder(ServiceId.toServiceId(object.getString("application")));
- builder.timestamp(object.getLong("timestamp"));
- if (object.has("status_code")) builder.statusCode(object.getInt("status_code"));
- if (object.has("status_msg")) builder.statusMessage(object.getString("status_msg"));
- if (object.has("metrics")) {
- JSONObject metrics = object.getJSONObject("metrics");
- Iterator<?> keys = metrics.keys();
- while(keys.hasNext()) {
- String key = (String) keys.next();
- builder.putMetric(MetricId.toMetricId(key), metrics.getLong(key));
- }
+ protected static void addObjectToBuilders(List<MetricsPacket.Builder> builders, JsonNode object) {
+ MetricsPacket.Builder builder = new MetricsPacket.Builder(ServiceId.toServiceId(object.get("application").textValue()));
+ builder.timestamp(object.get("timestamp").longValue());
+ if (object.has("status_code")) builder.statusCode(object.get("status_code").intValue());
+ if (object.has("status_msg")) builder.statusMessage(object.get("status_msg").textValue());
+ if (object.has("metrics")) {
+ JsonNode metrics = object.get("metrics");
+ Iterator<?> keys = metrics.fieldNames();
+ while(keys.hasNext()) {
+ String key = (String) keys.next();
+ builder.putMetric(MetricId.toMetricId(key), metrics.get(key).asLong());
}
- builders.add(builder);
- } catch (JSONException e) {
- Exceptions.toMessageString(e);
}
+ builders.add(builder);
}
}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java
index 1cab1a859a9..827f513a418 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java
@@ -2,9 +2,10 @@
package ai.vespa.metricsproxy.service;
import ai.vespa.metricsproxy.metric.HealthMetric;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
import java.util.logging.Level;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.io.IOException;
import java.util.logging.Logger;
@@ -15,6 +16,7 @@ import java.util.logging.Logger;
* @author Jo Kristian Bergum
*/
public class RemoteHealthMetricFetcher extends HttpMetricFetcher {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
private final static Logger log = Logger.getLogger(RemoteHealthMetricFetcher.class.getPackage().getName());
private final static String HEALTH_PATH = STATE_PATH + "health";
@@ -54,16 +56,16 @@ public class RemoteHealthMetricFetcher extends HttpMetricFetcher {
return HealthMetric.getUnknown("Empty response from status page");
}
try {
- JSONObject o = new JSONObject(data);
- JSONObject status = o.getJSONObject("status");
- String code = status.getString("code");
+ JsonNode o = jsonMapper.readTree(data);
+ JsonNode status = o.get("status");
+ String code = status.get("code").asText();
String message = "";
if (status.has("message")) {
- message = status.getString("message");
+ message = status.get("message").textValue();
}
return HealthMetric.get(code, message);
- } catch (JSONException e) {
+ } catch (IOException e) {
log.log(Level.FINE, "Failed to parse json response from metrics page:" + e + ":" + data);
return HealthMetric.getUnknown("Not able to parse json from status page");
}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
index 442ebc0d38d..464f215edc4 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
@@ -4,9 +4,9 @@ package ai.vespa.metricsproxy.service;
import ai.vespa.metricsproxy.metric.Metric;
import ai.vespa.metricsproxy.metric.Metrics;
import ai.vespa.metricsproxy.metric.model.DimensionId;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.IOException;
import java.util.Collections;
@@ -23,6 +23,8 @@ import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
*/
public class RemoteMetricsFetcher extends HttpMetricFetcher {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
final static String METRICS_PATH = STATE_PATH + "metrics";
RemoteMetricsFetcher(VespaService service, int port) {
@@ -57,21 +59,21 @@ public class RemoteMetricsFetcher extends HttpMetricFetcher {
return remoteMetrics;
}
- private Metrics parse(String data) throws JSONException {
- JSONObject o = new JSONObject(data);
+ private Metrics parse(String data) throws IOException {
+ JsonNode o = jsonMapper.readTree(data);
if (!(o.has("metrics"))) {
return new Metrics(); //empty
}
- JSONObject metrics = o.getJSONObject("metrics");
- JSONArray values;
+ JsonNode metrics = o.get("metrics");
+ ArrayNode values;
long timestamp;
try {
- JSONObject snapshot = metrics.getJSONObject("snapshot");
- timestamp = (long) snapshot.getDouble("to");
- values = metrics.getJSONArray("values");
- } catch (JSONException e) {
+ JsonNode snapshot = metrics.get("snapshot");
+ timestamp = snapshot.get("to").asLong();
+ values = (ArrayNode) metrics.get("values");
+ } catch (Exception e) {
// snapshot might not have been produced. Do not throw exception into log
return new Metrics();
}
@@ -81,29 +83,29 @@ public class RemoteMetricsFetcher extends HttpMetricFetcher {
Map<DimensionId, String> noDims = Collections.emptyMap();
Map<String, Map<DimensionId, String>> uniqueDimensions = new HashMap<>();
- for (int i = 0; i < values.length(); i++) {
- JSONObject metric = values.getJSONObject(i);
- String name = metric.getString("name");
+ for (int i = 0; i < values.size(); i++) {
+ JsonNode metric = values.get(i);
+ String name = metric.get("name").textValue();
String description = "";
if (metric.has("description")) {
- description = metric.getString("description");
+ description = metric.get("description").textValue();
}
Map<DimensionId, String> dim = noDims;
if (metric.has("dimensions")) {
- JSONObject dimensions = metric.getJSONObject("dimensions");
+ JsonNode dimensions = metric.get("dimensions");
StringBuilder sb = new StringBuilder();
- for (Iterator<?> it = dimensions.keys(); it.hasNext(); ) {
+ for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) {
String k = (String) it.next();
- String v = dimensions.getString(k);
+ String v = dimensions.get(k).asText();
sb.append(toDimensionId(k)).append(v);
}
if ( ! uniqueDimensions.containsKey(sb.toString())) {
dim = new HashMap<>();
- for (Iterator<?> it = dimensions.keys(); it.hasNext(); ) {
+ for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) {
String k = (String) it.next();
- String v = dimensions.getString(k);
+ String v = dimensions.get(k).textValue();
dim.put(toDimensionId(k), v);
}
uniqueDimensions.put(sb.toString(), Collections.unmodifiableMap(dim));
@@ -111,10 +113,17 @@ public class RemoteMetricsFetcher extends HttpMetricFetcher {
dim = uniqueDimensions.get(sb.toString());
}
- JSONObject aggregates = metric.getJSONObject("values");
- for (Iterator<?> it = aggregates.keys(); it.hasNext(); ) {
+ JsonNode aggregates = metric.get("values");
+ for (Iterator<?> it = aggregates.fieldNames(); it.hasNext(); ) {
String aggregator = (String) it.next();
- Number value = (Number) aggregates.get(aggregator);
+ JsonNode aggregatorValue = aggregates.get(aggregator);
+ if (aggregatorValue == null) {
+ throw new IllegalArgumentException("Value for aggregator '" + aggregator + "' is missing");
+ }
+ Number value = aggregatorValue.numberValue();
+ if (value == null) {
+ throw new IllegalArgumentException("Value for aggregator '" + aggregator + "' is not a number");
+ }
StringBuilder metricName = (new StringBuilder()).append(name).append(".").append(aggregator);
m.add(new Metric(metricName.toString(), value, timestamp, dim, description));
}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java
index d7576718e8a..cf1eac3c691 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java
@@ -8,13 +8,11 @@ import ai.vespa.metricsproxy.metric.model.json.GenericApplicationModel;
import ai.vespa.metricsproxy.metric.model.json.GenericJsonModel;
import ai.vespa.metricsproxy.metric.model.json.GenericMetrics;
import ai.vespa.metricsproxy.metric.model.json.GenericService;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
-import java.util.regex.Pattern;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -24,6 +22,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.Executors;
+import java.util.regex.Pattern;
import static ai.vespa.metricsproxy.TestUtil.getFileContents;
import static ai.vespa.metricsproxy.http.ValuesFetcher.defaultMetricsConsumerId;
@@ -49,6 +48,8 @@ import static org.junit.Assert.fail;
@SuppressWarnings("UnstableApiUsage")
public class ApplicationMetricsHandlerTest {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final String HOST = "localhost";
private static final String URI_BASE = "http://" + HOST;
private static final String METRICS_V1_URI = URI_BASE + METRICS_V1_PATH;
@@ -102,16 +103,16 @@ public class ApplicationMetricsHandlerTest {
@Test
public void v1_response_contains_values_uri() throws Exception {
String response = testDriver.sendRequest(METRICS_V1_URI).readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("resources"));
- JSONArray resources = root.getJSONArray("resources");
- assertEquals(2, resources.length());
+ ArrayNode resources = (ArrayNode) root.get("resources");
+ assertEquals(2, resources.size());
- JSONObject valuesUrl = resources.getJSONObject(0);
- assertEquals(METRICS_VALUES_URI, valuesUrl.getString("url"));
- JSONObject prometheusUrl = resources.getJSONObject(1);
- assertEquals(PROMETHEUS_VALUES_URI, prometheusUrl.getString("url"));
+ JsonNode valuesUrl = resources.get(0);
+ assertEquals(METRICS_VALUES_URI, valuesUrl.get("url").textValue());
+ JsonNode prometheusUrl = resources.get(1);
+ assertEquals(PROMETHEUS_VALUES_URI, prometheusUrl.get("url").textValue());
}
@Ignore
@@ -199,7 +200,7 @@ public class ApplicationMetricsHandlerTest {
@Test
public void invalid_path_yields_error_response() throws Exception {
String response = testDriver.sendRequest(METRICS_V1_URI + "/invalid").readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("error"));
}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java
index 1c5ce695155..379ef04d38d 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java
@@ -6,9 +6,9 @@ import ai.vespa.metricsproxy.metric.model.json.GenericJsonModel;
import ai.vespa.metricsproxy.metric.model.json.GenericMetrics;
import ai.vespa.metricsproxy.metric.model.json.GenericService;
import ai.vespa.metricsproxy.service.DownService;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.json.JSONArray;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import org.junit.Ignore;
import org.junit.Test;
@@ -32,6 +32,8 @@ import static org.junit.Assert.fail;
*/
public abstract class MetricsHandlerTestBase<MODEL> extends HttpHandlerTestBase {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
static String rootUri;
static String valuesUri;
@@ -56,21 +58,21 @@ public abstract class MetricsHandlerTestBase<MODEL> extends HttpHandlerTestBase
@Test
public void invalid_path_yields_error_response() throws Exception {
String response = testDriver.sendRequest(rootUri + "/invalid").readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("error"));
}
@Test
public void root_response_contains_values_uri() throws Exception {
String response = testDriver.sendRequest(rootUri).readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("resources"));
- JSONArray resources = root.getJSONArray("resources");
- assertEquals(1, resources.length());
+ ArrayNode resources = (ArrayNode) root.get("resources");
+ assertEquals(1, resources.size());
- JSONObject valuesUrl = resources.getJSONObject(0);
- assertEquals(valuesUri, valuesUrl.getString("url"));
+ JsonNode valuesUrl = resources.get(0);
+ assertEquals(valuesUri, valuesUrl.get("url").textValue());
}
@Ignore
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java
index a224c4090b3..89186e63b93 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java
@@ -3,9 +3,10 @@ package ai.vespa.metricsproxy.http.prometheus;
import ai.vespa.metricsproxy.http.HttpHandlerTestBase;
import ai.vespa.metricsproxy.service.DummyService;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
-import org.json.JSONArray;
-import org.json.JSONObject;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
@@ -22,6 +23,8 @@ import static org.junit.Assert.assertTrue;
@SuppressWarnings("UnstableApiUsage")
public class PrometheusHandlerTest extends HttpHandlerTestBase {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final String V1_URI = URI_BASE + PrometheusHandler.V1_PATH;
private static final String VALUES_URI = URI_BASE + PrometheusHandler.VALUES_PATH;
@@ -40,14 +43,14 @@ public class PrometheusHandlerTest extends HttpHandlerTestBase {
@Test
public void v1_response_contains_values_uri() throws Exception {
String response = testDriver.sendRequest(V1_URI).readAll();
- JSONObject root = new JSONObject(response);
+ JsonNode root = jsonMapper.readTree(response);
assertTrue(root.has("resources"));
- JSONArray resources = root.getJSONArray("resources");
- assertEquals(1, resources.length());
+ ArrayNode resources = (ArrayNode) root.get("resources");
+ assertEquals(1, resources.size());
- JSONObject valuesUrl = resources.getJSONObject(0);
- assertEquals(VALUES_URI, valuesUrl.getString("url"));
+ JsonNode valuesUrl = resources.get(0);
+ assertEquals(VALUES_URI, valuesUrl.get("url").textValue());
}
@Ignore
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java
index e2ad0ccd504..c2fc23a878d 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java
@@ -3,8 +3,9 @@ package ai.vespa.metricsproxy.node;
import ai.vespa.metricsproxy.metric.model.MetricId;
import ai.vespa.metricsproxy.metric.model.MetricsPacket;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Test;
import java.util.ArrayList;
@@ -17,10 +18,12 @@ import static org.junit.Assert.assertEquals;
*/
public class NodeMetricGathererTest {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
@Test
- public void testJSONObjectIsCorrectlyConvertedToMetricsPacket() throws JSONException {
+ public void testJSONObjectIsCorrectlyConvertedToMetricsPacket() {
List<MetricsPacket.Builder> builders = new ArrayList<>();
- JSONObject hostLifePacket = generateHostLifePacket();
+ JsonNode hostLifePacket = generateHostLifePacket();
NodeMetricGatherer.addObjectToBuilders(builders, hostLifePacket);
MetricsPacket packet = builders.remove(0).build();
@@ -32,17 +35,17 @@ public class NodeMetricGathererTest {
assertEquals(1l, packet.metrics().get(MetricId.toMetricId("alive")));
}
- private JSONObject generateHostLifePacket() throws JSONException {
+ private JsonNode generateHostLifePacket() {
- JSONObject jsonObject = new JSONObject();
+ ObjectNode jsonObject = jsonMapper.createObjectNode();
jsonObject.put("status_code", 0);
jsonObject.put("status_msg", "OK");
jsonObject.put("timestamp", 123);
jsonObject.put("application", "host_life");
- JSONObject metrics = new JSONObject();
+ ObjectNode metrics = jsonMapper.createObjectNode();
metrics.put("uptime", 12);
metrics.put("alive", 1);
- jsonObject.put("metrics", metrics);
+ jsonObject.set("metrics", metrics);
return jsonObject;
}
}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java
index 8d5bba77844..70970bfe8da 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java
@@ -5,17 +5,18 @@ import ai.vespa.metricsproxy.metric.Metric;
import ai.vespa.metricsproxy.metric.Metrics;
import ai.vespa.metricsproxy.metric.model.ConsumerId;
import ai.vespa.metricsproxy.service.VespaService;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.yahoo.jrt.Request;
import com.yahoo.jrt.Spec;
import com.yahoo.jrt.StringValue;
import com.yahoo.jrt.Supervisor;
import com.yahoo.jrt.Target;
import com.yahoo.jrt.Transport;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.junit.Test;
+import java.io.IOException;
import java.util.List;
import static ai.vespa.metricsproxy.TestUtil.getFileContents;
@@ -40,6 +41,8 @@ import static org.junit.Assert.assertTrue;
*/
public class RpcMetricsTest {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final String METRICS_RESPONSE = getFileContents("metrics-storage-simple.json").trim();
private static final String EXTRA_APP = "extra";
@@ -67,9 +70,9 @@ public class RpcMetricsTest {
String allServicesResponse = getMetricsForYamas(ALL_SERVICES, rpcClient).trim();
// Verify that application is used as serviceId, and that metric exists.
- JSONObject extraMetrics = findExtraMetricsObject(allServicesResponse);
- assertThat(extraMetrics.getJSONObject("metrics").getInt("foo.count"), is(3));
- assertThat(extraMetrics.getJSONObject("dimensions").getString("role"), is("extra-role"));
+ JsonNode extraMetrics = findExtraMetricsObject(allServicesResponse);
+ assertThat(extraMetrics.get("metrics").get("foo.count").intValue(), is(3));
+ assertThat(extraMetrics.get("dimensions").get("role").textValue(), is("extra-role"));
}
}
}
@@ -85,7 +88,7 @@ public class RpcMetricsTest {
// Verify that no extra metrics exists
String allServicesResponse = getMetricsForYamas(ALL_SERVICES, rpcClient).trim();
- JSONObject extraMetrics = findExtraMetricsObject(allServicesResponse);
+ JsonNode extraMetrics = findExtraMetricsObject(allServicesResponse);
assertEquals(extraMetrics.toString(), "{}");
}
}
@@ -130,28 +133,28 @@ public class RpcMetricsTest {
}
}
- private static void verifyMetricsFromRpcRequest(VespaService service, RpcClient client) throws JSONException {
+ private static void verifyMetricsFromRpcRequest(VespaService service, RpcClient client) throws IOException {
String jsonResponse = getMetricsForYamas(service.getMonitoringName(), client).trim();
- JSONArray metrics = new JSONObject(jsonResponse).getJSONArray("metrics");
- assertThat("Expected 3 metric messages", metrics.length(), is(3));
- for (int i = 0; i < metrics.length() - 1; i++) { // The last "metric message" contains only status code/message
- JSONObject jsonObject = metrics.getJSONObject(i);
+ ArrayNode metrics = (ArrayNode) jsonMapper.readTree(jsonResponse).get("metrics");
+ assertThat("Expected 3 metric messages", metrics.size(), is(3));
+ for (int i = 0; i < metrics.size() - 1; i++) { // The last "metric message" contains only status code/message
+ JsonNode jsonObject = metrics.get(i);
assertFalse(jsonObject.has("status_code"));
assertFalse(jsonObject.has("status_msg"));
- assertThat(jsonObject.getJSONObject("dimensions").getString("foo"), is("bar"));
- assertThat(jsonObject.getJSONObject("dimensions").getString("bar"), is("foo"));
- assertThat(jsonObject.getJSONObject("dimensions").getString("serviceDim"), is("serviceDimValue"));
- assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").length(), is(1));
- if (jsonObject.getJSONObject("metrics").has("foo_count")) {
- assertThat(jsonObject.getJSONObject("metrics").getInt("foo_count"), is(1));
- assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").get(0), is(vespaMetricsConsumerId.id));
+ assertThat(jsonObject.get("dimensions").get("foo").textValue(), is("bar"));
+ assertThat(jsonObject.get("dimensions").get("bar").textValue(), is("foo"));
+ assertThat(jsonObject.get("dimensions").get("serviceDim").textValue(), is("serviceDimValue"));
+ assertThat(jsonObject.get("routing").get("yamas").get("namespaces").size(), is(1));
+ if (jsonObject.get("metrics").has("foo_count")) {
+ assertThat(jsonObject.get("metrics").get("foo_count").intValue(), is(1));
+ assertThat(jsonObject.get("routing").get("yamas").get("namespaces").get(0).textValue(), is(vespaMetricsConsumerId.id));
} else {
- assertThat(jsonObject.getJSONObject("metrics").getInt("foo.count"), is(1));
- assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").get(0), is(CUSTOM_CONSUMER_ID.id));
+ assertThat(jsonObject.get("metrics").get("foo.count").intValue(), is(1));
+ assertThat(jsonObject.get("routing").get("yamas").get("namespaces").get(0).textValue(), is(CUSTOM_CONSUMER_ID.id));
}
}
- verifyStatusMessage(metrics.getJSONObject(metrics.length() - 1));
+ verifyStatusMessage(metrics.get(metrics.size() - 1));
}
private void verfiyMetricsFromServiceObject(VespaService service) {
@@ -166,15 +169,15 @@ public class RpcMetricsTest {
assertThat("Metric foo did not contain correct dimension for key = bar", foo.getDimensions().get(toDimensionId("bar")), is("foo"));
}
- private void verifyMetricsFromRpcRequestForAllServices(RpcClient client) throws JSONException {
+ private void verifyMetricsFromRpcRequestForAllServices(RpcClient client) throws IOException {
// Verify that metrics for all services can be retrieved in one request.
String allServicesResponse = getMetricsForYamas(ALL_SERVICES, client).trim();
- JSONArray allServicesMetrics = new JSONObject(allServicesResponse).getJSONArray("metrics");
- assertThat(allServicesMetrics.length(), is(5));
+ ArrayNode allServicesMetrics = (ArrayNode) jsonMapper.readTree(allServicesResponse).get("metrics");
+ assertThat(allServicesMetrics.size(), is(5));
}
@Test
- public void testGetAllMetricNames() throws Exception {
+ public void testGetAllMetricNames() {
try (IntegrationTester tester = new IntegrationTester()) {
tester.httpServer().setResponse(METRICS_RESPONSE);
@@ -205,14 +208,14 @@ public class RpcMetricsTest {
invoke(req, rpcClient, false);
}
- private JSONObject findExtraMetricsObject(String jsonResponse) throws JSONException {
- JSONArray metrics = new JSONObject(jsonResponse).getJSONArray("metrics");
- for (int i = 0; i < metrics.length(); i++) {
- JSONObject jsonObject = metrics.getJSONObject(i);
+ private JsonNode findExtraMetricsObject(String jsonResponse) throws IOException {
+ ArrayNode metrics = (ArrayNode) jsonMapper.readTree(jsonResponse).get("metrics");
+ for (int i = 0; i < metrics.size(); i++) {
+ JsonNode jsonObject = metrics.get(i);
assertTrue(jsonObject.has("application"));
- if (jsonObject.getString("application").equals(EXTRA_APP)) return jsonObject;
+ if (jsonObject.get("application").textValue().equals(EXTRA_APP)) return jsonObject;
}
- return new JSONObject();
+ return jsonMapper.createObjectNode();
}
private static String getMetricsForYamas(String service, RpcClient client) {
@@ -250,12 +253,12 @@ public class RpcMetricsTest {
return returnValue;
}
- private static void verifyStatusMessage(JSONObject jsonObject) throws JSONException {
- assertThat(jsonObject.getInt("status_code"), is(0));
- assertThat(jsonObject.getString("status_msg"), notNullValue());
- assertThat(jsonObject.getString("application"), notNullValue());
- assertThat(jsonObject.getString("routing"), notNullValue());
- assertThat(jsonObject.length(), is(4));
+ private static void verifyStatusMessage(JsonNode jsonObject) {
+ assertThat(jsonObject.get("status_code").intValue(), is(0));
+ assertThat(jsonObject.get("status_msg").textValue(), notNullValue());
+ assertThat(jsonObject.get("application").textValue(), notNullValue());
+ assertThat(jsonObject.get("routing"), notNullValue());
+ assertThat(jsonObject.size(), is(4));
}
}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java
index 0d53f988ac7..7ff179e5528 100644
--- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java
@@ -2,7 +2,6 @@
package ai.vespa.metricsproxy.service;
import ai.vespa.metricsproxy.metric.Metric;
-import org.json.JSONException;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -37,7 +36,7 @@ public class ContainerServiceTest {
}
@Test
- public void testMultipleQueryDimensions() throws JSONException {
+ public void testMultipleQueryDimensions() {
int count = 0;
VespaService service = VespaService.create("service1", "id", httpServer.port());
for (Metric m : service.getMetrics().getMetrics()) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
index 6094b497fff..46ffe14d4e2 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
@@ -161,7 +161,7 @@ public class CapacityChecker {
int timesHostCanBeRemoved = 0;
Optional<Node> unallocatedNode;
- while (timesHostCanBeRemoved < 1000) { // Arbitrary upper bound
+ while (timesHostCanBeRemoved < 100) { // Arbitrary upper bound
unallocatedNode = tryAllocateNodes(nodeChildren.get(host), hosts, resourceMap, containedAllocations);
if (unallocatedNode.isEmpty()) {
timesHostCanBeRemoved++;
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 3d61dd39b31..53ba73e4d82 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
@@ -61,12 +61,17 @@ public class GroupPreparer {
public List<Node> prepare(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
List<Node> surplusActiveNodes, MutableInteger highestIndex, int wantedGroups) {
+ String allocateOsRequirement = allocateOsRequirementFlag
+ .with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm())
+ .value();
+
// Try preparing in memory without global unallocated lock. Most of the time there should be no changes and we
// can return nodes previously allocated.
{
MutableInteger probePrepareHighestIndex = new MutableInteger(highestIndex.get());
NodeAllocation probeAllocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes,
- probePrepareHighestIndex, wantedGroups, PROBE_LOCK);
+ probePrepareHighestIndex, wantedGroups, PROBE_LOCK,
+ allocateOsRequirement);
if (probeAllocation.fulfilledAndNoChanges()) {
List<Node> acceptedNodes = probeAllocation.finalNodes();
surplusActiveNodes.removeAll(acceptedNodes);
@@ -80,10 +85,16 @@ public class GroupPreparer {
Mutex allocationLock = nodeRepository.lockUnallocated()) {
NodeAllocation allocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes,
- highestIndex, wantedGroups, allocationLock);
+ highestIndex, wantedGroups, allocationLock, allocateOsRequirement);
if (nodeRepository.zone().getCloud().dynamicProvisioning()) {
- Version osVersion = nodeRepository.osVersions().targetFor(NodeType.host).orElse(Version.emptyVersion);
+ final Version osVersion;
+ if (allocateOsRequirement.equals("rhel8")) {
+ osVersion = new Version(8);
+ } else {
+ 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(),
@@ -122,13 +133,10 @@ public class GroupPreparer {
private NodeAllocation prepareAllocation(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
List<Node> surplusActiveNodes, MutableInteger highestIndex, int wantedGroups,
- Mutex allocationLock) {
+ Mutex allocationLock, String allocateOsRequirement) {
LockedNodeList allNodes = nodeRepository.list(allocationLock);
NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requestedNodes,
highestIndex, nodeRepository);
- String allocateOsRequirement = allocateOsRequirementFlag
- .with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm())
- .value();
NodePrioritizer prioritizer = new NodePrioritizer(
allNodes, application, cluster, requestedNodes, wantedGroups,
nodeRepository.zone().getCloud().dynamicProvisioning(), nodeRepository.nameResolver(),
diff --git a/persistence/src/vespa/persistence/spi/attribute_resource_usage.h b/persistence/src/vespa/persistence/spi/attribute_resource_usage.h
index 8d28370e504..02e0c248e3d 100644
--- a/persistence/src/vespa/persistence/spi/attribute_resource_usage.h
+++ b/persistence/src/vespa/persistence/spi/attribute_resource_usage.h
@@ -29,6 +29,7 @@ public:
double get_usage() const noexcept { return _usage; }
const vespalib::string& get_name() const noexcept { return _name; }
+ bool valid() const noexcept { return !_name.empty(); }
bool operator==(const AttributeResourceUsage& rhs) const noexcept {
return ((_usage == rhs._usage) && (_name == rhs._name));
diff --git a/persistence/src/vespa/persistence/spi/bucket.h b/persistence/src/vespa/persistence/spi/bucket.h
index 30393109cc9..507cc80ad76 100644
--- a/persistence/src/vespa/persistence/spi/bucket.h
+++ b/persistence/src/vespa/persistence/spi/bucket.h
@@ -21,12 +21,12 @@ public:
explicit Bucket(const document::Bucket& b) noexcept
: _bucket(b) {}
- const document::Bucket &getBucket() const { return _bucket; }
- document::BucketId getBucketId() const { return _bucket.getBucketId(); }
- document::BucketSpace getBucketSpace() const { return _bucket.getBucketSpace(); }
+ const document::Bucket &getBucket() const noexcept { return _bucket; }
+ document::BucketId getBucketId() const noexcept { return _bucket.getBucketId(); }
+ document::BucketSpace getBucketSpace() const noexcept { return _bucket.getBucketSpace(); }
/** Convert easily to a document bucket id to make class easy to use. */
- operator document::BucketId() const { return _bucket.getBucketId(); }
+ operator document::BucketId() const noexcept { return _bucket.getBucketId(); }
bool operator==(const Bucket& o) const noexcept {
return (_bucket == o._bucket);
diff --git a/protocols/getnodestate/host_info.json b/protocols/getnodestate/host_info.json
index 7ae5b0043ff..7eddf506e63 100644
--- a/protocols/getnodestate/host_info.json
+++ b/protocols/getnodestate/host_info.json
@@ -110,7 +110,8 @@
"usage": 0.85
},
"disk": {
- "usage": 0.6
+ "usage": 0.6,
+ "name": "a cool disk"
}
}
}
diff --git a/searchcommon/src/vespa/searchcommon/common/CMakeLists.txt b/searchcommon/src/vespa/searchcommon/common/CMakeLists.txt
index 23e6e8dd394..bf5686280e6 100644
--- a/searchcommon/src/vespa/searchcommon/common/CMakeLists.txt
+++ b/searchcommon/src/vespa/searchcommon/common/CMakeLists.txt
@@ -1,7 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
vespa_add_library(searchcommon_searchcommon_common OBJECT
SOURCES
+ compaction_strategy.cpp
datatype.cpp
+ growstrategy.cpp
schema.cpp
schemaconfigurer.cpp
DEPENDS
diff --git a/searchcommon/src/vespa/searchcommon/common/compaction_strategy.cpp b/searchcommon/src/vespa/searchcommon/common/compaction_strategy.cpp
new file mode 100644
index 00000000000..c3377ed5857
--- /dev/null
+++ b/searchcommon/src/vespa/searchcommon/common/compaction_strategy.cpp
@@ -0,0 +1,15 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "compaction_strategy.h"
+#include <iostream>
+namespace search {
+
+std::ostream& operator<<(std::ostream& os, const CompactionStrategy& compaction_strategy)
+{
+ os << "{maxDeadBytesRatio=" << compaction_strategy.getMaxDeadBytesRatio() <<
+ ", maxDeadAddressSpaceRatio=" << compaction_strategy.getMaxDeadAddressSpaceRatio() <<
+ "}";
+ return os;
+}
+
+}
diff --git a/searchcommon/src/vespa/searchcommon/common/compaction_strategy.h b/searchcommon/src/vespa/searchcommon/common/compaction_strategy.h
index 0b3caf44481..ae354a4c4d2 100644
--- a/searchcommon/src/vespa/searchcommon/common/compaction_strategy.h
+++ b/searchcommon/src/vespa/searchcommon/common/compaction_strategy.h
@@ -3,6 +3,7 @@
#pragma once
#include <stdint.h>
+#include <iosfwd>
namespace search {
@@ -34,4 +35,6 @@ public:
bool operator!=(const CompactionStrategy & rhs) const { return !(operator==(rhs)); }
};
+std::ostream& operator<<(std::ostream& os, const CompactionStrategy& compaction_strategy);
+
} // namespace search
diff --git a/searchcommon/src/vespa/searchcommon/common/growstrategy.cpp b/searchcommon/src/vespa/searchcommon/common/growstrategy.cpp
new file mode 100644
index 00000000000..534be3060a7
--- /dev/null
+++ b/searchcommon/src/vespa/searchcommon/common/growstrategy.cpp
@@ -0,0 +1,18 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "growstrategy.h"
+#include <iostream>
+
+namespace search {
+
+std::ostream& operator<<(std::ostream& os, const GrowStrategy& grow_strategy)
+{
+ os << "{docsInitialCapacity=" << grow_strategy.getDocsInitialCapacity() <<
+ ", docsGrowFactor=" << grow_strategy.getDocsGrowFactor() <<
+ ", docsGrowDelta=" << grow_strategy.getDocsGrowDelta() <<
+ ", multiValueAllocGrowFactor=" << grow_strategy.getMultiValueAllocGrowFactor() <<
+ "}";
+ return os;
+}
+
+}
diff --git a/searchcommon/src/vespa/searchcommon/common/growstrategy.h b/searchcommon/src/vespa/searchcommon/common/growstrategy.h
index 4194541b8e5..f7c5c030d95 100644
--- a/searchcommon/src/vespa/searchcommon/common/growstrategy.h
+++ b/searchcommon/src/vespa/searchcommon/common/growstrategy.h
@@ -4,6 +4,7 @@
#include <vespa/vespalib/util/growstrategy.h>
#include <cstdint>
+#include <iosfwd>
namespace search {
@@ -18,10 +19,10 @@ public:
GrowStrategy()
: GrowStrategy(1024, 0.5, 0, 0.2)
{}
- GrowStrategy(uint32_t docsInitialCapacity, float docsGrowPercent,
+ GrowStrategy(uint32_t docsInitialCapacity, float docsGrowFactor,
uint32_t docsGrowDelta, float multiValueAllocGrowFactor)
: _docsInitialCapacity(docsInitialCapacity),
- _docsGrowFactor(docsGrowPercent),
+ _docsGrowFactor(docsGrowFactor),
_docsGrowDelta(docsGrowDelta),
_multiValueAllocGrowFactor(multiValueAllocGrowFactor)
{
@@ -54,5 +55,7 @@ public:
}
};
+std::ostream& operator<<(std::ostream& os, const GrowStrategy& grow_strategy);
+
}
diff --git a/searchcore/CMakeLists.txt b/searchcore/CMakeLists.txt
index 04cface5d40..d77bc69ba62 100644
--- a/searchcore/CMakeLists.txt
+++ b/searchcore/CMakeLists.txt
@@ -69,6 +69,7 @@ vespa_define_module(
src/tests/proton/attribute/imported_attributes_repo
src/tests/proton/bucketdb/bucketdb
src/tests/proton/common
+ src/tests/proton/common/alloc_config
src/tests/proton/common/attribute_updater
src/tests/proton/common/document_type_inspector
src/tests/proton/common/hw_info_sampler
diff --git a/searchcore/src/apps/tests/persistenceconformance_test.cpp b/searchcore/src/apps/tests/persistenceconformance_test.cpp
index 4715ff80d03..b47a1954c6f 100644
--- a/searchcore/src/apps/tests/persistenceconformance_test.cpp
+++ b/searchcore/src/apps/tests/persistenceconformance_test.cpp
@@ -13,6 +13,7 @@
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/document/test/make_bucket_space.h>
#include <vespa/searchcommon/common/schemaconfigurer.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/proton/matching/querylimiter.h>
#include <vespa/searchcore/proton/metrics/metricswireservice.h>
@@ -145,6 +146,7 @@ public:
std::make_shared<DocumentDBMaintenanceConfig>(),
search::LogDocumentStore::Config(),
std::make_shared<const ThreadingServiceConfig>(ThreadingServiceConfig::make(1)),
+ std::make_shared<const AllocConfig>(),
"client",
docTypeName.getName());
}
diff --git a/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp b/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp
index 90942e9aef4..6cca2e4bd48 100644
--- a/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp
+++ b/searchcore/src/apps/vespa-feed-bm/vespa_feed_bm.cpp
@@ -38,6 +38,7 @@
#include <vespa/messagebus/testlib/slobrok.h>
#include <vespa/metrics/config-metricsmanager.h>
#include <vespa/searchcommon/common/schemaconfigurer.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/proton/matching/querylimiter.h>
#include <vespa/searchcore/proton/metrics/metricswireservice.h>
@@ -198,6 +199,7 @@ std::shared_ptr<DocumentDBConfig> make_document_db_config(std::shared_ptr<Docume
std::make_shared<DocumentDBMaintenanceConfig>(),
search::LogDocumentStore::Config(),
std::make_shared<const ThreadingServiceConfig>(ThreadingServiceConfig::make(1)),
+ std::make_shared<const AllocConfig>(),
"client",
doc_type_name.getName());
}
diff --git a/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp b/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp
index d25a234c6f8..3987a8685ea 100644
--- a/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_manager/attribute_manager_test.cpp
@@ -232,8 +232,7 @@ struct ParallelAttributeManager
InitializerTask::SP documentMetaStoreInitTask;
BucketDBOwner::SP bucketDbOwner;
DocumentMetaStore::SP documentMetaStore;
- search::GrowStrategy attributeGrow;
- size_t attributeGrowNumDocs;
+ AllocStrategy alloc_strategy;
bool fastAccessAttributesOnly;
std::shared_ptr<AttributeManager::SP> mgr;
vespalib::ThreadStackExecutor masterExecutor;
@@ -250,15 +249,14 @@ ParallelAttributeManager::ParallelAttributeManager(search::SerialNum configSeria
: documentMetaStoreInitTask(std::make_shared<DummyInitializerTask>()),
bucketDbOwner(std::make_shared<BucketDBOwner>()),
documentMetaStore(std::make_shared<DocumentMetaStore>(bucketDbOwner)),
- attributeGrow(),
- attributeGrowNumDocs(1),
+ alloc_strategy(),
fastAccessAttributesOnly(false),
mgr(std::make_shared<AttributeManager::SP>()),
masterExecutor(1, 128 * 1024),
master(masterExecutor),
initializer(std::make_shared<AttributeManagerInitializer>(configSerialNum, documentMetaStoreInitTask,
documentMetaStore, baseAttrMgr, attrCfg,
- attributeGrow, attributeGrowNumDocs,
+ alloc_strategy,
fastAccessAttributesOnly, master, mgr))
{
documentMetaStore->setCommittedDocIdLimit(docIdLimit);
diff --git a/searchcore/src/tests/proton/attribute/attribute_test.cpp b/searchcore/src/tests/proton/attribute/attribute_test.cpp
index b67eaec1a8d..ebd3e27aeca 100644
--- a/searchcore/src/tests/proton/attribute/attribute_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_test.cpp
@@ -535,7 +535,7 @@ public:
AttributeCollectionSpecFactory _factory;
AttributeCollectionSpecTest(bool fastAccessOnly)
: _builder(),
- _factory(search::GrowStrategy(), 100, fastAccessOnly)
+ _factory(AllocStrategy(search::GrowStrategy(), search::CompactionStrategy(), 100), fastAccessOnly)
{
addAttribute("a1", false);
addAttribute("a2", true);
diff --git a/searchcore/src/tests/proton/attribute/attribute_usage_filter/attribute_usage_filter_test.cpp b/searchcore/src/tests/proton/attribute/attribute_usage_filter/attribute_usage_filter_test.cpp
index 0fced6e0bff..b35027eac2a 100644
--- a/searchcore/src/tests/proton/attribute/attribute_usage_filter/attribute_usage_filter_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attribute_usage_filter/attribute_usage_filter_test.cpp
@@ -3,9 +3,13 @@
LOG_SETUP("attribute_usage_filter_test");
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/searchcore/proton/attribute/attribute_usage_filter.h>
+#include <vespa/searchcore/proton/attribute/i_attribute_usage_listener.h>
using proton::AttributeUsageFilter;
using proton::AttributeUsageStats;
+using proton::IAttributeUsageListener;
+using search::AddressSpaceUsage;
+using vespalib::AddressSpace;
namespace
{
@@ -38,33 +42,47 @@ public:
}
};
+class MyListener : public IAttributeUsageListener {
+public:
+ AttributeUsageStats stats;
+ MyListener() : stats() {}
+ void notify_attribute_usage(const AttributeUsageStats &stats_in) override {
+ stats = stats_in;
+ }
+};
+
struct Fixture
{
- AttributeUsageFilter _filter;
+ AttributeUsageFilter filter;
+ const MyListener* listener;
using State = AttributeUsageFilter::State;
using Config = AttributeUsageFilter::Config;
Fixture()
- : _filter()
+ : filter(),
+ listener()
{
+ auto my_listener = std::make_unique<MyListener>();
+ listener = my_listener.get();
+ filter.set_listener(std::move(my_listener));
}
void testWrite(const vespalib::string &exp) {
if (exp.empty()) {
- EXPECT_TRUE(_filter.acceptWriteOperation());
- State state = _filter.getAcceptState();
+ EXPECT_TRUE(filter.acceptWriteOperation());
+ State state = filter.getAcceptState();
EXPECT_TRUE(state.acceptWriteOperation());
EXPECT_EQUAL(exp, state.message());
} else {
- EXPECT_FALSE(_filter.acceptWriteOperation());
- State state = _filter.getAcceptState();
+ EXPECT_FALSE(filter.acceptWriteOperation());
+ State state = filter.getAcceptState();
EXPECT_FALSE(state.acceptWriteOperation());
EXPECT_EQUAL(exp, state.message());
}
}
void setAttributeStats(const AttributeUsageStats &stats) {
- _filter.setAttributeStats(stats);
+ filter.setAttributeStats(stats);
}
};
@@ -78,7 +96,7 @@ TEST_F("Check that default filter allows write", Fixture)
TEST_F("Check that enum store limit can be reached", Fixture)
{
- f._filter.setConfig(Fixture::Config(0.8, 1.0));
+ f.filter.setConfig(Fixture::Config(0.8, 1.0));
MyAttributeStats stats;
stats.triggerEnumStoreLimit();
f.setAttributeStats(stats);
@@ -95,7 +113,7 @@ TEST_F("Check that enum store limit can be reached", Fixture)
TEST_F("Check that multivalue limit can be reached", Fixture)
{
- f._filter.setConfig(Fixture::Config(1.0, 0.8));
+ f.filter.setConfig(Fixture::Config(1.0, 0.8));
MyAttributeStats stats;
stats.triggerMultiValueLimit();
f.setAttributeStats(stats);
@@ -113,7 +131,7 @@ TEST_F("Check that multivalue limit can be reached", Fixture)
TEST_F("Check that both enumstore limit and multivalue limit can be reached",
Fixture)
{
- f._filter.setConfig(Fixture::Config(0.8, 0.8));
+ f.filter.setConfig(Fixture::Config(0.8, 0.8));
MyAttributeStats stats;
stats.triggerEnumStoreLimit();
stats.triggerMultiValueLimit();
@@ -139,4 +157,13 @@ TEST_F("Check that both enumstore limit and multivalue limit can be reached",
"attributeName: \"multiValueName\", subdb: \"ready\"}");
}
+TEST_F("listener is updated when attribute stats change", Fixture)
+{
+ AttributeUsageStats stats;
+ AddressSpaceUsage usage(AddressSpace(12, 10, 15), AddressSpace(22, 20, 25));
+ stats.merge(usage, "my_attr", "my_subdb");
+ f.setAttributeStats(stats);
+ EXPECT_EQUAL(stats, f.listener->stats);
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchcore/src/tests/proton/attribute/attributeflush_test.cpp b/searchcore/src/tests/proton/attribute/attributeflush_test.cpp
index 8a814e25bd5..d6c40b0d70d 100644
--- a/searchcore/src/tests/proton/attribute/attributeflush_test.cpp
+++ b/searchcore/src/tests/proton/attribute/attributeflush_test.cpp
@@ -18,6 +18,7 @@
#include <vespa/vespalib/util/foreground_thread_executor.h>
#include <vespa/vespalib/util/foregroundtaskexecutor.h>
#include <vespa/vespalib/util/threadstackexecutor.h>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("attributeflush_test");
diff --git a/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp b/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp
index e284f5ed1d9..cb64a1a59d8 100644
--- a/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp
+++ b/searchcore/src/tests/proton/attribute/exclusive_attribute_read_accessor/exclusive_attribute_read_accessor_test.cpp
@@ -40,7 +40,7 @@ TEST_F("require that attribute write thread is blocked while guard is held", Fix
ReadGuard::UP guard = f.accessor.takeGuard();
Gate gate;
f.writer->execute(f.writer->getExecutorIdFromName(f.attribute->getNamePrefix()), [&gate]() { gate.countDown(); });
- bool reachedZero = gate.await(100);
+ bool reachedZero = gate.await(100ms);
EXPECT_FALSE(reachedZero);
EXPECT_EQUAL(1u, gate.getCount());
diff --git a/searchcore/src/tests/proton/common/alloc_config/CMakeLists.txt b/searchcore/src/tests/proton/common/alloc_config/CMakeLists.txt
new file mode 100644
index 00000000000..26a2dc72cc2
--- /dev/null
+++ b/searchcore/src/tests/proton/common/alloc_config/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(searchcore_alloc_config_test_app TEST
+ SOURCES
+ alloc_config_test.cpp
+ DEPENDS
+ searchcore_pcommon
+ GTest::GTest
+)
+vespa_add_test(NAME searchcore_alloc_config_test_app COMMAND searchcore_alloc_config_test_app)
diff --git a/searchcore/src/tests/proton/common/alloc_config/alloc_config_test.cpp b/searchcore/src/tests/proton/common/alloc_config/alloc_config_test.cpp
new file mode 100644
index 00000000000..18a0ee47a47
--- /dev/null
+++ b/searchcore/src/tests/proton/common/alloc_config/alloc_config_test.cpp
@@ -0,0 +1,35 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchcore/proton/common/alloc_config.h>
+#include <vespa/searchcore/proton/common/subdbtype.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using proton::AllocConfig;
+using proton::AllocStrategy;
+using proton::SubDbType;
+using search::CompactionStrategy;
+using search::GrowStrategy;
+
+namespace {
+
+CompactionStrategy baseline_compaction_strategy(0.2, 0.25);
+
+GrowStrategy make_grow_strategy(uint32_t initial_docs) {
+ return GrowStrategy(initial_docs, 0.1, 1, 0.15);
+}
+
+AllocStrategy make_alloc_strategy(uint32_t initial_docs) {
+ return AllocStrategy(make_grow_strategy(initial_docs), baseline_compaction_strategy, 10000);
+}
+
+};
+
+TEST(AllocConfigTest, can_make_allocation_strategy_for_sub_dbs)
+{
+ AllocConfig config(make_alloc_strategy(10000000), 5, 2);
+ EXPECT_EQ(make_alloc_strategy(20000000), config.make_alloc_strategy(SubDbType::READY));
+ EXPECT_EQ(make_alloc_strategy(100000), config.make_alloc_strategy(SubDbType::REMOVED));
+ EXPECT_EQ(make_alloc_strategy(30000000), config.make_alloc_strategy(SubDbType::NOTREADY));
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp b/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
index d231040aeda..8f6b2f0bc10 100644
--- a/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/configurer/configurer_test.cpp
@@ -613,6 +613,12 @@ TEST("require that attribute manager (imported attributes) should change when vi
EXPECT_TRUE(params.shouldAttributeManagerChange());
}
+TEST("require that attribute manager should change when alloc config has changed")
+{
+ ReconfigParams params(CCR().set_alloc_config_changed(true));
+ EXPECT_TRUE(params.shouldAttributeManagerChange());
+}
+
void
assertMaintenanceControllerShouldNotChange(DocumentDBConfig::ComparisonResult result)
{
@@ -684,6 +690,7 @@ TEST("require that subdbs should change if relevant config changed")
TEST_DO(assertSubDbsShouldChange(CCR().setRankingConstantsChanged(true)));
TEST_DO(assertSubDbsShouldChange(CCR().setOnnxModelsChanged(true)));
TEST_DO(assertSubDbsShouldChange(CCR().setSchemaChanged(true)));
+ TEST_DO(assertSubDbsShouldChange(CCR().set_alloc_config_changed(true)));
}
TEST_MAIN()
diff --git a/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp b/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
index 9cdd984b152..40a9656ae41 100644
--- a/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/document_subdbs/document_subdbs_test.cpp
@@ -123,8 +123,7 @@ struct MyStoreOnlyConfig
: _cfg(DocTypeName(DOCTYPE_NAME),
SUB_NAME,
BASE_DIR,
- search::GrowStrategy(),
- 0, 0, SubDbType::READY)
+ 0, SubDbType::READY)
{
}
};
diff --git a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
index 0cc9a92895b..a952efdecdc 100644
--- a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
@@ -10,6 +10,7 @@
#include <vespa/searchcore/proton/server/idocumentmovehandler.h>
#include <vespa/searchcore/proton/server/imaintenancejobrunner.h>
#include <vespa/searchcore/proton/server/maintenancedocumentsubdb.h>
+#include <vespa/searchcore/proton/server/ibucketmodifiedhandler.h>
#include <vespa/searchcore/proton/test/buckethandler.h>
#include <vespa/searchcore/proton/test/clusterstatehandler.h>
#include <vespa/searchcore/proton/test/disk_mem_usage_notifier.h>
@@ -43,15 +44,8 @@ using BucketIdSet = std::set<BucketId>;
using BucketIdVector = BucketId::List;
using DocumentVector = std::vector<Document::SP>;
using MoveOperationVector = std::vector<MoveOperation>;
-using ScanItr = BucketMoveJob::ScanIterator;
-using ScanPos = BucketMoveJob::ScanPosition;
-
-namespace {
-
-const uint32_t FIRST_SCAN_PASS = 1;
-const uint32_t SECOND_SCAN_PASS = 2;
-
-}
+using ScanItr = bucketdb::ScanIterator;
+using ScanPass = ScanItr::Pass;
struct MyMoveOperationLimiter : public IMoveOperationLimiter {
uint32_t beginOpCount;
@@ -346,9 +340,8 @@ struct ScanFixtureBase
return ScanItr(_bucketDB->takeGuard(), BucketId());
}
- ScanItr getItr(BucketId bucket, BucketId endBucket = BucketId(), uint32_t pass = FIRST_SCAN_PASS) {
- return ScanItr(_bucketDB->takeGuard(), pass,
- bucket, endBucket);
+ ScanItr getItr(BucketId bucket, BucketId endBucket = BucketId(), ScanPass pass = ScanPass::FIRST) {
+ return ScanItr(_bucketDB->takeGuard(), pass, bucket, endBucket);
}
};
@@ -451,12 +444,12 @@ TEST_F("require that we can iterate from the middle of not ready buckets", ScanF
{
BucketId bucket = f._notReady.bucket(2);
{
- ScanItr itr = f.getItr(bucket, bucket, FIRST_SCAN_PASS);
+ ScanItr itr = f.getItr(bucket, bucket, ScanPass::FIRST);
assertEquals(BucketVector().
add(f._notReady.bucket(4)), itr, SubDbType::NOTREADY);
}
{
- ScanItr itr = f.getItr(BucketId(), bucket, SECOND_SCAN_PASS);
+ ScanItr itr = f.getItr(BucketId(), bucket, ScanPass::SECOND);
assertEquals(BucketVector().
add(f._notReady.bucket(2)), itr, SubDbType::NOTREADY);
}
@@ -478,12 +471,12 @@ TEST_F("require that we can iterate from the middle of ready buckets", ScanFixtu
add(f._notReady.bucket(4)), itr, SubDbType::NOTREADY);
}
{
- ScanItr itr = f.getItr(bucket, bucket, FIRST_SCAN_PASS);
+ ScanItr itr = f.getItr(bucket, bucket, ScanPass::FIRST);
assertEquals(BucketVector().
add(f._ready.bucket(8)), itr, SubDbType::READY);
}
{
- ScanItr itr = f.getItr(BucketId(), bucket, SECOND_SCAN_PASS);
+ ScanItr itr = f.getItr(BucketId(), bucket, ScanPass::SECOND);
assertEquals(BucketVector().
add(f._ready.bucket(6)), itr, SubDbType::READY);
}
diff --git a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
index 1127191a1a2..8840d4778e0 100644
--- a/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/feedhandler/feedhandler_test.cpp
@@ -78,20 +78,20 @@ struct Rendezvous {
vespalib::Gate gone;
typedef std::unique_ptr<Rendezvous> UP;
Rendezvous() : enter(), leave(), gone() {}
- bool run(uint32_t timeout = 80000) {
+ bool run(vespalib::duration timeout = 80s) {
enter.countDown();
bool retval = leave.await(timeout);
gone.countDown();
return retval;
}
- bool waitForEnter(uint32_t timeout = 80000) {
+ bool waitForEnter(vespalib::duration timeout = 80s) {
return enter.await(timeout);
}
- bool leaveAndWait(uint32_t timeout = 80000) {
+ bool leaveAndWait(vespalib::duration timeout = 80s) {
leave.countDown();
return gone.await(timeout);
}
- bool await(uint32_t timeout = 80000) {
+ bool await(vespalib::duration timeout = 80s) {
if (waitForEnter(timeout)) {
return leaveAndWait(timeout);
}
@@ -373,7 +373,7 @@ struct FeedTokenContext {
FeedTokenContext();
~FeedTokenContext();
- bool await(uint32_t timeout = 80000) { return transport.gate.await(timeout); }
+ bool await(vespalib::duration timeout = 80s) { return transport.gate.await(timeout); }
const Result *getResult() {
if (transport.result.get()) {
return transport.result.get();
@@ -399,36 +399,6 @@ struct PutContext {
{}
};
-
-struct PutHandler {
- FeedHandler &handler;
- DocBuilder &builder;
- Timestamp timestamp;
- std::vector<PutContext::SP> puts;
- PutHandler(FeedHandler &fh, DocBuilder &db) :
- handler(fh),
- builder(db),
- timestamp(0),
- puts()
- {}
- void put(const vespalib::string &docId) {
- PutContext::SP pc(new PutContext(docId, builder));
- FeedOperation::UP op(new PutOperation(pc->docCtx.bucketId, timestamp, pc->docCtx.doc));
- handler.handleOperation(pc->tokenCtx.token, std::move(op));
- timestamp = Timestamp(timestamp + 1);
- puts.push_back(pc);
- }
- bool await(uint32_t timeout = 80000) {
- for (const auto & put : puts) {
- if (!put->tokenCtx.await(timeout)) {
- return false;
- }
- }
- return true;
- }
-};
-
-
struct MyTlsWriter : TlsWriter {
int store_count;
int erase_count;
diff --git a/searchcore/src/tests/proton/documentdb/job_tracked_maintenance_job/job_tracked_maintenance_job_test.cpp b/searchcore/src/tests/proton/documentdb/job_tracked_maintenance_job/job_tracked_maintenance_job_test.cpp
index 2bde5761aea..c12b91c4c1e 100644
--- a/searchcore/src/tests/proton/documentdb/job_tracked_maintenance_job/job_tracked_maintenance_job_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/job_tracked_maintenance_job/job_tracked_maintenance_job_test.cpp
@@ -24,7 +24,7 @@ getGateVector(size_t size)
{
GateVector retval;
for (size_t i = 0; i < size; ++i) {
- retval.push_back(GateUP(new Gate()));
+ retval.push_back(std::make_unique<Gate>());
}
return retval;
}
@@ -46,7 +46,7 @@ struct MyMaintenanceJob : public IBlockableMaintenanceJob
void unBlock(BlockedReason) override { _blocked = false; }
bool isBlocked() const override { return _blocked; }
bool run() override {
- _runGates[_runIdx++]->await(5000);
+ _runGates[_runIdx++]->await(5s);
return _runIdx == _runGates.size();
}
};
@@ -82,10 +82,10 @@ struct Fixture
}
void runJobAndWait(size_t runIdx, size_t startedGateCount, size_t endedGateCount) {
_exec.execute(vespalib::makeLambdaTask([this]() { runJob(); }));
- _tracker->_started.await(5000);
+ _tracker->_started.await(5s);
assertTracker(startedGateCount, endedGateCount);
_myJob->_runGates[runIdx]->countDown();
- _runGates[runIdx]->await(5000);
+ _runGates[runIdx]->await(5s);
}
};
diff --git a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
index c6ffa167dbf..44afe1ddd75 100644
--- a/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/maintenancecontroller/maintenancecontroller_test.cpp
@@ -72,8 +72,7 @@ using BlockedReason = IBlockableMaintenanceJob::BlockedReason;
typedef BucketId::List BucketIdVector;
typedef std::set<BucketId> BucketIdSet;
-constexpr int TIMEOUT_MS = 60000;
-constexpr vespalib::duration TIMEOUT_SEC = 60s;
+constexpr vespalib::duration TIMEOUT = 60s;
namespace {
@@ -947,14 +946,14 @@ TEST_F("require that bucket move controller is active", MaintenanceControllerFix
EXPECT_EQUAL(5u, f._notReady.getNumUsedLids());
EXPECT_EQUAL(5u, f._notReady.getDocumentCount());
f.startMaintenance();
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(0u, f._ready.getNumUsedLids());
EXPECT_EQUAL(0u, f._ready.getDocumentCount());
EXPECT_EQUAL(10u, f._notReady.getNumUsedLids());
EXPECT_EQUAL(10u, f._notReady.getDocumentCount());
f._calc->addReady(bucketId1);
f.notifyClusterStateChanged();
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(3u, f._ready.getNumUsedLids());
EXPECT_EQUAL(3u, f._ready.getDocumentCount());
EXPECT_EQUAL(7u, f._notReady.getNumUsedLids());
@@ -963,13 +962,13 @@ TEST_F("require that bucket move controller is active", MaintenanceControllerFix
f._calc->addReady(bucketId2);
f._calc->addReady(bucketId4);
f.notifyClusterStateChanged();
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(6u, f._ready.getNumUsedLids());
EXPECT_EQUAL(6u, f._ready.getDocumentCount());
EXPECT_EQUAL(4u, f._notReady.getNumUsedLids());
EXPECT_EQUAL(4u, f._notReady.getDocumentCount());
frozen2.reset();
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(8u, f._ready.getNumUsedLids());
EXPECT_EQUAL(8u, f._ready.getDocumentCount());
EXPECT_EQUAL(2u, f._notReady.getNumUsedLids());
@@ -997,14 +996,14 @@ TEST_F("require that document pruner is active", MaintenanceControllerFixture)
EXPECT_EQUAL(10u, f._removed.getNumUsedLids());
EXPECT_EQUAL(10u, f._removed.getDocumentCount());
f.startMaintenance();
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(10u, f._removed.getNumUsedLids());
EXPECT_EQUAL(10u, f._removed.getDocumentCount());
MyFrozenBucket::UP frozen3(new MyFrozenBucket(f._mc, bucketId3));
f.setPruneConfig(DocumentDBPruneRemovedDocumentsConfig(200ms, 900s));
for (uint32_t i = 0; i < 6; ++i) {
std::this_thread::sleep_for(100ms);
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
if (f._removed.getNumUsedLids() != 10u)
break;
}
@@ -1013,7 +1012,7 @@ TEST_F("require that document pruner is active", MaintenanceControllerFixture)
frozen3.reset();
for (uint32_t i = 0; i < 600; ++i) {
std::this_thread::sleep_for(100ms);
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
if (f._removed.getNumUsedLids() != 10u)
break;
}
@@ -1075,7 +1074,7 @@ TEST_F("require that active bucket is not moved until de-activated", Maintenance
EXPECT_EQUAL(5u, f._notReady.getDocumentCount());
f.startMaintenance();
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(5u, f._ready.getNumUsedLids());
EXPECT_EQUAL(5u, f._ready.getDocumentCount());
EXPECT_EQUAL(5u, f._notReady.getNumUsedLids());
@@ -1084,7 +1083,7 @@ TEST_F("require that active bucket is not moved until de-activated", Maintenance
// de-activate bucket 1
f._ready.setBucketState(readyDocs.getBucket(1), false);
f.notifyBucketStateChanged(readyDocs.getBucket(1), BucketInfo::NOT_ACTIVE);
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(2u, f._ready.getNumUsedLids());
EXPECT_EQUAL(2u, f._ready.getDocumentCount());
EXPECT_EQUAL(8u, f._notReady.getNumUsedLids());
@@ -1093,7 +1092,7 @@ TEST_F("require that active bucket is not moved until de-activated", Maintenance
// re-activate bucket 1
f._ready.setBucketState(readyDocs.getBucket(1), true);
f.notifyBucketStateChanged(readyDocs.getBucket(1), BucketInfo::ACTIVE);
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(5u, f._ready.getNumUsedLids());
EXPECT_EQUAL(5u, f._ready.getDocumentCount());
EXPECT_EQUAL(5u, f._notReady.getNumUsedLids());
@@ -1102,7 +1101,7 @@ TEST_F("require that active bucket is not moved until de-activated", Maintenance
// de-activate bucket 1
f._ready.setBucketState(readyDocs.getBucket(1), false);
f.notifyBucketStateChanged(readyDocs.getBucket(1), BucketInfo::NOT_ACTIVE);
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(2u, f._ready.getNumUsedLids());
EXPECT_EQUAL(2u, f._ready.getDocumentCount());
EXPECT_EQUAL(8u, f._notReady.getNumUsedLids());
@@ -1111,7 +1110,7 @@ TEST_F("require that active bucket is not moved until de-activated", Maintenance
// re-activate bucket 1
f._ready.setBucketState(readyDocs.getBucket(1), true);
f.notifyBucketStateChanged(readyDocs.getBucket(1), BucketInfo::ACTIVE);
- ASSERT_TRUE(f._executor.waitIdle(TIMEOUT_SEC));
+ ASSERT_TRUE(f._executor.waitIdle(TIMEOUT));
EXPECT_EQUAL(5u, f._ready.getNumUsedLids());
EXPECT_EQUAL(5u, f._ready.getDocumentCount());
EXPECT_EQUAL(5u, f._notReady.getNumUsedLids());
@@ -1125,19 +1124,19 @@ TEST_F("require that a simple maintenance job is executed", MaintenanceControlle
f._mc.registerJobInMasterThread(std::move(job));
f._injectDefaultJobs = false;
f.startMaintenance();
- bool done = myJob._latch.await(TIMEOUT_MS);
+ bool done = myJob._latch.await(TIMEOUT);
EXPECT_TRUE(done);
EXPECT_EQUAL(0u, myJob._latch.getCount());
}
TEST_F("require that a split maintenance job is executed", MaintenanceControllerFixture)
{
- auto job = std::make_unique<MySplitJob>(200ms, TIMEOUT_SEC * 2, 3);
+ auto job = std::make_unique<MySplitJob>(200ms, TIMEOUT * 2, 3);
MySplitJob &myJob = *job;
f._mc.registerJobInMasterThread(std::move(job));
f._injectDefaultJobs = false;
f.startMaintenance();
- bool done = myJob._latch.await(TIMEOUT_MS);
+ bool done = myJob._latch.await(TIMEOUT);
EXPECT_TRUE(done);
EXPECT_EQUAL(0u, myJob._latch.getCount());
}
@@ -1145,9 +1144,9 @@ TEST_F("require that a split maintenance job is executed", MaintenanceController
TEST_F("require that a blocked job is unblocked and executed after thaw bucket",
MaintenanceControllerFixture)
{
- auto job1 = std::make_unique<MySimpleJob>(TIMEOUT_SEC * 2, TIMEOUT_SEC * 2, 1);
+ auto job1 = std::make_unique<MySimpleJob>(TIMEOUT * 2, TIMEOUT * 2, 1);
MySimpleJob &myJob1 = *job1;
- auto job2 = std::make_unique< MySimpleJob>(TIMEOUT_SEC * 2, TIMEOUT_SEC * 2, 0);
+ auto job2 = std::make_unique< MySimpleJob>(TIMEOUT * 2, TIMEOUT * 2, 0);
MySimpleJob &myJob2 = *job2;
f._mc.registerJobInMasterThread(std::move(job1));
f._mc.registerJobInMasterThread(std::move(job2));
@@ -1169,7 +1168,7 @@ TEST_F("require that a blocked job is unblocked and executed after thaw bucket",
f._executor.sync();
EXPECT_FALSE(myJob1.isBlocked());
EXPECT_FALSE(myJob2.isBlocked());
- bool done1 = myJob1._latch.await(TIMEOUT_MS);
+ bool done1 = myJob1._latch.await(TIMEOUT);
EXPECT_TRUE(done1);
std::this_thread::sleep_for(2s);
EXPECT_EQUAL(0u, myJob2._runCnt);
@@ -1190,14 +1189,14 @@ TEST_F("require that blocked jobs are not executed", MaintenanceControllerFixtur
TEST_F("require that maintenance controller state list jobs", MaintenanceControllerFixture)
{
{
- IMaintenanceJob::UP job1(new MySimpleJob(TIMEOUT_SEC * 2, TIMEOUT_SEC * 2, 0));
- IMaintenanceJob::UP job2(new MyLongRunningJob(200ms, 200ms));
+ auto job1 = std::make_unique<MySimpleJob>(TIMEOUT * 2, TIMEOUT * 2, 0);
+ auto job2 = std::make_unique<MyLongRunningJob>(200ms, 200ms);
auto &longRunningJob = dynamic_cast<MyLongRunningJob &>(*job2);
f._mc.registerJobInMasterThread(std::move(job1));
f._mc.registerJobInMasterThread(std::move(job2));
f._injectDefaultJobs = false;
f.startMaintenance();
- longRunningJob._firstRun.await(TIMEOUT_MS);
+ longRunningJob._firstRun.await(TIMEOUT);
}
MaintenanceControllerExplorer explorer(f._mc.getJobList());
diff --git a/searchcore/src/tests/proton/flushengine/flushengine_test.cpp b/searchcore/src/tests/proton/flushengine/flushengine_test.cpp
index a675a45aa54..d5823a8e055 100644
--- a/searchcore/src/tests/proton/flushengine/flushengine_test.cpp
+++ b/searchcore/src/tests/proton/flushengine/flushengine_test.cpp
@@ -14,7 +14,7 @@
#include <vespa/vespalib/test/insertion_operators.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <mutex>
-#include <chrono>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("flushengine_test");
@@ -31,9 +31,9 @@ using searchcorespi::IFlushTarget;
using searchcorespi::FlushTask;
using vespalib::Slime;
-const long LONG_TIMEOUT = 66666;
-const long SHORT_TIMEOUT = 1;
-const uint32_t IINTERVAL = 1000;
+constexpr vespalib::duration LONG_TIMEOUT = 66666ms;
+constexpr vespalib::duration SHORT_TIMEOUT = 1ms;
+constexpr vespalib::duration IINTERVAL = 1s;
class SimpleExecutor : public vespalib::Executor {
public:
@@ -42,8 +42,7 @@ public:
public:
SimpleExecutor()
: _done()
- {
- }
+ { }
Task::UP
execute(Task::UP task) override
@@ -83,8 +82,7 @@ public:
SimpleHandler &handler)
: _task(std::move(task)),
_handler(handler)
- {
- }
+ { }
search::SerialNum getFlushSerial() const override {
return _task->getFlushSerial();
@@ -95,19 +93,15 @@ class WrappedFlushTarget : public FlushTargetProxy
{
SimpleHandler &_handler;
public:
- WrappedFlushTarget(const IFlushTarget::SP &target,
- SimpleHandler &handler)
+ WrappedFlushTarget(const IFlushTarget::SP &target, SimpleHandler &handler)
: FlushTargetProxy(target),
_handler(handler)
- {
- }
+ { }
- Task::UP initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override
- {
+ Task::UP initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override {
Task::UP task(_target->initFlush(currentSerial, std::move(flush_token)));
if (task) {
- return std::make_unique<WrappedFlushTask>(std::move(task),
- _handler);
+ return std::make_unique<WrappedFlushTask>(std::move(task), _handler);
}
return task;
}
@@ -140,33 +134,25 @@ public:
_lock(),
_done(targets.size()),
_flushDoneHistory()
- {
- }
+ { }
- search::SerialNum
- getCurrentSerialNumber() const override
- {
- LOG(info, "SimpleHandler(%s)::getCurrentSerialNumber()",
- getName().c_str());
+ search::SerialNum getCurrentSerialNumber() const override {
+ LOG(info, "SimpleHandler(%s)::getCurrentSerialNumber()", getName().c_str());
return _currentSerial;
}
std::vector<IFlushTarget::SP>
- getFlushTargets() override
- {
- LOG(info, "SimpleHandler(%s)::getFlushTargets()",
- getName().c_str());
+ getFlushTargets() override {
+ LOG(info, "SimpleHandler(%s)::getFlushTargets()", getName().c_str());
std::vector<IFlushTarget::SP> wrappedTargets;
for (const auto &target : _targets) {
- wrappedTargets.push_back(std::make_shared<WrappedFlushTarget>
- (target, *this));
+ wrappedTargets.push_back(std::make_shared<WrappedFlushTarget>(target, *this));
}
return wrappedTargets;
}
// Called once by flush engine thread for each task done
- void taskDone()
- {
+ void taskDone() {
std::lock_guard<std::mutex> guard(_lock);
++_pendingDone;
}
@@ -174,12 +160,9 @@ public:
// Called by flush engine master thread after flush handler is
// added to flush engine and when one or more flush tasks related
// to flush handler have completed.
- void
- flushDone(search::SerialNum oldestSerial) override
- {
+ void flushDone(search::SerialNum oldestSerial) override {
std::lock_guard<std::mutex> guard(_lock);
- LOG(info, "SimpleHandler(%s)::flushDone(%" PRIu64 ")",
- getName().c_str(), oldestSerial);
+ LOG(info, "SimpleHandler(%s)::flushDone(%" PRIu64 ")", getName().c_str(), oldestSerial);
_oldestSerial = std::max(_oldestSerial, oldestSerial);
_flushDoneHistory.push_back(oldestSerial);
while (_pendingDone > 0) {
@@ -188,8 +171,7 @@ public:
}
}
- FlushDoneHistory getFlushDoneHistory()
- {
+ FlushDoneHistory getFlushDoneHistory() {
std::lock_guard<std::mutex> guard(_lock);
return _flushDoneHistory;
}
@@ -217,12 +199,11 @@ public:
search::SerialNum &currentSerial)
: _flushedSerial(flushedSerial), _currentSerial(currentSerial),
_start(start), _done(done), _proceed(proceed)
- {
- }
+ { }
void run() override {
_start.countDown();
- if (_proceed != NULL) {
+ if (_proceed != nullptr) {
_proceed->await();
}
_flushedSerial = _currentSerial;
@@ -270,8 +251,7 @@ public:
_taskStart(),
_taskDone(),
_task(std::move(task))
- {
- }
+ { }
SimpleTarget(search::SerialNum flushedSerial = 0, bool proceedImmediately = true)
: SimpleTarget("anon", flushedSerial, proceedImmediately)
@@ -316,8 +296,7 @@ public:
: SimpleTarget("anon"),
_mgain(false),
_serial(false)
- {
- }
+ { }
MemoryGain getApproxMemoryGain() const override {
LOG_ASSERT(_mgain == false);
@@ -389,8 +368,7 @@ public:
class NoFlushStrategy : public SimpleStrategy
{
- FlushContext::List getFlushTargets(const FlushContext::List &,
- const flushengine::TlsStatsMap &) const override {
+ FlushContext::List getFlushTargets(const FlushContext::List &, const flushengine::TlsStatsMap &) const override {
return FlushContext::List();
}
};
@@ -426,17 +404,15 @@ struct Fixture
SimpleStrategy::SP strategy;
FlushEngine engine;
- Fixture(uint32_t numThreads, uint32_t idleIntervalMS, SimpleStrategy::SP strategy_)
+ Fixture(uint32_t numThreads, vespalib::duration idleInterval, SimpleStrategy::SP strategy_)
: tlsStatsFactory(std::make_shared<SimpleTlsStatsFactory>()),
strategy(strategy_),
- engine(tlsStatsFactory, strategy, numThreads, idleIntervalMS)
- {
- }
+ engine(tlsStatsFactory, strategy, numThreads, idleInterval)
+ { }
- Fixture(uint32_t numThreads, uint32_t idleIntervalMS)
- : Fixture(numThreads, idleIntervalMS, std::make_shared<SimpleStrategy>())
- {
- }
+ Fixture(uint32_t numThreads, vespalib::duration idleInterval)
+ : Fixture(numThreads, idleInterval, std::make_shared<SimpleStrategy>())
+ { }
void putFlushHandler(const vespalib::string &docTypeName, IFlushHandler::SP handler) {
engine.putFlushHandler(DocTypeName(docTypeName), handler);
@@ -446,17 +422,14 @@ struct Fixture
strategy->_targets.push_back(std::move(target));
}
- std::shared_ptr<SimpleHandler>
- addSimpleHandler(Targets targets)
- {
+ std::shared_ptr<SimpleHandler> addSimpleHandler(Targets targets) {
auto handler = std::make_shared<SimpleHandler>(targets, "handler", 20);
engine.putFlushHandler(DocTypeName("handler"), handler);
engine.start();
return handler;
}
- void assertOldestSerial(SimpleHandler &handler, search::SerialNum expOldestSerial)
- {
+ void assertOldestSerial(SimpleHandler &handler, search::SerialNum expOldestSerial) {
using namespace std::chrono_literals;
for (int pass = 0; pass < 600; ++pass) {
std::this_thread::sleep_for(100ms);
@@ -488,12 +461,12 @@ TEST_F("require that strategy controls flush target", Fixture(1, IINTERVAL))
EXPECT_EQUAL("bar", order[1]);
}
-TEST_F("require that zero handlers does not core", Fixture(2, 50))
+TEST_F("require that zero handlers does not core", Fixture(2, 50ms))
{
f.engine.start();
}
-TEST_F("require that zero targets does not core", Fixture(2, 50))
+TEST_F("require that zero targets does not core", Fixture(2, 50ms))
{
f.putFlushHandler("foo", std::make_shared<SimpleHandler>(Targets(), "foo"));
f.putFlushHandler("bar", std::make_shared<SimpleHandler>(Targets(), "bar"));
@@ -593,8 +566,7 @@ TEST_F("require that target can refuse flush", Fixture(2, IINTERVAL))
EXPECT_TRUE(!handler->_done.await(SHORT_TIMEOUT));
}
-TEST_F("require that targets are flushed when nothing new to flush",
- Fixture(2, IINTERVAL))
+TEST_F("require that targets are flushed when nothing new to flush", Fixture(2, IINTERVAL))
{
auto target = std::make_shared<SimpleTarget>("anon", 5); // oldest unflushed serial num = 5
auto handler = std::make_shared<SimpleHandler>(Targets({target}), "anon", 4); // current serial num = 4
@@ -640,7 +612,7 @@ TEST("require that threaded target works")
auto target = std::make_shared<ThreadedFlushTarget>(executor, getSerialNum, std::make_shared<SimpleTarget>());
EXPECT_FALSE(executor._done.await(SHORT_TIMEOUT));
- EXPECT_TRUE(target->initFlush(0, std::make_shared<search::FlushToken>()).get() != NULL);
+ EXPECT_TRUE(target->initFlush(0, std::make_shared<search::FlushToken>()));
EXPECT_TRUE(executor._done.await(LONG_TIMEOUT));
}
@@ -692,7 +664,7 @@ assertThatHandlersInCurrentSet(FlushEngine & engine, const std::vector<const cha
}
}
-TEST_F("require that concurrency works", Fixture(2, 1))
+TEST_F("require that concurrency works", Fixture(2, 1ms))
{
auto target1 = std::make_shared<SimpleTarget>("target1", 1, false);
auto target2 = std::make_shared<SimpleTarget>("target2", 2, false);
@@ -713,7 +685,31 @@ TEST_F("require that concurrency works", Fixture(2, 1))
target2->_proceed.countDown();
}
-TEST_F("require that state explorer can list flush targets", Fixture(1, 1))
+TEST_F("require that concurrency works with triggerFlush", Fixture(2, 1ms))
+{
+ auto target1 = std::make_shared<SimpleTarget>("target1", 1, false);
+ auto target2 = std::make_shared<SimpleTarget>("target2", 2, false);
+ auto target3 = std::make_shared<SimpleTarget>("target3", 3, false);
+ auto handler = std::make_shared<SimpleHandler>(Targets({target1, target2, target3}), "handler", 9);
+ f.putFlushHandler("handler", handler);
+ std::thread thread([this]() { f.engine.triggerFlush(); });
+ std::this_thread::sleep_for(1s);
+ f.engine.start();
+
+ EXPECT_TRUE(target1->_initDone.await(LONG_TIMEOUT));
+ EXPECT_TRUE(target2->_initDone.await(LONG_TIMEOUT));
+ EXPECT_TRUE(!target3->_initDone.await(SHORT_TIMEOUT));
+ assertThatHandlersInCurrentSet(f.engine, {"handler.target1", "handler.target2"});
+ EXPECT_TRUE(!target3->_initDone.await(SHORT_TIMEOUT));
+ target1->_proceed.countDown();
+ EXPECT_TRUE(target1->_taskDone.await(LONG_TIMEOUT));
+ assertThatHandlersInCurrentSet(f.engine, {"handler.target2", "handler.target3"});
+ target3->_proceed.countDown();
+ target2->_proceed.countDown();
+ thread.join();
+}
+
+TEST_F("require that state explorer can list flush targets", Fixture(1, 1ms))
{
auto target = std::make_shared<SimpleTarget>("target1", 100, false);
f.putFlushHandler("handler",
@@ -744,7 +740,7 @@ TEST_F("require that state explorer can list flush targets", Fixture(1, 1))
target->_taskDone.await(LONG_TIMEOUT);
}
-TEST_F("require that oldest serial is updated when closing engine", Fixture(1, 100))
+TEST_F("require that oldest serial is updated when closing engine", Fixture(1, 100ms))
{
auto target1 = std::make_shared<SimpleTarget>("target1", 10, false);
auto handler = f.addSimpleHandler({ target1 });
@@ -754,7 +750,7 @@ TEST_F("require that oldest serial is updated when closing engine", Fixture(1, 1
EXPECT_EQUAL(20u, handler->_oldestSerial);
}
-TEST_F("require that oldest serial is updated when finishing priority flush strategy", Fixture(1, 100, std::make_shared<NoFlushStrategy>()))
+TEST_F("require that oldest serial is updated when finishing priority flush strategy", Fixture(1, 100ms, std::make_shared<NoFlushStrategy>()))
{
auto target1 = std::make_shared<SimpleTarget>("target1", 10, true);
auto handler = f.addSimpleHandler({ target1 });
diff --git a/searchcore/src/tests/proton/matching/docid_range_scheduler/docid_range_scheduler_test.cpp b/searchcore/src/tests/proton/matching/docid_range_scheduler/docid_range_scheduler_test.cpp
index 4814ab4cb49..510269f2195 100644
--- a/searchcore/src/tests/proton/matching/docid_range_scheduler/docid_range_scheduler_test.cpp
+++ b/searchcore/src/tests/proton/matching/docid_range_scheduler/docid_range_scheduler_test.cpp
@@ -1,9 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/searchcore/proton/matching/docid_range_scheduler.h>
-#include <chrono>
-#include <thread>
using namespace proton::matching;
using vespalib::TimeBomb;
diff --git a/searchcore/src/tests/proton/metrics/documentdb_job_trackers/documentdb_job_trackers_test.cpp b/searchcore/src/tests/proton/metrics/documentdb_job_trackers/documentdb_job_trackers_test.cpp
index c108e98cc2a..1b9c60b379e 100644
--- a/searchcore/src/tests/proton/metrics/documentdb_job_trackers/documentdb_job_trackers_test.cpp
+++ b/searchcore/src/tests/proton/metrics/documentdb_job_trackers/documentdb_job_trackers_test.cpp
@@ -4,6 +4,7 @@
#include <vespa/searchcore/proton/metrics/job_tracked_flush_target.h>
#include <vespa/searchcore/proton/test/dummy_flush_target.h>
#include <vespa/vespalib/testkit/testapp.h>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("documentdb_job_trackers_test");
diff --git a/searchcore/src/tests/proton/metrics/job_tracked_flush/job_tracked_flush_test.cpp b/searchcore/src/tests/proton/metrics/job_tracked_flush/job_tracked_flush_test.cpp
index 7c22aab2285..20d75972c8b 100644
--- a/searchcore/src/tests/proton/metrics/job_tracked_flush/job_tracked_flush_test.cpp
+++ b/searchcore/src/tests/proton/metrics/job_tracked_flush/job_tracked_flush_test.cpp
@@ -27,8 +27,8 @@ struct MyFlushTask : public searchcorespi::FlushTask
MyFlushTask(Gate &execGate) : _execGate(execGate) {}
// Implements searchcorespi::FlushTask
- virtual void run() override {
- _execGate.await(5000);
+ void run() override {
+ _execGate.await(5s);
}
virtual search::SerialNum getFlushSerial() const override { return 5; }
};
@@ -39,7 +39,7 @@ struct MyFlushTarget : public test::DummyFlushTarget
SerialNum _initFlushSerial;
Gate _execGate;
Gate _initGate;
- MyFlushTarget()
+ MyFlushTarget() noexcept
: test::DummyFlushTarget("mytarget", Type::FLUSH, Component::OTHER),
_initFlushSerial(0),
_execGate(),
@@ -50,8 +50,8 @@ struct MyFlushTarget : public test::DummyFlushTarget
FlushTask::UP initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken>) override {
if (currentSerial > 0) {
_initFlushSerial = currentSerial;
- _initGate.await(5000);
- return FlushTask::UP(new MyFlushTask(_execGate));
+ _initGate.await(5s);
+ return std::make_unique<MyFlushTask>(_execGate);
}
return FlushTask::UP();
}
@@ -66,8 +66,8 @@ struct Fixture
Gate _taskGate;
ThreadStackExecutor _exec;
Fixture(uint32_t numJobTrackings = 1)
- : _tracker(new SimpleJobTracker(numJobTrackings)),
- _target(new MyFlushTarget()),
+ : _tracker(std::make_shared<SimpleJobTracker>(numJobTrackings)),
+ _target(std::make_shared<MyFlushTarget>()),
_trackedFlush(_tracker, _target),
_task(),
_taskGate(),
@@ -95,12 +95,12 @@ TEST_F("require that flush task init is tracked", Fixture)
EXPECT_EQUAL(1u, f._tracker->_ended.getCount());
f._exec.execute(makeLambdaTask([&]() {f.initFlush(FLUSH_SERIAL); }));
- f._tracker->_started.await(5000);
+ f._tracker->_started.await(5s);
EXPECT_EQUAL(0u, f._tracker->_started.getCount());
EXPECT_EQUAL(1u, f._tracker->_ended.getCount());
f._target->_initGate.countDown();
- f._taskGate.await(5000);
+ f._taskGate.await(5s);
EXPECT_EQUAL(0u, f._tracker->_ended.getCount());
{
JobTrackedFlushTask *trackedTask = dynamic_cast<JobTrackedFlushTask *>(f._task.get());
@@ -114,18 +114,18 @@ TEST_F("require that flush task execution is tracked", Fixture(2))
{
f._exec.execute(makeLambdaTask([&]() { f.initFlush(FLUSH_SERIAL); }));
f._target->_initGate.countDown();
- f._taskGate.await(5000);
+ f._taskGate.await(5s);
EXPECT_EQUAL(1u, f._tracker->_started.getCount());
EXPECT_EQUAL(1u, f._tracker->_ended.getCount());
f._exec.execute(std::move(f._task));
- f._tracker->_started.await(5000);
+ f._tracker->_started.await(5s);
EXPECT_EQUAL(0u, f._tracker->_started.getCount());
EXPECT_EQUAL(1u, f._tracker->_ended.getCount());
f._target->_execGate.countDown();
- f._tracker->_ended.await(5000);
+ f._tracker->_ended.await(5s);
EXPECT_EQUAL(0u, f._tracker->_ended.getCount());
}
diff --git a/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp b/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp
index 93fe2f0ae24..2e53b97a878 100644
--- a/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp
+++ b/searchcore/src/tests/proton/proton_config_fetcher/proton_config_fetcher_test.cpp
@@ -6,7 +6,9 @@
#include <vespa/searchcore/proton/server/proton_config_fetcher.h>
#include <vespa/searchcore/proton/server/proton_config_snapshot.h>
#include <vespa/searchcore/proton/server/i_proton_configurer.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/hw_info.h>
+#include <vespa/searchcore/proton/common/subdbtype.h>
#include <vespa/searchcore/config/config-ranking-constants.h>
#include <vespa/searchcore/config/config-onnx-models.h>
#include <vespa/searchsummary/config/config-juniperrc.h>
@@ -41,6 +43,8 @@ using document::DocumenttypesConfigBuilder;
using search::TuneFileDocumentDB;
using std::map;
using vespalib::VarHolder;
+using search::GrowStrategy;
+using search::CompactionStrategy;
struct DoctypeFixture {
using UP = std::unique_ptr<DoctypeFixture>;
@@ -389,6 +393,32 @@ TEST_FF("require that docstore config computes cachesize automatically if unset"
EXPECT_EQUAL(500000ul, config->getStoreConfig().getMaxCacheBytes());
}
+TEST_FF("require that allocation config is propagated",
+ ConfigTestFixture("test"),
+ DocumentDBConfigManager(f1.configId + "/test", "test"))
+{
+ f1.protonBuilder.distribution.redundancy = 5;
+ f1.protonBuilder.distribution.searchablecopies = 2;
+ f1.addDocType("test");
+ {
+ auto& allocation = f1.protonBuilder.documentdb.back().allocation;
+ allocation.initialnumdocs = 10000000;
+ allocation.growfactor = 0.1;
+ allocation.growbias = 1;
+ allocation.amortizecount = 10000;
+ allocation.multivaluegrowfactor = 0.15;
+ allocation.maxDeadBytesRatio = 0.25;
+ allocation.maxDeadAddressSpaceRatio = 0.3;
+ }
+ auto config = getDocumentDBConfig(f1, f2);
+ {
+ auto& alloc_config = config->get_alloc_config();
+ EXPECT_EQUAL(AllocStrategy(GrowStrategy(20000000, 0.1, 1, 0.15), CompactionStrategy(0.25, 0.3), 10000), alloc_config.make_alloc_strategy(SubDbType::READY));
+ EXPECT_EQUAL(AllocStrategy(GrowStrategy(100000, 0.1, 1, 0.15), CompactionStrategy(0.25, 0.3), 10000), alloc_config.make_alloc_strategy(SubDbType::REMOVED));
+ EXPECT_EQUAL(AllocStrategy(GrowStrategy(30000000, 0.1, 1, 0.15), CompactionStrategy(0.25, 0.3), 10000), alloc_config.make_alloc_strategy(SubDbType::NOTREADY));
+ }
+}
+
TEST("test HwInfo equality") {
EXPECT_TRUE(HwInfo::Cpu(1) == HwInfo::Cpu(1));
EXPECT_FALSE(HwInfo::Cpu(1) == HwInfo::Cpu(2));
diff --git a/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp b/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
index 5a0cfc18c78..4f8e8e8aa8c 100644
--- a/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
+++ b/searchcore/src/tests/proton/proton_configurer/proton_configurer_test.cpp
@@ -9,6 +9,7 @@
#include <vespa/config-summarymap.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/fileacquirer/config-filedistributorrpc.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/server/bootstrapconfig.h>
#include <vespa/searchcore/proton/server/bootstrapconfigmanager.h>
#include <vespa/searchcore/proton/server/documentdbconfigmanager.h>
@@ -103,6 +104,7 @@ struct DBConfigFixture {
std::make_shared<DocumentDBMaintenanceConfig>(),
search::LogDocumentStore::Config(),
std::make_shared<const ThreadingServiceConfig>(ThreadingServiceConfig::make(1)),
+ std::make_shared<const AllocConfig>(),
configId,
docTypeName);
}
diff --git a/searchcore/src/tests/proton/reference/gid_to_lid_change_listener/gid_to_lid_change_listener_test.cpp b/searchcore/src/tests/proton/reference/gid_to_lid_change_listener/gid_to_lid_change_listener_test.cpp
index f028de23be6..63c861a60d9 100644
--- a/searchcore/src/tests/proton/reference/gid_to_lid_change_listener/gid_to_lid_change_listener_test.cpp
+++ b/searchcore/src/tests/proton/reference/gid_to_lid_change_listener/gid_to_lid_change_listener_test.cpp
@@ -7,6 +7,7 @@
#include <vespa/searchcore/proton/reference/gid_to_lid_change_listener.h>
#include <vespa/searchlib/common/i_gid_to_lid_mapper_factory.h>
#include <vespa/vespalib/util/destructor_callbacks.h>
+#include <vespa/vespalib/util/gate.h>
#include <vespa/searchlib/test/mock_gid_to_lid_mapping.h>
#include <map>
#include <vespa/log/log.h>
diff --git a/searchcore/src/vespa/searchcore/config/proton.def b/searchcore/src/vespa/searchcore/config/proton.def
index 17d0ef8ad37..fb845982d74 100644
--- a/searchcore/src/vespa/searchcore/config/proton.def
+++ b/searchcore/src/vespa/searchcore/config/proton.def
@@ -306,6 +306,12 @@ documentdb[].allocation.amortizecount int default=10000
## used in multi-value attribute vectors to store underlying values.
documentdb[].allocation.multivaluegrowfactor double default=0.2
+## The ratio of used bytes that can be dead before attempting to perform compaction.
+documentdb[].allocation.max_dead_bytes_ratio double default=0.2
+
+## The ratio of used address space that can be dead before attempting to perform compaction.
+documentdb[].allocation.max_dead_address_space_ratio double default=0.2
+
## The interval of when periodic tasks should be run
periodic.interval double default=3600.0
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.cpp b/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.cpp
index 3390447e26a..1acfa64285c 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.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 "address_space_usage_stats.h"
+#include <ostream>
namespace proton {
@@ -25,4 +26,13 @@ AddressSpaceUsageStats::merge(const vespalib::AddressSpace &usage,
}
}
+std::ostream&
+operator<<(std::ostream& out, const AddressSpaceUsageStats& rhs)
+{
+ out << "{usage=" << rhs.getUsage() <<
+ ", attribute_name=" << rhs.getAttributeName() <<
+ ", subdb_name=" << rhs.getSubDbName() << "}";
+ return out;
+}
+
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.h b/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.h
index 400c7cde03f..9ed68693ec1 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.h
+++ b/searchcore/src/vespa/searchcore/proton/attribute/address_space_usage_stats.h
@@ -7,8 +7,8 @@
namespace proton {
-/*
- * class representing usage of a single address space (enum store or
+/**
+ * Class representing usage of a single address space (enum store or
* multi value) and the most largest attribute in that respect, relative
* to the limit.
*/
@@ -28,6 +28,14 @@ public:
const vespalib::AddressSpace &getUsage() const { return _usage; }
const vespalib::string &getAttributeName() const { return _attributeName; }
const vespalib::string &getSubDbName() const { return _subDbName; }
+
+ bool operator==(const AddressSpaceUsageStats& rhs) const {
+ return (_usage == rhs._usage) &&
+ (_attributeName == rhs._attributeName) &&
+ (_subDbName == rhs._subDbName);
+ }
};
+std::ostream& operator<<(std::ostream &out, const AddressSpaceUsageStats& rhs);
+
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.cpp
index 23701bfdd5d..4189688ea81 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.cpp
@@ -9,11 +9,9 @@ using search::GrowStrategy;
namespace proton {
AttributeCollectionSpecFactory::AttributeCollectionSpecFactory(
- const search::GrowStrategy &growStrategy,
- size_t growNumDocs,
+ const AllocStrategy &alloc_strategy,
bool fastAccessOnly)
- : _growStrategy(growStrategy),
- _growNumDocs(growNumDocs),
+ : _alloc_strategy(alloc_strategy),
_fastAccessOnly(fastAccessOnly)
{
}
@@ -25,8 +23,8 @@ AttributeCollectionSpecFactory::create(const AttributesConfig &attrCfg,
{
AttributeCollectionSpec::AttributeList attrs;
// Amortize memory spike cost over N docs
- const size_t skew = _growNumDocs/(attrCfg.attribute.size()+1);
- GrowStrategy grow = _growStrategy;
+ const size_t skew = _alloc_strategy.get_amortize_count()/(attrCfg.attribute.size()+1);
+ GrowStrategy grow = _alloc_strategy.get_grow_strategy();
grow.setDocsInitialCapacity(std::max(grow.getDocsInitialCapacity(),docIdLimit));
for (const auto &attr : attrCfg.attribute) {
search::attribute::Config cfg = ConfigConverter::convert(attr);
@@ -35,6 +33,7 @@ AttributeCollectionSpecFactory::create(const AttributesConfig &attrCfg,
}
grow.setDocsGrowDelta(grow.getDocsGrowDelta() + skew);
cfg.setGrowStrategy(grow);
+ cfg.setCompactionStrategy(_alloc_strategy.get_compaction_strategy());
attrs.push_back(AttributeSpec(attr.name, cfg));
}
return std::make_unique<AttributeCollectionSpec>(attrs, docIdLimit, serialNum);
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.h b/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.h
index bfa6681d6f2..074c56448f3 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.h
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_collection_spec_factory.h
@@ -4,7 +4,7 @@
#include "attribute_collection_spec.h"
#include <vespa/searchcommon/attribute/config.h>
-#include <vespa/searchcommon/common/growstrategy.h>
+#include <vespa/searchcore/proton/common/alloc_strategy.h>
#include <vespa/searchlib/common/serialnum.h>
#include <vespa/config-attributes.h>
@@ -19,13 +19,11 @@ class AttributeCollectionSpecFactory
private:
typedef vespa::config::search::AttributesConfig AttributesConfig;
- const search::GrowStrategy _growStrategy;
- const size_t _growNumDocs;
+ const AllocStrategy _alloc_strategy;
const bool _fastAccessOnly;
public:
- AttributeCollectionSpecFactory(const search::GrowStrategy &growStrategy,
- size_t growNumDocs,
+ AttributeCollectionSpecFactory(const AllocStrategy& alloc_strategy,
bool fastAccessOnly);
AttributeCollectionSpec::UP create(const AttributesConfig &attrCfg,
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_initializer.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_initializer.cpp
index 368e4ce12f2..372bfe5d631 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_initializer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_initializer.cpp
@@ -199,7 +199,7 @@ AttributeInitializer::loadAttribute(const AttributeVectorSP &attr,
return false;
} else {
attr->commit(serialNum, serialNum);
- EventLogger::loadAttributeComplete(_documentSubDbName, attr->getName(), vespalib::count_ms(timer.elapsed()));
+ EventLogger::loadAttributeComplete(_documentSubDbName, attr->getName(), timer.elapsed());
}
return true;
}
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.cpp
index 9198cfdafab..109a4bb2192 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.cpp
@@ -7,6 +7,7 @@
#include <future>
using search::AttributeVector;
+using search::CompactionStrategy;
using search::GrowStrategy;
using search::SerialNum;
using vespa::config::search::AttributesConfig;
@@ -135,7 +136,7 @@ AttributeCollectionSpec::UP
AttributeManagerInitializer::createAttributeSpec() const
{
uint32_t docIdLimit = 1; // The real docIdLimit is used after attributes are loaded to pad them
- AttributeCollectionSpecFactory factory(_attributeGrow, _attributeGrowNumDocs, _fastAccessAttributesOnly);
+ AttributeCollectionSpecFactory factory(_alloc_strategy, _fastAccessAttributesOnly);
return factory.create(_attrCfg, docIdLimit, _configSerialNum);
}
@@ -144,8 +145,7 @@ AttributeManagerInitializer::AttributeManagerInitializer(SerialNum configSerialN
DocumentMetaStore::SP documentMetaStore,
AttributeManager::SP baseAttrMgr,
const AttributesConfig &attrCfg,
- const GrowStrategy &attributeGrow,
- size_t attributeGrowNumDocs,
+ const AllocStrategy& alloc_strategy,
bool fastAccessAttributesOnly,
searchcorespi::index::IThreadService &master,
std::shared_ptr<AttributeManager::SP> attrMgrResult)
@@ -153,8 +153,7 @@ AttributeManagerInitializer::AttributeManagerInitializer(SerialNum configSerialN
_documentMetaStore(documentMetaStore),
_attrMgr(),
_attrCfg(attrCfg),
- _attributeGrow(attributeGrow),
- _attributeGrowNumDocs(attributeGrowNumDocs),
+ _alloc_strategy(alloc_strategy),
_fastAccessAttributesOnly(fastAccessAttributesOnly),
_master(master),
_attributesResult(),
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.h b/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.h
index d56f4c25ace..f74a0f1519d 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.h
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_manager_initializer.h
@@ -4,9 +4,9 @@
#include "attributemanager.h"
#include "initialized_attributes_result.h"
-#include <vespa/searchcommon/common/growstrategy.h>
#include <vespa/searchcore/proton/documentmetastore/documentmetastore.h>
#include <vespa/searchcore/proton/initializer/initializer_task.h>
+#include <vespa/searchcore/proton/common/alloc_strategy.h>
#include <vespa/searchlib/common/serialnum.h>
#include <vespa/config-attributes.h>
@@ -24,8 +24,7 @@ private:
DocumentMetaStore::SP _documentMetaStore;
AttributeManager::SP _attrMgr;
vespa::config::search::AttributesConfig _attrCfg;
- search::GrowStrategy _attributeGrow;
- size_t _attributeGrowNumDocs;
+ AllocStrategy _alloc_strategy;
bool _fastAccessAttributesOnly;
searchcorespi::index::IThreadService &_master;
InitializedAttributesResult _attributesResult;
@@ -39,8 +38,7 @@ public:
DocumentMetaStore::SP documentMetaStore,
AttributeManager::SP baseAttrMgr,
const vespa::config::search::AttributesConfig &attrCfg,
- const search::GrowStrategy &attributeGrow,
- size_t attributeGrowNumDocs,
+ const AllocStrategy& alloc_strategy,
bool fastAccessAttributesOnly,
searchcorespi::index::IThreadService &master,
std::shared_ptr<AttributeManager::SP> attrMgrResult);
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.cpp
index 2852497cf3f..e19e7976227 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.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 "attribute_usage_filter.h"
+#include "i_attribute_usage_listener.h"
#include <sstream>
namespace proton {
@@ -85,7 +86,8 @@ AttributeUsageFilter::AttributeUsageFilter()
_attributeStats(),
_config(),
_state(),
- _acceptWrite(true)
+ _acceptWrite(true),
+ _listener()
{
}
@@ -97,6 +99,9 @@ AttributeUsageFilter::setAttributeStats(AttributeUsageStats attributeStats_in)
Guard guard(_lock);
_attributeStats = attributeStats_in;
recalcState(guard);
+ if (_listener) {
+ _listener->notify_attribute_usage(_attributeStats);
+ }
}
AttributeUsageStats
@@ -114,6 +119,13 @@ AttributeUsageFilter::setConfig(Config config_in)
recalcState(guard);
}
+void
+AttributeUsageFilter::set_listener(std::unique_ptr<IAttributeUsageListener> listener)
+{
+ Guard guard(_lock);
+ _listener = std::move(listener);
+}
+
double
AttributeUsageFilter::getEnumStoreUsedRatio() const
{
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.h b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.h
index ecc95d4ee02..cb9687e31a1 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.h
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_filter.h
@@ -10,7 +10,9 @@
namespace proton {
-/*
+class IAttributeUsageListener;
+
+/**
* Class to filter write operations based on sampled information about
* attribute resource usage (e.g. enum store and multivalue mapping).
* If resource limit is reached then further writes are denied in
@@ -29,6 +31,7 @@ private:
Config _config;
State _state;
std::atomic<bool> _acceptWrite;
+ std::unique_ptr<IAttributeUsageListener> _listener;
void recalcState(const Guard &guard); // called with _lock held
public:
@@ -37,6 +40,7 @@ public:
void setAttributeStats(AttributeUsageStats attributeStats_in);
AttributeUsageStats getAttributeUsageStats() const;
void setConfig(Config config);
+ void set_listener(std::unique_ptr<IAttributeUsageListener> listener);
double getEnumStoreUsedRatio() const;
double getMultiValueUsedRatio() const;
bool acceptWriteOperation() const override;
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.cpp
index 4d59c9081e0..f3da5486d3e 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.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 "attribute_usage_stats.h"
+#include <iostream>
namespace proton {
@@ -19,5 +20,12 @@ AttributeUsageStats::merge(const search::AddressSpaceUsage &usage,
_multiValueUsage.merge(usage.multiValueUsage(), attributeName, subDbName);
}
+std::ostream&
+operator<<(std::ostream& out, const AttributeUsageStats& rhs)
+{
+ out << "{enum_store=" << rhs.enumStoreUsage() <<
+ ", multi_value=" << rhs.multiValueUsage() << "}";
+ return out;
+}
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.h b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.h
index f1805809e80..1eb6a9cc6be 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.h
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attribute_usage_stats.h
@@ -7,7 +7,7 @@
namespace proton {
-/*
+/**
* Class representing aggregated attribute usage, with info about
* the most bloated attributes with regards to enum store and
* multivalue mapping.
@@ -23,10 +23,15 @@ public:
const vespalib::string &attributeName,
const vespalib::string &subDbName);
- const AddressSpaceUsageStats &
- enumStoreUsage() const { return _enumStoreUsage; }
- const AddressSpaceUsageStats &
- multiValueUsage() const { return _multiValueUsage; }
+ const AddressSpaceUsageStats& enumStoreUsage() const { return _enumStoreUsage; }
+ const AddressSpaceUsageStats& multiValueUsage() const { return _multiValueUsage; }
+
+ bool operator==(const AttributeUsageStats& rhs) const {
+ return (_enumStoreUsage == rhs._enumStoreUsage) &&
+ (_multiValueUsage == rhs._multiValueUsage);
+ }
};
+std::ostream& operator<<(std::ostream& out, const AttributeUsageStats& rhs);
+
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp b/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp
index cc1af923fc0..f635ee34a04 100644
--- a/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/attribute/attributemanager.cpp
@@ -177,10 +177,14 @@ AttributeManager::transferExistingAttributes(const AttributeManager &currMgr,
auto shrinker = wrap->getShrinker();
assert(shrinker);
addAttribute(AttributeWrap::normalAttribute(av), shrinker);
+ auto id = _attributeFieldWriter.getExecutorIdFromName(av->getNamePrefix());
+ auto cfg = aspec.getConfig();
+ _attributeFieldWriter.execute(id, [av, cfg]() { av->update_config(cfg); });
} else {
toBeAdded.push_back(aspec);
}
}
+ _attributeFieldWriter.sync();
}
void
@@ -328,7 +332,7 @@ AttributeManager::flushAll(SerialNum currentSerial)
for (const auto &ft : flushTargets) {
vespalib::Executor::Task::UP task;
task = ft->initFlush(currentSerial, std::make_shared<search::FlushToken>());
- if (task.get() != NULL) {
+ if (task) {
task->run();
}
}
diff --git a/searchcore/src/vespa/searchcore/proton/bucketdb/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/bucketdb/CMakeLists.txt
index 6619f9e419d..66a58ad66a3 100644
--- a/searchcore/src/vespa/searchcore/proton/bucketdb/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/bucketdb/CMakeLists.txt
@@ -6,6 +6,7 @@ vespa_add_library(searchcore_bucketdb STATIC
bucket_db_owner.cpp
bucketdb.cpp
bucketdbhandler.cpp
+ bucketscaniterator.cpp
bucketsessionbase.cpp
bucketstate.cpp
checksumaggregators.cpp
diff --git a/searchcore/src/vespa/searchcore/proton/bucketdb/bucketdb.h b/searchcore/src/vespa/searchcore/proton/bucketdb/bucketdb.h
index 05388931e20..e6848d095df 100644
--- a/searchcore/src/vespa/searchcore/proton/bucketdb/bucketdb.h
+++ b/searchcore/src/vespa/searchcore/proton/bucketdb/bucketdb.h
@@ -31,7 +31,7 @@ private:
void checkEmpty() const;
public:
BucketDB();
- virtual ~BucketDB();
+ ~BucketDB();
const BucketState & add(const GlobalId &gid,
const BucketId &bucketId, const Timestamp &timestamp, uint32_t docSize,
diff --git a/searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.cpp b/searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.cpp
new file mode 100644
index 00000000000..46cbb4fc37f
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.cpp
@@ -0,0 +1,29 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "bucketscaniterator.h"
+
+using document::BucketId;
+using storage::spi::BucketInfo;
+
+namespace proton::bucketdb {
+
+ScanIterator::ScanIterator(BucketDBOwner::Guard db, Pass pass, BucketId lastBucket, BucketId endBucket)
+ : _db(std::move(db)),
+ _itr(lastBucket.isSet() ? _db->upperBound(lastBucket) : _db->begin()),
+ _end(pass == Pass::SECOND && endBucket.isSet() ?
+ _db->upperBound(endBucket) : _db->end())
+{ }
+
+ScanIterator::ScanIterator(BucketDBOwner::Guard db, BucketId bucket)
+ : _db(std::move(db)),
+ _itr(_db->lowerBound(bucket)),
+ _end(_db->end())
+{ }
+
+ScanIterator::ScanIterator(ScanIterator &&rhs)
+ : _db(std::move(rhs._db)),
+ _itr(rhs._itr),
+ _end(rhs._end)
+{ }
+
+} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.h b/searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.h
new file mode 100644
index 00000000000..a437230ed0f
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/bucketdb/bucketscaniterator.h
@@ -0,0 +1,49 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "bucket_db_owner.h"
+#include "bucketdb.h"
+
+namespace proton::bucketdb {
+
+struct ScanPosition {
+ document::BucketId _lastBucket;
+
+ ScanPosition() : _lastBucket() { }
+ bool validBucket() const { return _lastBucket.isSet(); }
+};
+
+
+class ScanIterator {
+private:
+ using BucketId = document::BucketId;
+ using BucketIterator = BucketDB::ConstMapIterator;
+ BucketDBOwner::Guard _db;
+ BucketIterator _itr;
+ BucketIterator _end;
+
+public:
+ enum class Pass {FIRST, SECOND};
+ ScanIterator(BucketDBOwner::Guard db, Pass pass, BucketId lastBucket, BucketId endBucket);
+
+ ScanIterator(BucketDBOwner::Guard db, BucketId bucket);
+
+ ScanIterator(const ScanIterator &) = delete;
+ ScanIterator(ScanIterator &&rhs);
+ ScanIterator &operator=(const ScanIterator &) = delete;
+ ScanIterator &operator=(ScanIterator &&rhs) = delete;
+
+ bool valid() const { return _itr != _end; }
+ bool isActive() const { return _itr->second.isActive(); }
+ BucketId getBucket() const { return _itr->first; }
+ bool hasReadyBucketDocs() const { return _itr->second.getReadyCount() != 0; }
+ bool hasNotReadyBucketDocs() const { return _itr->second.getNotReadyCount() != 0; }
+
+ ScanIterator & operator++() {
+ ++_itr;
+ return *this;
+ }
+};
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt b/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt
index d59a4075dce..a91c35f0485 100644
--- a/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/proton/common/CMakeLists.txt
@@ -1,6 +1,8 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
vespa_add_library(searchcore_pcommon STATIC
SOURCES
+ alloc_config.cpp
+ alloc_strategy.cpp
attribute_updater.cpp
attributefieldvaluenode.cpp
cachedselect.cpp
diff --git a/searchcore/src/vespa/searchcore/proton/common/alloc_config.cpp b/searchcore/src/vespa/searchcore/proton/common/alloc_config.cpp
new file mode 100644
index 00000000000..1611d00fb0f
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/common/alloc_config.cpp
@@ -0,0 +1,56 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "alloc_config.h"
+#include <vespa/searchcore/proton/common/subdbtype.h>
+#include <algorithm>
+
+using search::CompactionStrategy;
+using search::GrowStrategy;
+
+namespace proton {
+
+AllocConfig::AllocConfig(const AllocStrategy& alloc_strategy,
+ uint32_t redundancy, uint32_t searchable_copies)
+ : _alloc_strategy(alloc_strategy),
+ _redundancy(redundancy),
+ _searchable_copies(searchable_copies)
+{
+}
+
+AllocConfig::AllocConfig()
+ : AllocConfig(AllocStrategy(), 1, 1)
+{
+}
+
+AllocConfig::~AllocConfig() = default;
+
+bool
+AllocConfig::operator==(const AllocConfig &rhs) const noexcept
+{
+ return ((_alloc_strategy == rhs._alloc_strategy) &&
+ (_redundancy == rhs._redundancy) &&
+ (_searchable_copies == rhs._searchable_copies));
+}
+
+AllocStrategy
+AllocConfig::make_alloc_strategy(SubDbType sub_db_type) const
+{
+ auto &baseline_grow_strategy = _alloc_strategy.get_grow_strategy();
+ size_t initial_capacity = baseline_grow_strategy.getDocsInitialCapacity();
+ switch (sub_db_type) {
+ case SubDbType::READY:
+ initial_capacity *= _searchable_copies;
+ break;
+ case SubDbType::NOTREADY:
+ initial_capacity *= (_redundancy - _searchable_copies);
+ break;
+ case SubDbType::REMOVED:
+ default:
+ initial_capacity = std::max(1024ul, initial_capacity / 100);
+ break;
+ }
+ GrowStrategy grow_strategy(initial_capacity, baseline_grow_strategy.getDocsGrowFactor(), baseline_grow_strategy.getDocsGrowDelta(), baseline_grow_strategy.getMultiValueAllocGrowFactor());
+ return AllocStrategy(grow_strategy, _alloc_strategy.get_compaction_strategy(), _alloc_strategy.get_amortize_count());
+}
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/common/alloc_config.h b/searchcore/src/vespa/searchcore/proton/common/alloc_config.h
new file mode 100644
index 00000000000..25b953b9871
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/common/alloc_config.h
@@ -0,0 +1,34 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "alloc_strategy.h"
+
+namespace proton {
+
+enum class SubDbType;
+
+/*
+ * Class representing allocation config for proton which can be used
+ * to make an allocation strategy for large data structures owned by a
+ * document sub db (e.g. attribute vectors, document meta store).
+ */
+class AllocConfig
+{
+ AllocStrategy _alloc_strategy; // baseline before adjusting for redundancy / searchable copies
+ const uint32_t _redundancy;
+ const uint32_t _searchable_copies;
+
+public:
+ AllocConfig(const AllocStrategy& alloc_strategy, uint32_t redundancy, uint32_t searchable_copies);
+ AllocConfig();
+ ~AllocConfig();
+
+ bool operator==(const AllocConfig &rhs) const noexcept;
+ bool operator!=(const AllocConfig &rhs) const noexcept {
+ return !operator==(rhs);
+ }
+ AllocStrategy make_alloc_strategy(SubDbType sub_db_type) const;
+};
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/common/alloc_strategy.cpp b/searchcore/src/vespa/searchcore/proton/common/alloc_strategy.cpp
new file mode 100644
index 00000000000..3af72757ccb
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/common/alloc_strategy.cpp
@@ -0,0 +1,41 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "alloc_strategy.h"
+#include <iostream>
+
+using search::CompactionStrategy;
+using search::GrowStrategy;
+
+namespace proton {
+
+AllocStrategy::AllocStrategy(const GrowStrategy& grow_strategy,
+ const CompactionStrategy& compaction_strategy,
+ uint32_t amortize_count)
+ : _grow_strategy(grow_strategy),
+ _compaction_strategy(compaction_strategy),
+ _amortize_count(amortize_count)
+{
+}
+
+AllocStrategy::AllocStrategy()
+ : AllocStrategy(GrowStrategy(), CompactionStrategy(), 10000)
+{
+}
+
+AllocStrategy::~AllocStrategy() = default;
+
+bool
+AllocStrategy::operator==(const AllocStrategy &rhs) const noexcept
+{
+ return ((_grow_strategy == rhs._grow_strategy) &&
+ (_compaction_strategy == rhs._compaction_strategy) &&
+ (_amortize_count == rhs._amortize_count));
+}
+
+std::ostream& operator<<(std::ostream& os, const AllocStrategy&alloc_strategy)
+{
+ os << "{ grow_strategy=" << alloc_strategy.get_grow_strategy() << ", compaction_strategy=" << alloc_strategy.get_compaction_strategy() << ", amortize_count=" << alloc_strategy.get_amortize_count() << "}";
+ return os;
+}
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/common/alloc_strategy.h b/searchcore/src/vespa/searchcore/proton/common/alloc_strategy.h
new file mode 100644
index 00000000000..74bcc1772ee
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/proton/common/alloc_strategy.h
@@ -0,0 +1,41 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/searchcommon/common/compaction_strategy.h>
+#include <vespa/searchcommon/common/growstrategy.h>
+#include <iosfwd>
+
+namespace proton {
+
+/*
+ * Class representing allocation strategy for large data structures
+ * owned by a document sub db (e.g. attribute vectors, document meta store).
+ */
+class AllocStrategy
+{
+protected:
+ const search::GrowStrategy _grow_strategy;
+ const search::CompactionStrategy _compaction_strategy;
+ const uint32_t _amortize_count;
+
+public:
+ AllocStrategy(const search::GrowStrategy& grow_strategy,
+ const search::CompactionStrategy& compaction_strategy,
+ uint32_t amortize_count);
+
+ AllocStrategy();
+ ~AllocStrategy();
+
+ bool operator==(const AllocStrategy &rhs) const noexcept;
+ bool operator!=(const AllocStrategy &rhs) const noexcept {
+ return !operator==(rhs);
+ }
+ const search::GrowStrategy& get_grow_strategy() const noexcept { return _grow_strategy; }
+ const search::CompactionStrategy& get_compaction_strategy() const noexcept { return _compaction_strategy; }
+ uint32_t get_amortize_count() const noexcept { return _amortize_count; }
+};
+
+std::ostream& operator<<(std::ostream& os, const AllocStrategy&alloc_strategy);
+
+}
diff --git a/searchcore/src/vespa/searchcore/proton/common/eventlogger.cpp b/searchcore/src/vespa/searchcore/proton/common/eventlogger.cpp
index 5966589d635..20b87f4bc63 100644
--- a/searchcore/src/vespa/searchcore/proton/common/eventlogger.cpp
+++ b/searchcore/src/vespa/searchcore/proton/common/eventlogger.cpp
@@ -10,6 +10,7 @@ LOG_SETUP(".proton.common.eventlogger");
using search::util::LogUtil;
using vespalib::JSONStringer;
using vespalib::make_string;
+using vespalib::count_ms;
namespace {
@@ -32,12 +33,12 @@ doTransactionLogReplayStart(const string &domainName, SerialNum first, SerialNum
}
void
-doTransactionLogReplayComplete(const string &domainName, int64_t elapsedTimeMs, const string &eventName)
+doTransactionLogReplayComplete(const string &domainName, vespalib::duration elapsedTime, const string &eventName)
{
JSONStringer jstr;
jstr.beginObject();
jstr.appendKey("domain").appendString(domainName);
- jstr.appendKey("time.elapsed.ms").appendInt64(elapsedTimeMs);
+ jstr.appendKey("time.elapsed.ms").appendInt64(count_ms(elapsedTime));
jstr.endObject();
EV_STATE(eventName.c_str(), jstr.toString().data());
}
@@ -71,9 +72,9 @@ EventLogger::transactionLogReplayProgress(const string &domainName, float progre
}
void
-EventLogger::transactionLogReplayComplete(const string &domainName, int64_t elapsedTimeMs)
+EventLogger::transactionLogReplayComplete(const string &domainName, vespalib::duration elapsedTime)
{
- doTransactionLogReplayComplete(domainName, elapsedTimeMs, "transactionlog.replay.complete");
+ doTransactionLogReplayComplete(domainName, elapsedTime, "transactionlog.replay.complete");
}
void
@@ -109,13 +110,13 @@ EventLogger::flushStart(const string &name, int64_t beforeMemory, int64_t afterM
}
void
-EventLogger::flushComplete(const string &name, int64_t elapsedTimeMs, SerialNum flushed,
+EventLogger::flushComplete(const string &name, vespalib::duration elapsedTime, SerialNum flushed,
const string &outputPath, size_t outputPathElems)
{
JSONStringer jstr;
jstr.beginObject();
jstr.appendKey("name").appendString(name);
- jstr.appendKey("time.elapsed.ms").appendInt64(elapsedTimeMs);
+ jstr.appendKey("time.elapsed.ms").appendInt64(count_ms(elapsedTime));
jstr.appendKey("serialnum")
.beginObject()
.appendKey("flushed").appendInt64(flushed)
@@ -237,13 +238,13 @@ EventLogger::reprocessDocumentsProgress(const string &subDb, double progress, do
void
-EventLogger::reprocessDocumentsComplete(const string &subDb, double visitCost, int64_t elapsedTimeMs)
+EventLogger::reprocessDocumentsComplete(const string &subDb, double visitCost, vespalib::duration elapsedTime)
{
JSONStringer jstr;
jstr.beginObject();
jstr.appendKey("documentsubdb").appendString(subDb);
jstr.appendKey("visitcost").appendDouble(visitCost);
- jstr.appendKey("time.elapsed.ms").appendInt64(elapsedTimeMs);
+ jstr.appendKey("time.elapsed.ms").appendInt64(count_ms(elapsedTime));
jstr.endObject();
EV_STATE("reprocess.documents.complete", jstr.toString().data());
}
@@ -261,13 +262,13 @@ EventLogger::loadAttributeStart(const vespalib::string &subDbName, const vespali
void
EventLogger::loadAttributeComplete(const vespalib::string &subDbName,
- const vespalib::string &attrName, int64_t elapsedTimeMs)
+ const vespalib::string &attrName, vespalib::duration elapsedTime)
{
JSONStringer jstr;
jstr.beginObject();
jstr.appendKey("documentsubdb").appendString(subDbName);
jstr.appendKey("name").appendString(attrName);
- jstr.appendKey("time.elapsed.ms").appendInt64(elapsedTimeMs);
+ jstr.appendKey("time.elapsed.ms").appendInt64(count_ms(elapsedTime));
jstr.endObject();
EV_STATE("load.attribute.complete", jstr.toString().data());
}
@@ -285,12 +286,12 @@ loadComponentStart(const vespalib::string &subDbName, const vespalib::string &co
}
void
-loadComponentComplete(const vespalib::string &subDbName, const vespalib::string &componentName, int64_t elapsedTimeMs)
+loadComponentComplete(const vespalib::string &subDbName, const vespalib::string &componentName, vespalib::duration elapsedTime)
{
JSONStringer jstr;
jstr.beginObject();
jstr.appendKey("documentsubdb").appendString(subDbName);
- jstr.appendKey("time.elapsed.ms").appendInt64(elapsedTimeMs);
+ jstr.appendKey("time.elapsed.ms").appendInt64(count_ms(elapsedTime));
jstr.endObject();
EV_STATE(make_string("load.%s.complete", componentName.c_str()).c_str(), jstr.toString().data());
}
@@ -304,9 +305,9 @@ EventLogger::loadDocumentMetaStoreStart(const vespalib::string &subDbName)
}
void
-EventLogger::loadDocumentMetaStoreComplete(const vespalib::string &subDbName, int64_t elapsedTimeMs)
+EventLogger::loadDocumentMetaStoreComplete(const vespalib::string &subDbName, vespalib::duration elapsedTime)
{
- loadComponentComplete(subDbName, "documentmetastore", elapsedTimeMs);
+ loadComponentComplete(subDbName, "documentmetastore", elapsedTime);
}
void
@@ -316,9 +317,9 @@ EventLogger::loadDocumentStoreStart(const vespalib::string &subDbName)
}
void
-EventLogger::loadDocumentStoreComplete(const vespalib::string &subDbName, int64_t elapsedTimeMs)
+EventLogger::loadDocumentStoreComplete(const vespalib::string &subDbName, vespalib::duration elapsedTime)
{
- loadComponentComplete(subDbName, "documentstore", elapsedTimeMs);
+ loadComponentComplete(subDbName, "documentstore", elapsedTime);
}
void
diff --git a/searchcore/src/vespa/searchcore/proton/common/eventlogger.h b/searchcore/src/vespa/searchcore/proton/common/eventlogger.h
index 574e650732a..7d32c777f98 100644
--- a/searchcore/src/vespa/searchcore/proton/common/eventlogger.h
+++ b/searchcore/src/vespa/searchcore/proton/common/eventlogger.h
@@ -3,6 +3,7 @@
#include <vespa/searchlib/common/serialnum.h>
#include <vespa/vespalib/stllike/string.h>
+#include <vespa/vespalib/util/time.h>
#include <vector>
namespace proton {
@@ -15,7 +16,7 @@ private:
typedef search::SerialNum SerialNum;
typedef vespalib::string string;
public:
- static void transactionLogReplayComplete(const string &domainName, int64_t elapsedTimeMs);
+ static void transactionLogReplayComplete(const string &domainName, vespalib::duration elapsedTime);
static void populateAttributeStart(const std::vector<string> &names);
static void populateAttributeComplete(const std::vector<string> &names, int64_t documentsVisisted);
static void populateDocumentFieldStart(const string &fieldName);
@@ -23,7 +24,7 @@ public:
static void lidSpaceCompactionComplete(const string &subDbName, uint32_t lidLimit);
static void reprocessDocumentsStart(const string &subDb, double visitCost);
static void reprocessDocumentsProgress(const string &subDb, double progress, double visitCost);
- static void reprocessDocumentsComplete(const string &subDb, double visitCost, int64_t elapsedTimeMs);
+ static void reprocessDocumentsComplete(const string &subDb, double visitCost, vespalib::duration elapsedTime);
static void transactionLogReplayStart(const string &domainName,
SerialNum first,
SerialNum last);
@@ -40,18 +41,18 @@ public:
SerialNum unflushed,
SerialNum current);
static void flushComplete(const string &name,
- int64_t elapsedTimeMs,
+ vespalib::duration elapsedTime,
SerialNum flushed,
const string &outputPath,
size_t outputPathElems);
static void flushPrune(const string &name, SerialNum oldestFlushed);
static void loadAttributeStart(const vespalib::string &subDbName, const vespalib::string &attrName);
static void loadAttributeComplete(const vespalib::string &subDbName,
- const vespalib::string &attrName, int64_t elapsedTimeMs);
+ const vespalib::string &attrName, vespalib::duration elapsedTime);
static void loadDocumentMetaStoreStart(const vespalib::string &subDbName);
- static void loadDocumentMetaStoreComplete(const vespalib::string &subDbName, int64_t elapsedTimeMs);
+ static void loadDocumentMetaStoreComplete(const vespalib::string &subDbName, vespalib::duration elapsedTime);
static void loadDocumentStoreStart(const vespalib::string &subDbName);
- static void loadDocumentStoreComplete(const vespalib::string &subDbName, int64_t elapsedTimeMs);
+ static void loadDocumentStoreComplete(const vespalib::string &subDbName, vespalib::duration elapsedTime);
static void transactionLogPruneComplete(const string &domainName, SerialNum prunedSerial);
};
diff --git a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp
index 29346903aef..85c92f0da6b 100644
--- a/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/docsummary/summarymanagerinitializer.cpp
@@ -43,7 +43,7 @@ SummaryManagerInitializer::run()
*_result = std::make_shared<SummaryManager>
(_summaryExecutor, _storeCfg, _grow, _baseDir, _docTypeName,
_tuneFile, _fileHeaderContext, _tlSyncer, _bucketizer);
- EventLogger::loadDocumentStoreComplete(_subDbName, vespalib::count_ms(timer.elapsed()));
+ EventLogger::loadDocumentStoreComplete(_subDbName, timer.elapsed());
}
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreflushtarget.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreflushtarget.cpp
index b563cee1d0c..5e6a9f354f9 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreflushtarget.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreflushtarget.cpp
@@ -39,9 +39,7 @@ private:
bool saveDocumentMetaStore(); // not updating snap info.
public:
Flusher(DocumentMetaStoreFlushTarget &dmsft, uint64_t syncToken, AttributeDirectory::Writer &writer);
- ~Flusher();
- uint64_t getSyncToken() const { return _syncToken; }
- bool saveSnapInfo();
+ ~Flusher() override;
bool flush(AttributeDirectory::Writer &writer);
void updateStats();
bool cleanUp(AttributeDirectory::Writer &writer);
@@ -66,29 +64,23 @@ Flusher(DocumentMetaStoreFlushTarget &dmsft,
assert(_saver);
}
-DocumentMetaStoreFlushTarget::Flusher::~Flusher()
-{
- // empty
-}
+DocumentMetaStoreFlushTarget::Flusher::~Flusher() = default;
bool
DocumentMetaStoreFlushTarget::Flusher::saveDocumentMetaStore()
{
vespalib::mkdir(_flushDir, false);
- SerialNumFileHeaderContext fileHeaderContext(_dmsft._fileHeaderContext,
- _syncToken);
+ SerialNumFileHeaderContext fileHeaderContext(_dmsft._fileHeaderContext, _syncToken);
bool saveSuccess = false;
if (_dmsft._hwInfo.disk().slow()) {
search::AttributeMemorySaveTarget memorySaveTarget;
saveSuccess = _saver->save(memorySaveTarget);
_saver.reset();
if (saveSuccess) {
- saveSuccess = memorySaveTarget.writeToFile(_dmsft._tuneFileAttributes,
- fileHeaderContext);
+ saveSuccess = memorySaveTarget.writeToFile(_dmsft._tuneFileAttributes, fileHeaderContext);
}
} else {
- search::AttributeFileSaveTarget saveTarget(_dmsft._tuneFileAttributes,
- fileHeaderContext);
+ search::AttributeFileSaveTarget saveTarget(_dmsft._tuneFileAttributes, fileHeaderContext);
saveSuccess = _saver->save(saveTarget);
_saver.reset();
}
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreinitializer.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreinitializer.cpp
index a1a8297d5c0..2bf6a1c8432 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreinitializer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoreinitializer.cpp
@@ -51,7 +51,7 @@ DocumentMetaStoreInitializer::run()
} else {
_dms->commit(snap.syncToken, snap.syncToken);
}
- EventLogger::loadDocumentMetaStoreComplete(_subDbName, vespalib::count_ms(stopWatch.elapsed()));
+ EventLogger::loadDocumentMetaStoreComplete(_subDbName, stopWatch.elapsed());
}
} else {
vespalib::mkdir(_baseDir, false);
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.cpp
index 94bba1bf64f..9a4054d02ca 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.cpp
@@ -83,7 +83,7 @@ DocumentMetaStoreSaver(vespalib::GenerationHandler::Guard &&guard,
}
-DocumentMetaStoreSaver::~DocumentMetaStoreSaver() { }
+DocumentMetaStoreSaver::~DocumentMetaStoreSaver() = default;
bool
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.h b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.h
index 2f745929667..a11f60af4b6 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.h
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/documentmetastoresaver.h
@@ -32,14 +32,14 @@ private:
const MetaDataStore &_metaDataStore;
bool _writeDocSize;
- virtual bool onSave(search::IAttributeSaveTarget &saveTarget) override;
+ bool onSave(search::IAttributeSaveTarget &saveTarget) override;
public:
DocumentMetaStoreSaver(vespalib::GenerationHandler::Guard &&guard,
const search::attribute::AttributeHeader &header,
const GidIterator &gidIterator,
const MetaDataStore &metaDataStore);
- virtual ~DocumentMetaStoreSaver();
+ ~DocumentMetaStoreSaver() override;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/cachedflushtarget.h b/searchcore/src/vespa/searchcore/proton/flushengine/cachedflushtarget.h
index ecd66f9cbfb..f2b3514fcf9 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/cachedflushtarget.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/cachedflushtarget.h
@@ -42,16 +42,18 @@ public:
const IFlushTarget::SP & getFlushTarget() { return _target; }
// Implements IFlushTarget.
- virtual MemoryGain getApproxMemoryGain() const override { return _memoryGain; }
- virtual DiskGain getApproxDiskGain() const override { return _diskGain; }
- virtual SerialNum getFlushedSerialNum() const override { return _flushedSerialNum; }
- virtual Time getLastFlushTime() const override { return _lastFlushTime; }
- virtual bool needUrgentFlush() const override { return _needUrgentFlush; }
-
- virtual Task::UP initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override { return _target->initFlush(currentSerial, std::move(flush_token)); }
- virtual FlushStats getLastFlushStats() const override { return _target->getLastFlushStats(); }
-
- virtual uint64_t getApproxBytesToWriteToDisk() const override;
+ MemoryGain getApproxMemoryGain() const override { return _memoryGain; }
+ DiskGain getApproxDiskGain() const override { return _diskGain; }
+ SerialNum getFlushedSerialNum() const override { return _flushedSerialNum; }
+ Time getLastFlushTime() const override { return _lastFlushTime; }
+ bool needUrgentFlush() const override { return _needUrgentFlush; }
+
+ Task::UP initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override {
+ return _target->initFlush(currentSerial, std::move(flush_token));
+ }
+ FlushStats getLastFlushStats() const override { return _target->getLastFlushStats(); }
+
+ uint64_t getApproxBytesToWriteToDisk() const override;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.cpp b/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.cpp
index 2a447957334..1f5ad8ae3a3 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.cpp
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.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 "flush_engine_explorer.h"
-
+#include "flushengine.h"
#include <vespa/vespalib/data/slime/cursor.h>
#include <vespa/vespalib/data/slime/inserter.h>
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.h b/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.h
index a674b39bc26..c2a90597ca2 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flush_engine_explorer.h
@@ -2,11 +2,12 @@
#pragma once
-#include "flushengine.h"
#include <vespa/vespalib/net/state_explorer.h>
namespace proton {
+class FlushEngine;
+
/**
* Class used to explore the state of a flush engine and its flush targets.
*/
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushcontext.cpp b/searchcore/src/vespa/searchcore/proton/flushengine/flushcontext.cpp
index b569e499876..ed31317cb69 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushcontext.cpp
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushcontext.cpp
@@ -16,20 +16,17 @@ FlushContext::FlushContext(
_target(target),
_task(),
_lastSerial(lastSerial)
-{
- // empty
-}
+{ }
-vespalib::string FlushContext::createName(const IFlushHandler & handler, const IFlushTarget & target)
-{
+vespalib::string
+FlushContext::createName(const IFlushHandler & handler, const IFlushTarget & target) {
return (handler.getName() + "." + target.getName());
}
FlushContext::~FlushContext()
{
- if (_task.get() != NULL) {
- LOG(warning, "Unexecuted flush task for '%s' destroyed.",
- _name.c_str());
+ if (_task) {
+ LOG(warning, "Unexecuted flush task for '%s' destroyed.", _name.c_str());
}
}
@@ -38,11 +35,11 @@ FlushContext::initFlush(std::shared_ptr<search::IFlushToken> flush_token)
{
LOG(debug, "Attempting to flush '%s'.", _name.c_str());
_task = _target->initFlush(std::max(_handler->getCurrentSerialNumber(), _lastSerial), std::move(flush_token));
- if (_task.get() == NULL) {
+ if ( ! _task ) {
LOG(debug, "Target refused to init flush.");
return false;
}
return true;
}
-} // namespace proton
+}
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp
index ab5b1ac5937..50162936835 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.cpp
@@ -77,10 +77,10 @@ FlushEngine::FlushInfo::FlushInfo(uint32_t taskId, const IFlushTarget::SP &targe
}
FlushEngine::FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory> tlsStatsFactory,
- IFlushStrategy::SP strategy, uint32_t numThreads, uint32_t idleIntervalMS)
+ IFlushStrategy::SP strategy, uint32_t numThreads, vespalib::duration idleInterval)
: _closed(false),
_maxConcurrent(numThreads),
- _idleIntervalMS(idleIntervalMS),
+ _idleInterval(idleInterval),
_taskId(0),
_threadPool(128 * 1024),
_strategy(std::move(strategy)),
@@ -151,13 +151,13 @@ FlushEngine::canFlushMore(const std::unique_lock<std::mutex> &guard) const
}
bool
-FlushEngine::wait(size_t minimumWaitTimeIfReady)
+FlushEngine::wait(vespalib::duration minimumWaitTimeIfReady, bool ignorePendingPrune)
{
std::unique_lock<std::mutex> guard(_lock);
- if ( (minimumWaitTimeIfReady > 0) && canFlushMore(guard) && _pendingPrune.empty()) {
- _cond.wait_for(guard, std::chrono::milliseconds(minimumWaitTimeIfReady));
+ if ( (minimumWaitTimeIfReady != vespalib::duration::zero()) && canFlushMore(guard) && _pendingPrune.empty()) {
+ _cond.wait_for(guard, minimumWaitTimeIfReady);
}
- while ( ! canFlushMore(guard) && _pendingPrune.empty()) {
+ while ( ! canFlushMore(guard) && ( ignorePendingPrune || _pendingPrune.empty())) {
_cond.wait_for(guard, 1s); // broadcast when flush done
}
return !_closed;
@@ -168,7 +168,7 @@ FlushEngine::Run(FastOS_ThreadInterface *, void *)
{
bool shouldIdle = false;
vespalib::string prevFlushName;
- while (wait(shouldIdle ? _idleIntervalMS : 0)) {
+ while (wait(shouldIdle ? _idleInterval : vespalib::duration::zero(), false)) {
shouldIdle = false;
if (prune()) {
continue; // Prune attempted on one or more handlers
@@ -181,8 +181,8 @@ FlushEngine::Run(FastOS_ThreadInterface *, void *)
} else {
shouldIdle = true;
}
- LOG(debug, "Making another wait(idle=%s, timeMS=%d) last was '%s'",
- shouldIdle ? "true" : "false", shouldIdle ? _idleIntervalMS : 0, prevFlushName.c_str());
+ LOG(debug, "Making another wait(idle=%s, timeS=%1.3f) last was '%s'",
+ shouldIdle ? "true" : "false", shouldIdle ? vespalib::to_s(_idleInterval) : 0, prevFlushName.c_str());
}
_executor.sync();
prune();
@@ -220,7 +220,8 @@ FlushEngine::prune()
return true;
}
-bool FlushEngine::isFlushing(const std::lock_guard<std::mutex> & guard, const vespalib::string & name) const
+bool
+FlushEngine::isFlushing(const std::lock_guard<std::mutex> & guard, const vespalib::string & name) const
{
(void) guard;
for(const auto & it : _flushing) {
@@ -306,7 +307,7 @@ FlushEngine::flushAll(const FlushContext::List &lst)
{
LOG(debug, "%ld targets to flush.", lst.size());
for (const FlushContext::SP & ctx : lst) {
- if (wait(0)) {
+ if (wait(vespalib::duration::zero(), true)) {
if (ctx->initFlush(get_flush_token(*ctx))) {
logTarget("initiated", *ctx);
_executor.execute(std::make_unique<FlushTask>(initFlush(*ctx), *this, ctx));
@@ -371,7 +372,7 @@ FlushEngine::flushDone(const FlushContext &ctx, uint32_t taskId)
}
if (LOG_WOULD_LOG(event)) {
FlushStats stats = ctx.getTarget()->getLastFlushStats();
- EventLogger::flushComplete(ctx.getName(), vespalib::count_ms(duration), ctx.getTarget()->getFlushedSerialNum(),
+ EventLogger::flushComplete(ctx.getName(), duration, ctx.getTarget()->getFlushedSerialNum(),
stats.getPath(), stats.getPathElementsToLog());
}
LOG(debug, "FlushEngine::flushDone(taskId='%d') took '%f' secs", taskId, vespalib::to_s(duration));
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h
index f51e93f0fbd..8aea546bd14 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushengine.h
@@ -50,7 +50,7 @@ private:
typedef HandlerMap<IFlushHandler> FlushHandlerMap;
bool _closed;
const uint32_t _maxConcurrent;
- const uint32_t _idleIntervalMS;
+ const vespalib::duration _idleInterval;
uint32_t _taskId;
FastOS_ThreadPool _threadPool;
IFlushStrategy::SP _strategy;
@@ -79,7 +79,7 @@ private:
uint32_t initFlush(const IFlushHandler::SP &handler, const IFlushTarget::SP &target);
void flushDone(const FlushContext &ctx, uint32_t taskId);
bool canFlushMore(const std::unique_lock<std::mutex> &guard) const;
- bool wait(size_t minimumWaitTimeIfReady);
+ bool wait(vespalib::duration minimumWaitTimeIfReady, bool ignorePendingPrune);
bool isFlushing(const std::lock_guard<std::mutex> &guard, const vespalib::string & name) const;
friend class FlushTask;
@@ -102,7 +102,7 @@ public:
* @param idleInterval The interval between when flushes are checked whne there are no one progressing.
*/
FlushEngine(std::shared_ptr<flushengine::ITlsStatsFactory> tlsStatsFactory,
- IFlushStrategy::SP strategy, uint32_t numThreads, uint32_t idleIntervalMS);
+ IFlushStrategy::SP strategy, uint32_t numThreads, vespalib::duration idleInterval);
/**
* Destructor. Waits for all pending tasks to complete.
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushtargetproxy.h b/searchcore/src/vespa/searchcore/proton/flushengine/flushtargetproxy.h
index 84bf91a2f9a..2d0be231adc 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushtargetproxy.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushtargetproxy.h
@@ -31,8 +31,7 @@ public:
* @param target The target to decorate.
* @param prefix The prefix to prepend to the target
*/
- FlushTargetProxy(const IFlushTarget::SP &target,
- const vespalib::string & prefix);
+ FlushTargetProxy(const IFlushTarget::SP &target, const vespalib::string & prefix);
/**
* Returns the decorated flush target. This should not be used for anything
* but testing, as invoking a method on the returned target beats the
@@ -40,34 +39,16 @@ public:
*
* @return The decorated flush target.
*/
- const IFlushTarget::SP &
- getFlushTarget() const
- {
- return _target;
- }
+ const IFlushTarget::SP & getFlushTarget() const { return _target; }
// Implements IFlushTarget.
- virtual MemoryGain
- getApproxMemoryGain() const override;
-
- virtual DiskGain
- getApproxDiskGain() const override;
-
- virtual SerialNum
- getFlushedSerialNum() const override;
-
- virtual Time
- getLastFlushTime() const override;
-
- virtual bool
- needUrgentFlush() const override;
-
- virtual Task::UP
- initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override;
-
- virtual searchcorespi::FlushStats
- getLastFlushStats() const override;
-
- virtual uint64_t getApproxBytesToWriteToDisk() const override;
+ MemoryGain getApproxMemoryGain() const override;
+ DiskGain getApproxDiskGain() const override;
+ SerialNum getFlushedSerialNum() const override;
+ Time getLastFlushTime() const override;
+ bool needUrgentFlush() const override;
+ Task::UP initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override;
+ searchcorespi::FlushStats getLastFlushStats() const override;
+ uint64_t getApproxBytesToWriteToDisk() const override;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.cpp b/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.cpp
index 94476d93b15..2b1bd3b423d 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.cpp
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.cpp
@@ -1,17 +1,18 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "flushtask.h"
+#include "flushengine.h"
namespace proton {
FlushTask::FlushTask(uint32_t taskId,
FlushEngine &engine,
- const FlushContext::SP &ctx)
+ std::shared_ptr<FlushContext> ctx)
: _taskId(taskId),
_engine(engine),
- _context(ctx)
+ _context(std::move(ctx))
{
- assert(_context.get() != NULL);
+ assert(_context);
}
FlushTask::~FlushTask()
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.h b/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.h
index 7860acaab41..fb3474e2a90 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/flushtask.h
@@ -1,10 +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 <vespa/searchcore/proton/flushengine/flushengine.h>
+#include <memory>
+#include <vespa/vespalib/util/executor.h>
namespace proton {
+class FlushEngine;
+class FlushContext;
/**
* This class decorates a task returned by initFlush() in IFlushTarget so that
* the appropriate callback is invoked on the running FlushEngine.
@@ -14,7 +17,7 @@ class FlushTask : public vespalib::Executor::Task
private:
uint32_t _taskId;
FlushEngine &_engine;
- FlushContext::SP _context;
+ std::shared_ptr<FlushContext> _context;
public:
FlushTask(const FlushTask &) = delete;
@@ -26,15 +29,14 @@ public:
* @param engine The running flush engine.
* @param ctx The context of the flush to perform.
*/
- FlushTask(uint32_t taskId, FlushEngine &engine, const FlushContext::SP &ctx);
+ FlushTask(uint32_t taskId, FlushEngine &engine, std::shared_ptr<FlushContext> ctx);
/**
* Destructor. Notifies the engine that the flush is done to prevent the
* engine from locking targets because of a glitch.
*/
- ~FlushTask();
+ ~FlushTask() override;
- // Implements Executor::Task.
void run() override;
};
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h b/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h
index ca23f27ed49..66ca730bfbc 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/iflushhandler.h
@@ -2,7 +2,6 @@
#pragma once
#include <vespa/searchcorespi/flush/iflushtarget.h>
-#include <vector>
namespace proton {
@@ -35,7 +34,7 @@ public:
/**
* Virtual destructor required for inheritance.
*/
- virtual ~IFlushHandler() { }
+ virtual ~IFlushHandler() = default;
/**
* Returns the unique name of this handler.
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/iflushstrategy.h b/searchcore/src/vespa/searchcore/proton/flushengine/iflushstrategy.h
index 67f6771650a..8c6a31ff270 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/iflushstrategy.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/iflushstrategy.h
@@ -14,17 +14,12 @@ namespace flushengine { class TlsStatsMap; }
*/
class IFlushStrategy {
public:
- /**
- * Convenience typedefs.
- */
typedef std::shared_ptr<IFlushStrategy> SP;
IFlushStrategy(const IFlushStrategy &) = delete;
IFlushStrategy & operator = (const IFlushStrategy &) = delete;
- /**
- * Virtual destructor required for inheritance.
- */
- virtual ~IFlushStrategy() { }
+
+ virtual ~IFlushStrategy() = default;
/**
* Takes an input of targets that are candidates for flush and returns
@@ -34,11 +29,10 @@ public:
* @return A prioritized list of targets to flush.
*/
virtual FlushContext::List getFlushTargets(const FlushContext::List & targetList,
- const flushengine::TlsStatsMap &
- tlsStatsMap) const = 0;
+ const flushengine::TlsStatsMap & tlsStatsMap) const = 0;
protected:
IFlushStrategy() = default;
};
-} // namespace proton
+}
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.cpp b/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.cpp
index 59a5504b57b..3db341ecb70 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.cpp
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.cpp
@@ -49,11 +49,9 @@ ThreadedFlushTarget::initFlush(SerialNum currentSerial, std::shared_ptr<search::
{
std::promise<Task::UP> promise;
std::future<Task::UP> future = promise.get_future();
- _executor.execute(makeLambdaTask([&]()
- { promise.set_value(callInitFlush(_target.get(),
- currentSerial,
- &_getSerialNum,
- flush_token)); }));
+ _executor.execute(makeLambdaTask([&]() {
+ promise.set_value(callInitFlush(_target.get(), currentSerial, &_getSerialNum, flush_token));
+ }));
return future.get();
}
diff --git a/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.h b/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.h
index cd2ee11265f..d949ffa9c50 100644
--- a/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.h
+++ b/searchcore/src/vespa/searchcore/proton/flushengine/threadedflushtarget.h
@@ -18,7 +18,7 @@ class ThreadedFlushTarget : public FlushTargetProxy
{
private:
using IFlushTarget = searchcorespi::IFlushTarget;
- vespalib::Executor &_executor;
+ vespalib::Executor &_executor;
const IGetSerialNum &_getSerialNum;
public:
@@ -48,9 +48,7 @@ public:
const IFlushTarget::SP &target,
const vespalib::string & prefix);
- // Implements IFlushTarget.
- virtual Task::UP
- initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override;
+ Task::UP initFlush(SerialNum currentSerial, std::shared_ptr<search::IFlushToken> flush_token) override;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/reprocessing/reprocess_documents_task.cpp b/searchcore/src/vespa/searchcore/proton/reprocessing/reprocess_documents_task.cpp
index d8e0fcc7843..d5cbb4615f4 100644
--- a/searchcore/src/vespa/searchcore/proton/reprocessing/reprocess_documents_task.cpp
+++ b/searchcore/src/vespa/searchcore/proton/reprocessing/reprocess_documents_task.cpp
@@ -44,8 +44,7 @@ ReprocessDocumentsTask::run()
docstore.accept(_handler, *this, *_docTypeRepo);
}
_handler.done();
- EventLogger::reprocessDocumentsComplete(_subDbName, _visitorCost,
- duration_cast<milliseconds>(clock::now() - _start).count());
+ EventLogger::reprocessDocumentsComplete(_subDbName, _visitorCost,(clock::now() - _start));
}
}
@@ -55,8 +54,8 @@ ReprocessDocumentsTask::updateProgress(double progress)
_visitorProgress = progress;
double deltaProgress = progress - _loggedProgress;
if (deltaProgress >= 0.01) {
- auto secondsSinceLastLog = duration_cast<seconds>(clock::now() - _lastLogTime);
- if (secondsSinceLastLog >= 60s || deltaProgress >= 0.10) {
+ auto timeSinceLastLog = clock::now() - _lastLogTime;
+ if (timeSinceLastLog >= 60s || deltaProgress >= 0.10) {
EventLogger::reprocessDocumentsProgress(_subDbName, progress, _visitorCost);
_lastLogTime = clock::now();
_loggedProgress = progress;
diff --git a/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.cpp b/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.cpp
index d8b5aa7b129..ce3bd3b8e9b 100644
--- a/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.cpp
@@ -4,6 +4,7 @@
#include "disk_mem_usage_state.h"
#include "imaintenancejobrunner.h"
#include "document_db_maintenance_config.h"
+#include "move_operation_limiter.h"
namespace proton {
@@ -49,7 +50,7 @@ BlockableMaintenanceJob::BlockableMaintenanceJob(const vespalib::string &name,
BlockableMaintenanceJob::~BlockableMaintenanceJob()
{
- _moveOpsLimiter->clearJob();
+ dynamic_cast<MoveOperationLimiter &>(*_moveOpsLimiter).clearJob();
}
bool
diff --git a/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.h b/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.h
index db7b8d05ca2..f20511bc417 100644
--- a/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.h
+++ b/searchcore/src/vespa/searchcore/proton/server/blockable_maintenance_job.h
@@ -2,7 +2,7 @@
#pragma once
#include "i_blockable_maintenance_job.h"
-#include "move_operation_limiter.h"
+#include "i_move_operation_limiter.h"
#include <mutex>
#include <unordered_set>
@@ -11,6 +11,7 @@ namespace proton {
class BlockableMaintenanceJobConfig;
class DiskMemUsageState;
class IMaintenanceJobRunner;
+struct IMoveOperationLimiter;
/**
* Implementation of a maintenance job that can be blocked and unblocked due to various external reasons.
@@ -27,12 +28,10 @@ private:
bool _blocked;
IMaintenanceJobRunner *_runner;
double _resourceLimitFactor;
+ std::shared_ptr<IMoveOperationLimiter> _moveOpsLimiter;
void updateBlocked(const LockGuard &guard);
-
protected:
- MoveOperationLimiter::SP _moveOpsLimiter;
-
void internalNotifyDiskMemUsage(const DiskMemUsageState &state);
public:
@@ -54,6 +53,7 @@ public:
void unBlock(BlockedReason reason) override;
bool isBlocked() const override;
void registerRunner(IMaintenanceJobRunner *runner) override { _runner = runner; }
+ IMoveOperationLimiter & getLimiter() { return *_moveOpsLimiter; }
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp b/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp
index 8485cb6835e..1c642205c86 100644
--- a/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.cpp
@@ -6,6 +6,8 @@
#include "iclusterstatechangednotifier.h"
#include "maintenancedocumentsubdb.h"
#include "i_disk_mem_usage_notifier.h"
+#include "ibucketmodifiedhandler.h"
+#include "move_operation_limiter.h"
#include <vespa/searchcore/proton/bucketdb/i_bucket_create_notifier.h>
#include <vespa/searchcore/proton/documentmetastore/i_document_meta_store.h>
@@ -19,37 +21,10 @@ namespace proton {
namespace {
-const uint32_t FIRST_SCAN_PASS = 1;
-const uint32_t SECOND_SCAN_PASS = 2;
-
const char * bool2str(bool v) { return (v ? "T" : "F"); }
}
-BucketMoveJob::ScanIterator::
-ScanIterator(BucketDBOwner::Guard db, uint32_t pass, BucketId lastBucket, BucketId endBucket)
- : _db(std::move(db)),
- _itr(lastBucket.isSet() ? _db->upperBound(lastBucket) : _db->begin()),
- _end(pass == SECOND_SCAN_PASS && endBucket.isSet() ?
- _db->upperBound(endBucket) : _db->end())
-{
-}
-
-BucketMoveJob::ScanIterator::
-ScanIterator(BucketDBOwner::Guard db, BucketId bucket)
- : _db(std::move(db)),
- _itr(_db->lowerBound(bucket)),
- _end(_db->end())
-{
-}
-
-BucketMoveJob::ScanIterator::ScanIterator(ScanIterator &&rhs)
- : _db(std::move(rhs._db)),
- _itr(rhs._itr),
- _end(rhs._end)
-{
-}
-
void
BucketMoveJob::checkBucket(const BucketId &bucket,
ScanIterator &itr,
@@ -176,17 +151,17 @@ BucketMoveJob(const IBucketStateCalculator::SP &calc,
_modifiedHandler(modifiedHandler),
_ready(ready),
_notReady(notReady),
- _mover(*_moveOpsLimiter),
+ _mover(getLimiter()),
_doneScan(false),
_scanPos(),
- _scanPass(FIRST_SCAN_PASS),
+ _scanPass(ScanPass::FIRST),
_endPos(),
_bucketSpace(bucketSpace),
_delayedBuckets(),
_delayedBucketsFrozen(),
_frozenBuckets(frozenBuckets),
_bucketCreateNotifier(bucketCreateNotifier),
- _delayedMover(*_moveOpsLimiter),
+ _delayedMover(getLimiter()),
_clusterStateChangedNotifier(clusterStateChangedNotifier),
_bucketStateChangedNotifier(bucketStateChangedNotifier),
_diskMemUsageNotifier(diskMemUsageNotifier)
@@ -277,18 +252,14 @@ BucketMoveJob::changedCalculator()
_endPos = _scanPos;
}
_doneScan = false;
- _scanPass = FIRST_SCAN_PASS;
+ _scanPass = ScanPass::FIRST;
maybeCancelMover(_mover);
maybeCancelMover(_delayedMover);
}
bool
-BucketMoveJob::scanAndMove(size_t maxBucketsToScan,
- size_t maxDocsToMove)
+BucketMoveJob::scanAndMove(size_t maxBucketsToScan, size_t maxDocsToMove)
{
- if (done()) {
- return true;
- }
IFrozenBucketHandler::ExclusiveBucketGuard::UP bucketGuard;
// Look for delayed bucket to be processed now
while (!_delayedBuckets.empty() && _delayedMover.bucketDone()) {
@@ -306,14 +277,13 @@ BucketMoveJob::scanAndMove(size_t maxBucketsToScan,
size_t bucketsScanned = 0;
for (;;) {
if (_mover.bucketDone()) {
- ScanResult res = scanBuckets(maxBucketsToScan -
- bucketsScanned, bucketGuard);
+ ScanResult res = scanBuckets(maxBucketsToScan - bucketsScanned, bucketGuard);
bucketsScanned += res.first;
if (res.second) {
- if (_scanPass == FIRST_SCAN_PASS &&
+ if (_scanPass == ScanPass::FIRST &&
_endPos.validBucket()) {
_scanPos = ScanPosition();
- _scanPass = SECOND_SCAN_PASS;
+ _scanPass = ScanPass::SECOND;
} else {
_doneScan = true;
break;
@@ -334,7 +304,7 @@ BucketMoveJob::scanAndMove(size_t maxBucketsToScan,
bool
BucketMoveJob::run()
{
- if (isBlocked()) {
+ if (isBlocked() || done()) {
return true; // indicate work is done, since node state is bad
}
/// Returning false here will immediately post the job back on the executor. This will give a busy loop,
@@ -363,8 +333,7 @@ BucketMoveJob::notifyClusterStateChanged(const IBucketStateCalculator::SP &newCa
}
void
-BucketMoveJob::notifyBucketStateChanged(const BucketId &bucketId,
- BucketInfo::ActiveState newState)
+BucketMoveJob::notifyBucketStateChanged(const BucketId &bucketId, BucketInfo::ActiveState newState)
{
// Called by master write thread
if (newState == BucketInfo::NOT_ACTIVE) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.h b/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.h
index e462b8ac7c6..8a84a10199c 100644
--- a/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.h
+++ b/searchcore/src/vespa/searchcore/proton/server/bucketmovejob.h
@@ -6,22 +6,21 @@
#include "documentbucketmover.h"
#include "i_disk_mem_usage_listener.h"
#include "ibucketfreezelistener.h"
-#include "ibucketmodifiedhandler.h"
-#include "ibucketstatecalculator.h"
#include "ibucketstatechangedhandler.h"
#include "iclusterstatechangedhandler.h"
#include "ifrozenbuckethandler.h"
-#include <vespa/searchcore/proton/bucketdb/bucket_db_owner.h>
+#include <vespa/searchcore/proton/bucketdb/bucketscaniterator.h>
#include <vespa/searchcore/proton/bucketdb/i_bucket_create_listener.h>
#include <set>
-namespace proton
-{
+namespace proton {
class BlockableMaintenanceJobConfig;
class IBucketStateChangedNotifier;
class IClusterStateChangedNotifier;
class IDiskMemUsageNotifier;
+class IBucketModifiedHandler;
+
namespace bucketdb { class IBucketCreateNotifier; }
/**
@@ -35,65 +34,24 @@ class BucketMoveJob : public BlockableMaintenanceJob,
public IBucketStateChangedHandler,
public IDiskMemUsageListener
{
-public:
- struct ScanPosition
- {
- document::BucketId _lastBucket;
-
- ScanPosition() : _lastBucket() { }
- ScanPosition(document::BucketId lastBucket) : _lastBucket(lastBucket) { }
- bool validBucket() const { return _lastBucket.isSet(); }
- };
-
- typedef BucketDB::ConstMapIterator BucketIterator;
-
- class ScanIterator
- {
- private:
- BucketDBOwner::Guard _db;
- BucketIterator _itr;
- BucketIterator _end;
-
- public:
- ScanIterator(BucketDBOwner::Guard db,
- uint32_t pass,
- document::BucketId lastBucket,
- document::BucketId endBucket);
-
- ScanIterator(BucketDBOwner::Guard db, document::BucketId bucket);
-
- ScanIterator(const ScanIterator &) = delete;
- ScanIterator(ScanIterator &&rhs);
- ScanIterator &operator=(const ScanIterator &) = delete;
- ScanIterator &operator=(ScanIterator &&rhs) = delete;
-
- bool valid() const { return _itr != _end; }
- bool isActive() const { return _itr->second.isActive(); }
- document::BucketId getBucket() const { return _itr->first; }
- bool hasReadyBucketDocs() const { return _itr->second.getReadyCount() != 0; }
- bool hasNotReadyBucketDocs() const { return _itr->second.getNotReadyCount() != 0; }
-
- ScanIterator & operator++() {
- ++_itr;
- return *this;
- }
- };
-
private:
- typedef std::pair<size_t, bool> ScanResult;
- IBucketStateCalculator::SP _calc;
- IDocumentMoveHandler &_moveHandler;
- IBucketModifiedHandler &_modifiedHandler;
- const MaintenanceDocumentSubDB &_ready;
- const MaintenanceDocumentSubDB &_notReady;
- DocumentBucketMover _mover;
- bool _doneScan;
- ScanPosition _scanPos;
- uint32_t _scanPass;
- ScanPosition _endPos;
- document::BucketSpace _bucketSpace;
-
- typedef std::set<document::BucketId> DelayedBucketSet;
+ using ScanPosition = bucketdb::ScanPosition;
+ using ScanIterator = bucketdb::ScanIterator;
+ using ScanPass = ScanIterator::Pass;
+ using ScanResult = std::pair<size_t, bool>;
+ std::shared_ptr<IBucketStateCalculator> _calc;
+ IDocumentMoveHandler &_moveHandler;
+ IBucketModifiedHandler &_modifiedHandler;
+ const MaintenanceDocumentSubDB &_ready;
+ const MaintenanceDocumentSubDB &_notReady;
+ DocumentBucketMover _mover;
+ bool _doneScan;
+ ScanPosition _scanPos;
+ ScanPass _scanPass;
+ ScanPosition _endPos;
+ document::BucketSpace _bucketSpace;
+
+ using DelayedBucketSet = std::set<document::BucketId>;
// Delayed buckets that are no longer frozen or active that can be considered for moving.
DelayedBucketSet _delayedBuckets;
@@ -185,4 +143,3 @@ public:
};
} // namespace proton
-
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp b/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp
index 6951125f408..1f2b0cf1a75 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.cpp
@@ -12,7 +12,6 @@
using document::BucketId;
using document::Document;
using document::GlobalId;
-using search::DocumentIdT;
using storage::spi::Timestamp;
namespace proton {
@@ -20,9 +19,7 @@ namespace proton {
typedef IDocumentMetaStore::Iterator Iterator;
bool
-DocumentBucketMover::moveDocument(DocumentIdT lid,
- const document::GlobalId &gid,
- Timestamp timestamp)
+DocumentBucketMover::moveDocument(uint32_t lid, const document::GlobalId &gid, Timestamp timestamp)
{
if ( _source->lidNeedsCommit(lid) ) {
return false;
@@ -80,13 +77,11 @@ namespace {
class MoveKey
{
public:
- DocumentIdT _lid;
+ uint32_t _lid;
document::GlobalId _gid;
Timestamp _timestamp;
- MoveKey(DocumentIdT lid,
- const document::GlobalId &gid,
- Timestamp timestamp)
+ MoveKey(uint32_t lid, const document::GlobalId &gid, Timestamp timestamp)
: _lid(lid),
_gid(gid),
_timestamp(timestamp)
@@ -115,7 +110,7 @@ DocumentBucketMover::moveDocuments(size_t maxDocsToMove)
typedef std::vector<MoveKey> MoveVec;
MoveVec toMove;
for (; itr != end && docsMoved < maxDocsToMove; ++itr) {
- DocumentIdT lid = itr.getKey().get_lid();
+ uint32_t lid = itr.getKey().get_lid();
const RawDocumentMetaData &metaData = _source->meta_store()->getRawMetaData(lid);
if (metaData.getBucketUsedBits() != _bucket.getUsedBits()) {
++docsSkipped;
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.h b/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.h
index eded3a456d8..80e1811268b 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.h
+++ b/searchcore/src/vespa/searchcore/proton/server/documentbucketmover.h
@@ -4,7 +4,6 @@
#include <vespa/document/bucket/bucketid.h>
#include <vespa/document/base/globalid.h>
-#include <vespa/searchlib/query/base.h>
#include <persistence/spi/types.h>
namespace proton {
@@ -32,7 +31,7 @@ private:
document::GlobalId _lastGid;
bool _lastGidValid;
- bool moveDocument(search::DocumentIdT lid,
+ bool moveDocument(uint32_t lid,
const document::GlobalId &gid,
storage::spi::Timestamp timestamp);
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
index cd517fe7c60..166d1e928bb 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.cpp
@@ -6,16 +6,17 @@
#include "document_subdb_collection_explorer.h"
#include "documentdb.h"
#include "documentdbconfigscout.h"
+#include "feedhandler.h"
#include "idocumentdbowner.h"
#include "idocumentsubdb.h"
#include "lid_space_compaction_handler.h"
#include "maintenance_jobs_injector.h"
#include "reconfig_params.h"
-#include "feedhandler.h"
-#include <vespa/searchcore/proton/persistenceengine/commit_and_wait_document_retriever.h>
#include <vespa/document/repo/documenttyperepo.h>
+#include <vespa/metrics/updatehook.h>
#include <vespa/searchcore/proton/attribute/attribute_config_inspector.h>
#include <vespa/searchcore/proton/attribute/attribute_writer.h>
+#include <vespa/searchcore/proton/attribute/i_attribute_usage_listener.h>
#include <vespa/searchcore/proton/attribute/imported_attributes_repo.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/searchcore/proton/common/statusreport.h>
@@ -26,6 +27,7 @@
#include <vespa/searchcore/proton/initializer/task_runner.h>
#include <vespa/searchcore/proton/metrics/executor_threading_service_stats.h>
#include <vespa/searchcore/proton/metrics/metricswireservice.h>
+#include <vespa/searchcore/proton/persistenceengine/commit_and_wait_document_retriever.h>
#include <vespa/searchcore/proton/reference/document_db_reference_resolver.h>
#include <vespa/searchcore/proton/reference/i_document_db_reference_registry.h>
#include <vespa/searchlib/attribute/attributefactory.h>
@@ -34,7 +36,6 @@
#include <vespa/searchlib/engine/searchreply.h>
#include <vespa/vespalib/util/destructor_callbacks.h>
#include <vespa/vespalib/util/exceptions.h>
-#include <vespa/metrics/updatehook.h>
#include <vespa/log/log.h>
#include <vespa/searchcorespi/index/warmupconfig.h>
@@ -69,39 +70,11 @@ namespace proton {
namespace {
constexpr uint32_t indexing_thread_stack_size = 128 * 1024;
-using Allocation = ProtonConfig::Documentdb::Allocation;
-GrowStrategy
-makeGrowStrategy(uint32_t docsInitialCapacity, const Allocation &allocCfg)
-{
- return GrowStrategy(docsInitialCapacity, allocCfg.growfactor, allocCfg.growbias, allocCfg.multivaluegrowfactor);
-}
-
-DocumentSubDBCollection::Config
-makeSubDBConfig(const ProtonConfig::Distribution & distCfg, const Allocation & allocCfg, size_t numSearcherThreads) {
- size_t initialNumDocs(allocCfg.initialnumdocs);
- GrowStrategy searchableGrowth = makeGrowStrategy(initialNumDocs * distCfg.searchablecopies, allocCfg);
- GrowStrategy removedGrowth = makeGrowStrategy(std::max(1024ul, initialNumDocs/100), allocCfg);
- GrowStrategy notReadyGrowth = makeGrowStrategy(initialNumDocs * (distCfg.redundancy - distCfg.searchablecopies), allocCfg);
- return DocumentSubDBCollection::Config(searchableGrowth, notReadyGrowth, removedGrowth, allocCfg.amortizecount, numSearcherThreads);
-}
-
index::IndexConfig
makeIndexConfig(const ProtonConfig::Index & cfg) {
return index::IndexConfig(WarmupConfig(vespalib::from_s(cfg.warmup.time), cfg.warmup.unpack), cfg.maxflushed, cfg.cache.size);
}
-ProtonConfig::Documentdb _G_defaultProtonDocumentDBConfig;
-
-const ProtonConfig::Documentdb *
-findDocumentDB(const ProtonConfig::DocumentdbVector & documentDBs, const vespalib::string & docType) {
- for (const auto & dbCfg : documentDBs) {
- if (dbCfg.inputdoctypename == docType) {
- return & dbCfg;
- }
- }
- return &_G_defaultProtonDocumentDBConfig;
-}
-
class MetricsUpdateHook : public metrics::UpdateHook {
DocumentDB &_db;
public:
@@ -182,9 +155,7 @@ DocumentDB::DocumentDB(const vespalib::string &baseDir,
_feedHandler(std::make_unique<FeedHandler>(_writeService, tlsSpec, docTypeName, *this, _writeFilter, *this, tlsWriterFactory)),
_subDBs(*this, *this, *_feedHandler, _docTypeName, _writeService, warmupExecutor, fileHeaderContext,
metricsWireService, getMetrics(), queryLimiter, clock, _configMutex, _baseDir,
- makeSubDBConfig(protonCfg.distribution,
- findDocumentDB(protonCfg.documentdb, docTypeName.getName())->allocation,
- protonCfg.numsearcherthreads),
+ DocumentSubDBCollection::Config(protonCfg.numsearcherthreads),
hwInfo),
_maintenanceController(_writeService.master(), sharedExecutor, _docTypeName),
_lidSpaceCompactionHandlers(),
@@ -1113,4 +1084,10 @@ DocumentDB::transient_memory_usage_provider()
return _transient_memory_usage_provider;
}
+void
+DocumentDB::set_attribute_usage_listener(std::unique_ptr<IAttributeUsageListener> listener)
+{
+ _writeFilter.set_listener(std::move(listener));
+}
+
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdb.h b/searchcore/src/vespa/searchcore/proton/server/documentdb.h
index 9c2facdddb0..b5d975884a3 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdb.h
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdb.h
@@ -416,6 +416,8 @@ public:
IDiskMemUsageListener *diskMemUsageListener() { return &_dmUsageForwarder; }
std::shared_ptr<const ITransientMemoryUsageProvider> transient_memory_usage_provider();
ExecutorThreadingService & getWriteService() { return _writeService; }
+
+ void set_attribute_usage_listener(std::unique_ptr<IAttributeUsageListener> listener);
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
index baa6c8eb450..7c487043b5b 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.cpp
@@ -14,6 +14,7 @@
#include <vespa/searchcore/config/config-ranking-constants.h>
#include <vespa/searchcore/config/config-onnx-models.h>
#include <vespa/searchcore/proton/attribute/attribute_aspect_delayer.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/document_type_inspector.h>
#include <vespa/searchcore/proton/common/indexschema_inspector.h>
@@ -49,7 +50,8 @@ DocumentDBConfig::ComparisonResult::ComparisonResult()
storeChanged(false),
visibilityDelayChanged(false),
flushChanged(false),
- threading_service_config_changed(false)
+ threading_service_config_changed(false),
+ alloc_config_changed(false)
{ }
DocumentDBConfig::DocumentDBConfig(
@@ -70,6 +72,7 @@ DocumentDBConfig::DocumentDBConfig(
const DocumentDBMaintenanceConfig::SP &maintenance,
const search::LogDocumentStore::Config & storeConfig,
std::shared_ptr<const ThreadingServiceConfig> threading_service_config,
+ std::shared_ptr<const AllocConfig> alloc_config,
const vespalib::string &configId,
const vespalib::string &docTypeName) noexcept
: _configId(configId),
@@ -91,6 +94,7 @@ DocumentDBConfig::DocumentDBConfig(
_maintenance(maintenance),
_storeConfig(storeConfig),
_threading_service_config(std::move(threading_service_config)),
+ _alloc_config(std::move(alloc_config)),
_orig(),
_delayedAttributeAspects(false)
{ }
@@ -117,6 +121,7 @@ DocumentDBConfig(const DocumentDBConfig &cfg)
_maintenance(cfg._maintenance),
_storeConfig(cfg._storeConfig),
_threading_service_config(cfg._threading_service_config),
+ _alloc_config(cfg._alloc_config),
_orig(cfg._orig),
_delayedAttributeAspects(false)
{ }
@@ -141,7 +146,8 @@ DocumentDBConfig::operator==(const DocumentDBConfig & rhs) const
equals<Schema>(_schema.get(), rhs._schema.get()) &&
equals<DocumentDBMaintenanceConfig>(_maintenance.get(), rhs._maintenance.get()) &&
_storeConfig == rhs._storeConfig &&
- equals<ThreadingServiceConfig>(_threading_service_config.get(), rhs._threading_service_config.get());
+ equals<ThreadingServiceConfig>(_threading_service_config.get(), rhs._threading_service_config.get()) &&
+ equals<AllocConfig>(_alloc_config.get(), rhs._alloc_config.get());
}
@@ -167,6 +173,7 @@ DocumentDBConfig::compare(const DocumentDBConfig &rhs) const
retval.visibilityDelayChanged = (_maintenance->getVisibilityDelay() != rhs._maintenance->getVisibilityDelay());
retval.flushChanged = !equals<DocumentDBMaintenanceConfig>(_maintenance.get(), rhs._maintenance.get(), [](const auto &l, const auto &r) { return l.getFlushConfig() == r.getFlushConfig(); });
retval.threading_service_config_changed = !equals<ThreadingServiceConfig>(_threading_service_config.get(), rhs._threading_service_config.get());
+ retval.alloc_config_changed = !equals<AllocConfig>(_alloc_config.get(), rhs._alloc_config.get());
return retval;
}
@@ -188,7 +195,8 @@ DocumentDBConfig::valid() const
_tuneFileDocumentDB &&
_schema &&
_maintenance &&
- _threading_service_config;
+ _threading_service_config &&
+ _alloc_config;
}
namespace
@@ -232,6 +240,7 @@ DocumentDBConfig::makeReplayConfig(const SP & orig)
o._maintenance,
o._storeConfig,
o._threading_service_config,
+ o._alloc_config,
o._configId,
o._docTypeName);
ret->_orig = orig;
@@ -274,6 +283,7 @@ DocumentDBConfig::newFromAttributesConfig(const AttributesConfigSP &attributes)
_maintenance,
_storeConfig,
_threading_service_config,
+ _alloc_config,
_configId,
_docTypeName);
}
@@ -311,6 +321,7 @@ DocumentDBConfig::makeDelayedAttributeAspectConfig(const SP &newCfg, const Docum
n._maintenance,
n._storeConfig,
n._threading_service_config,
+ n._alloc_config,
n._configId,
n._docTypeName);
result->_delayedAttributeAspects = true;
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h
index dc163e91ade..8e24ed8e96a 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfig.h
@@ -30,6 +30,7 @@ namespace document::internal { class InternalDocumenttypesType; }
namespace proton {
class ThreadingServiceConfig;
+class AllocConfig;
class DocumentDBConfig
{
@@ -55,6 +56,7 @@ public:
bool visibilityDelayChanged;
bool flushChanged;
bool threading_service_config_changed;
+ bool alloc_config_changed;
ComparisonResult();
ComparisonResult &setRankProfilesChanged(bool val) { rankProfilesChanged = val; return *this; }
@@ -88,6 +90,7 @@ public:
return *this;
}
ComparisonResult &set_threading_service_config_changed(bool val) { threading_service_config_changed = val; return *this; }
+ ComparisonResult &set_alloc_config_changed(bool val) { alloc_config_changed = val; return *this; }
};
using SP = std::shared_ptr<DocumentDBConfig>;
@@ -131,6 +134,7 @@ private:
MaintenanceConfigSP _maintenance;
search::LogDocumentStore::Config _storeConfig;
std::shared_ptr<const ThreadingServiceConfig> _threading_service_config;
+ std::shared_ptr<const AllocConfig> _alloc_config;
SP _orig;
bool _delayedAttributeAspects;
@@ -169,6 +173,7 @@ public:
const DocumentDBMaintenanceConfig::SP &maintenance,
const search::LogDocumentStore::Config & storeConfig,
std::shared_ptr<const ThreadingServiceConfig> threading_service_config,
+ std::shared_ptr<const AllocConfig> alloc_config,
const vespalib::string &configId,
const vespalib::string &docTypeName) noexcept;
@@ -210,6 +215,8 @@ public:
bool getDelayedAttributeAspects() const { return _delayedAttributeAspects; }
const ThreadingServiceConfig& get_threading_service_config() const { return *_threading_service_config; }
const std::shared_ptr<const ThreadingServiceConfig>& get_threading_service_config_shared_ptr() const { return _threading_service_config; }
+ const AllocConfig& get_alloc_config() const { return *_alloc_config; }
+ const std::shared_ptr<const AllocConfig>& get_alloc_config_shared_ptr() const { return _alloc_config; }
bool operator==(const DocumentDBConfig &rhs) const;
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
index 115a49fe997..f5a594d2f36 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentdbconfigmanager.cpp
@@ -3,6 +3,7 @@
#include "documentdbconfigmanager.h"
#include "bootstrapconfig.h"
#include "threading_service_config.h"
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/config/config-ranking-constants.h>
#include <vespa/searchcore/config/config-onnx-models.h>
@@ -252,6 +253,19 @@ build_threading_service_config(const ProtonConfig &proton_config,
hw_info.cpu()));
}
+std::shared_ptr<const AllocConfig>
+build_alloc_config(const ProtonConfig& proton_config, const vespalib::string& doc_type_name)
+{
+ auto& document_db_config_entry = find_document_db_config_entry(proton_config.documentdb, doc_type_name);
+ auto& alloc_config = document_db_config_entry.allocation;
+ auto& distribution_config = proton_config.distribution;
+ search::GrowStrategy grow_strategy(alloc_config.initialnumdocs, alloc_config.growfactor, alloc_config.growbias, alloc_config.multivaluegrowfactor);
+ search::CompactionStrategy compaction_strategy(alloc_config.maxDeadBytesRatio, alloc_config.maxDeadAddressSpaceRatio);
+ return std::make_shared<const AllocConfig>
+ (AllocStrategy(grow_strategy, compaction_strategy, alloc_config.amortizecount),
+ distribution_config.redundancy, distribution_config.searchablecopies);
+}
+
}
void
@@ -275,6 +289,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
MaintenanceConfigSP oldMaintenanceConfig;
MaintenanceConfigSP newMaintenanceConfig;
std::shared_ptr<const ThreadingServiceConfig> old_threading_service_config;
+ std::shared_ptr<const AllocConfig> old_alloc_config;
if (!_ignoreForwardedConfig) {
if (!(_bootstrapConfig->getDocumenttypesConfigSP() &&
@@ -299,6 +314,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
newIndexschemaConfig = current->getIndexschemaConfigSP();
oldMaintenanceConfig = current->getMaintenanceConfigSP();
old_threading_service_config = current->get_threading_service_config_shared_ptr();
+ old_alloc_config = current->get_alloc_config_shared_ptr();
currentGeneration = current->getGeneration();
}
@@ -382,6 +398,10 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
(*new_threading_service_config == *old_threading_service_config)) {
new_threading_service_config = old_threading_service_config;
}
+ auto new_alloc_config = build_alloc_config(_bootstrapConfig->getProtonConfig(), _docTypeName);
+ if (new_alloc_config && old_alloc_config &&(*new_alloc_config == *old_alloc_config)) {
+ new_alloc_config = old_alloc_config;
+ }
auto newSnapshot = std::make_shared<DocumentDBConfig>(generation,
newRankProfilesConfig,
newRankingConstants,
@@ -399,6 +419,7 @@ DocumentDBConfigManager::update(const ConfigSnapshot &snapshot)
newMaintenanceConfig,
storeConfig,
new_threading_service_config,
+ new_alloc_config,
_configId,
_docTypeName);
assert(newSnapshot->valid());
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp b/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp
index 61cf45a81d7..6ce0b896a50 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.cpp
@@ -19,13 +19,8 @@ using vespalib::makeLambdaTask;
namespace proton {
-DocumentSubDBCollection::Config::Config(GrowStrategy ready, GrowStrategy notReady, GrowStrategy removed,
- size_t fixedAttributeTotalSkew, size_t numSearchThreads)
- : _readyGrowth(ready),
- _notReadyGrowth(notReady),
- _removedGrowth(removed),
- _fixedAttributeTotalSkew(fixedAttributeTotalSkew),
- _numSearchThreads(numSearchThreads)
+DocumentSubDBCollection::Config::Config(size_t numSearchThreads)
+ : _numSearchThreads(numSearchThreads)
{ }
DocumentSubDBCollection::DocumentSubDBCollection(
@@ -66,7 +61,6 @@ DocumentSubDBCollection::DocumentSubDBCollection(
SearchableDocSubDB::Config(
FastAccessDocSubDB::Config(
StoreOnlyDocSubDB::Config(docTypeName, "0.ready", baseDir,
- cfg.getReadyGrowth(), cfg.getFixedAttributeTotalSkew(),
_readySubDbId, SubDbType::READY),
true, true, false),
cfg.getNumSearchThreads()),
@@ -76,15 +70,14 @@ DocumentSubDBCollection::DocumentSubDBCollection(
_subDBs.push_back
(new StoreOnlyDocSubDB(
- StoreOnlyDocSubDB::Config(docTypeName, "1.removed", baseDir, cfg.getRemovedGrowth(),
- cfg.getFixedAttributeTotalSkew(), _remSubDbId, SubDbType::REMOVED),
+ StoreOnlyDocSubDB::Config(docTypeName, "1.removed", baseDir,
+ _remSubDbId, SubDbType::REMOVED),
context));
_subDBs.push_back
(new FastAccessDocSubDB(
FastAccessDocSubDB::Config(
StoreOnlyDocSubDB::Config(docTypeName, "2.notready", baseDir,
- cfg.getNotReadyGrowth(), cfg.getFixedAttributeTotalSkew(),
_notReadySubDbId, SubDbType::NOTREADY),
true, true, true),
FastAccessDocSubDB::Context(context, metrics.notReady.attributes, metricsWireService)));
diff --git a/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.h b/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.h
index 317ec191d60..c86370c942b 100644
--- a/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.h
+++ b/searchcore/src/vespa/searchcore/proton/server/documentsubdbcollection.h
@@ -5,7 +5,6 @@
#include <vespa/searchcore/proton/bucketdb/bucketdbhandler.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/proton/persistenceengine/i_document_retriever.h>
-#include <vespa/searchcommon/common/growstrategy.h>
#include <vespa/searchlib/common/serialnum.h>
#include <vespa/vespalib/util/varholder.h>
#include <mutex>
@@ -61,19 +60,9 @@ public:
using SerialNum = search::SerialNum;
class Config {
public:
- using GrowStrategy = search::GrowStrategy;
- Config(GrowStrategy ready, GrowStrategy notReady, GrowStrategy removed,
- size_t fixedAttributeTotalSkew, size_t numSearchThreads);
- GrowStrategy getReadyGrowth() const { return _readyGrowth; }
- GrowStrategy getNotReadyGrowth() const { return _notReadyGrowth; }
- GrowStrategy getRemovedGrowth() const { return _removedGrowth; }
- size_t getNumSearchThreads() const { return _numSearchThreads; }
- size_t getFixedAttributeTotalSkew() const { return _fixedAttributeTotalSkew; }
+ Config(size_t numSearchThreads);
+ size_t getNumSearchThreads() const noexcept { return _numSearchThreads; }
private:
- const GrowStrategy _readyGrowth;
- const GrowStrategy _notReadyGrowth;
- const GrowStrategy _removedGrowth;
- const size_t _fixedAttributeTotalSkew;
const size_t _numSearchThreads;
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp b/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp
index f92f3e74e31..ca2a98d43e0 100644
--- a/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.cpp
@@ -13,6 +13,7 @@
#include <vespa/searchcore/proton/attribute/attribute_populator.h>
#include <vespa/searchcore/proton/attribute/filter_attribute_manager.h>
#include <vespa/searchcore/proton/attribute/sequential_attributes_initializer.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/matching/sessionmanager.h>
#include <vespa/searchcore/proton/reprocessing/attribute_reprocessing_initializer.h>
#include <vespa/searchcore/proton/reprocessing/document_reprocessing_handler.h>
@@ -62,6 +63,7 @@ FastAccessDocSubDB::createAttributeManagerInitializer(const DocumentDBConfig &co
DocumentMetaStore::SP documentMetaStore,
std::shared_ptr<AttributeManager::SP> attrMgrResult) const
{
+ AllocStrategy alloc_strategy = configSnapshot.get_alloc_config().make_alloc_strategy(_subDbType);
IAttributeFactory::SP attrFactory = std::make_shared<AttributeFactory>();
AttributeManager::SP baseAttrMgr =
std::make_shared<AttributeManager>(_baseDir + "/attribute",
@@ -77,8 +79,7 @@ FastAccessDocSubDB::createAttributeManagerInitializer(const DocumentDBConfig &co
documentMetaStore,
baseAttrMgr,
(_hasAttributes ? configSnapshot.getAttributesConfig() : AttributesConfig()),
- _attributeGrow,
- _attributeGrowNumDocs,
+ alloc_strategy,
_fastAccessAttributesOnly,
_writeService.master(),
attrMgrResult);
@@ -101,11 +102,10 @@ FastAccessDocSubDB::setupAttributeManager(AttributeManager::SP attrMgrResult)
AttributeCollectionSpec::UP
-FastAccessDocSubDB::createAttributeSpec(const AttributesConfig &attrCfg, SerialNum serialNum) const
+FastAccessDocSubDB::createAttributeSpec(const AttributesConfig &attrCfg, const AllocStrategy& alloc_strategy, SerialNum serialNum) const
{
uint32_t docIdLimit(_dms->getCommittedDocIdLimit());
- AttributeCollectionSpecFactory factory(_attributeGrow,
- _attributeGrowNumDocs, _fastAccessAttributesOnly);
+ AttributeCollectionSpecFactory factory(alloc_strategy, _fastAccessAttributesOnly);
return factory.create(attrCfg, docIdLimit, serialNum);
}
@@ -246,7 +246,8 @@ FastAccessDocSubDB::applyConfig(const DocumentDBConfig &newConfigSnapshot, const
{
(void) resolver;
- reconfigure(newConfigSnapshot.getStoreConfig());
+ AllocStrategy alloc_strategy = newConfigSnapshot.get_alloc_config().make_alloc_strategy(_subDbType);
+ reconfigure(newConfigSnapshot.getStoreConfig(), alloc_strategy);
IReprocessingTask::List tasks;
/*
* If attribute manager should change then document retriever
@@ -262,7 +263,7 @@ FastAccessDocSubDB::applyConfig(const DocumentDBConfig &newConfigSnapshot, const
std::make_unique<AttributeWriterFactory>(), getSubDbName());
proton::IAttributeManager::SP oldMgr = extractAttributeManager(_fastAccessFeedView.get());
AttributeCollectionSpec::UP attrSpec =
- createAttributeSpec(newConfigSnapshot.getAttributesConfig(), serialNum);
+ createAttributeSpec(newConfigSnapshot.getAttributesConfig(), alloc_strategy, serialNum);
IReprocessingInitializer::UP initializer =
configurer.reconfigure(newConfigSnapshot, oldConfigSnapshot, *attrSpec);
if (initializer->hasReprocessors()) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.h b/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.h
index 2bf574aba10..7e2ac3a67cc 100644
--- a/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.h
+++ b/searchcore/src/vespa/searchcore/proton/server/fast_access_doc_subdb.h
@@ -84,7 +84,7 @@ protected:
MetricsWireService &_metricsWireService;
DocIdLimit _docIdLimit;
- AttributeCollectionSpec::UP createAttributeSpec(const AttributesConfig &attrCfg, SerialNum serialNum) const;
+ AttributeCollectionSpec::UP createAttributeSpec(const AttributesConfig &attrCfg, const AllocStrategy& alloc_strategy, SerialNum serialNum) const;
AttributeManager::SP getAndResetInitAttributeManager();
virtual IFlushTargetList getFlushTargetsInternal() override;
void reconfigureAttributeMetrics(const IAttributeManager &newMgr, const IAttributeManager &oldMgr);
diff --git a/searchcore/src/vespa/searchcore/proton/server/i_move_operation_limiter.h b/searchcore/src/vespa/searchcore/proton/server/i_move_operation_limiter.h
index d9fc426b08a..cc91413826c 100644
--- a/searchcore/src/vespa/searchcore/proton/server/i_move_operation_limiter.h
+++ b/searchcore/src/vespa/searchcore/proton/server/i_move_operation_limiter.h
@@ -11,7 +11,7 @@ namespace proton {
* Interface used to limit the number of outstanding move operations a blockable maintenance job can have.
*/
struct IMoveOperationLimiter {
- virtual ~IMoveOperationLimiter() {}
+ virtual ~IMoveOperationLimiter() = default;
virtual std::shared_ptr<vespalib::IDestructorCallback> beginOperation() = 0;
};
diff --git a/searchcore/src/vespa/searchcore/proton/server/ibucketmodifiedhandler.h b/searchcore/src/vespa/searchcore/proton/server/ibucketmodifiedhandler.h
index 652c303283f..1c23c35e082 100644
--- a/searchcore/src/vespa/searchcore/proton/server/ibucketmodifiedhandler.h
+++ b/searchcore/src/vespa/searchcore/proton/server/ibucketmodifiedhandler.h
@@ -10,7 +10,7 @@ class IBucketModifiedHandler
{
public:
virtual void notifyBucketModified(const document::BucketId &bucket) = 0;
- virtual ~IBucketModifiedHandler() {}
+ virtual ~IBucketModifiedHandler() = default;
};
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/imaintenancejobrunner.h b/searchcore/src/vespa/searchcore/proton/server/imaintenancejobrunner.h
index 617aa69631f..f733821a47b 100644
--- a/searchcore/src/vespa/searchcore/proton/server/imaintenancejobrunner.h
+++ b/searchcore/src/vespa/searchcore/proton/server/imaintenancejobrunner.h
@@ -14,7 +14,7 @@ public:
* Schedule job to be run in the future.
*/
virtual void run() = 0;
- virtual ~IMaintenanceJobRunner() { }
+ virtual ~IMaintenanceJobRunner() = default;
};
} // namespace proton
diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job.cpp b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job.cpp
index 78efdaebe16..095169b84ce 100644
--- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job.cpp
@@ -30,7 +30,7 @@ LidSpaceCompactionJob::scanDocuments(const LidUsageStats &stats)
if ( ! op ) {
return false;
}
- vespalib::IDestructorCallback::SP context = _moveOpsLimiter->beginOperation();
+ vespalib::IDestructorCallback::SP context = getLimiter().beginOperation();
_opStorer.appendOperation(*op, context);
_handler->handleMove(*op, std::move(context));
if (isBlocked(BlockedReason::OUTSTANDING_OPS)) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp
index 6e1b8ed79b5..cbf3de20b1f 100644
--- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.cpp
@@ -27,7 +27,7 @@ CompactionJob::scanDocuments(const LidUsageStats &stats)
DocumentMetaData document = getNextDocument(stats, false);
if (document.valid()) {
Bucket metaBucket(document::Bucket(_bucketSpace, document.bucketId));
- IDestructorCallback::SP context = _moveOpsLimiter->beginOperation();
+ IDestructorCallback::SP context = getLimiter().beginOperation();
auto failed = _bucketExecutor.execute(metaBucket, makeBucketTask([this, meta=document, opsTracker=std::move(context)] (const Bucket & bucket, std::shared_ptr<IDestructorCallback> onDone) {
assert(bucket.getBucketId() == meta.bucketId);
using DoneContext = vespalib::KeepAlive<std::pair<IDestructorCallback::SP, IDestructorCallback::SP>>;
diff --git a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h
index 35e02246255..053950ebd2f 100644
--- a/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h
+++ b/searchcore/src/vespa/searchcore/proton/server/lid_space_compaction_job_take2.h
@@ -6,7 +6,8 @@
#include <vespa/document/bucket/bucketspace.h>
namespace storage::spi { struct BucketExecutor;}
-namespace searchcorespi::index { class IThreadService; }
+namespace searchcorespi::index { struct IThreadService; }
+namespace vespalib { class IDestructorCallback; }
namespace proton {
class IDiskMemUsageNotifier;
class IClusterStateChangedNotifier;
diff --git a/searchcore/src/vespa/searchcore/proton/server/move_operation_limiter.h b/searchcore/src/vespa/searchcore/proton/server/move_operation_limiter.h
index 01737336a58..b5c8b1b9998 100644
--- a/searchcore/src/vespa/searchcore/proton/server/move_operation_limiter.h
+++ b/searchcore/src/vespa/searchcore/proton/server/move_operation_limiter.h
@@ -35,7 +35,7 @@ private:
public:
using SP = std::shared_ptr<MoveOperationLimiter>;
MoveOperationLimiter(IBlockableMaintenanceJob *job, uint32_t maxOutstandingOps);
- ~MoveOperationLimiter();
+ ~MoveOperationLimiter() override;
void clearJob();
bool isAboveLimit() const;
bool hasPending() const;
diff --git a/searchcore/src/vespa/searchcore/proton/server/proton.cpp b/searchcore/src/vespa/searchcore/proton/server/proton.cpp
index 6706581f52c..1b615d765f0 100644
--- a/searchcore/src/vespa/searchcore/proton/server/proton.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/proton.cpp
@@ -18,14 +18,16 @@
#include <vespa/document/base/exceptions.h>
#include <vespa/document/datatype/documenttype.h>
#include <vespa/document/repo/documenttyperepo.h>
+#include <vespa/metrics/updatehook.h>
+#include <vespa/searchcore/proton/attribute/i_attribute_usage_listener.h>
#include <vespa/searchcore/proton/flushengine/flush_engine_explorer.h>
#include <vespa/searchcore/proton/flushengine/flushengine.h>
#include <vespa/searchcore/proton/flushengine/tls_stats_factory.h>
#include <vespa/searchcore/proton/matchengine/matchengine.h>
+#include <vespa/searchcore/proton/persistenceengine/persistenceengine.h>
#include <vespa/searchcore/proton/reference/document_db_reference_registry.h>
#include <vespa/searchcore/proton/summaryengine/docsum_by_slime.h>
#include <vespa/searchcore/proton/summaryengine/summaryengine.h>
-#include <vespa/searchcore/proton/persistenceengine/persistenceengine.h>
#include <vespa/searchlib/common/packets.h>
#include <vespa/searchlib/transactionlog/trans_log_server_explorer.h>
#include <vespa/searchlib/transactionlog/translogserverapp.h>
@@ -36,7 +38,6 @@
#include <vespa/vespalib/util/host_name.h>
#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/vespalib/util/random.h>
-#include <vespa/metrics/updatehook.h>
#include <vespa/searchlib/aggregation/forcelink.hpp>
#include <vespa/searchlib/expression/forcelink.hpp>
@@ -303,7 +304,7 @@ Proton::init(const BootstrapConfig::SP & configSnapshot)
vespalib::chdir(protonConfig.basedir);
_tls->start();
_flushEngine = std::make_unique<FlushEngine>(std::make_shared<flushengine::TlsStatsFactory>(_tls->getTransLogServer()),
- strategy, flush.maxconcurrent, flush.idleinterval*1000);
+ strategy, flush.maxconcurrent, vespalib::from_s(flush.idleinterval));
_metricsEngine->addExternalMetrics(_summaryEngine->getMetrics());
char tmp[1024];
@@ -630,6 +631,8 @@ Proton::addDocumentDB(const document::DocumentType &docType,
}
// TODO: Fix race with new cluster state setting.
_persistenceEngine->putHandler(persistenceWGuard, bucketSpace, docTypeName, persistenceHandler);
+ ret->set_attribute_usage_listener(
+ _persistenceEngine->get_resource_usage_tracker().make_attribute_usage_listener(docTypeName.getName()));
}
auto searchHandler = std::make_shared<SearchHandlerProxy>(ret);
_summaryEngine->putSearchHandler(docTypeName, searchHandler);
diff --git a/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp b/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp
index 4fc241571ac..bb6c5423175 100644
--- a/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/reconfig_params.cpp
@@ -27,7 +27,8 @@ ReconfigParams::configHasChanged() const
_res.tuneFileDocumentDBChanged ||
_res.schemaChanged ||
_res.maintenanceChanged ||
- _res.storeChanged;
+ _res.storeChanged ||
+ _res.alloc_config_changed;
}
bool
@@ -51,7 +52,7 @@ ReconfigParams::shouldIndexManagerChange() const
bool
ReconfigParams::shouldAttributeManagerChange() const
{
- return _res.attributesChanged || _res.importedFieldsChanged || _res.visibilityDelayChanged;
+ return _res.attributesChanged || _res.importedFieldsChanged || _res.visibilityDelayChanged || _res.alloc_config_changed;
}
bool
diff --git a/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp b/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
index 2768c7ea337..51e6f8e45df 100644
--- a/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/searchabledocsubdb.cpp
@@ -7,6 +7,7 @@
#include "i_document_subdb_owner.h"
#include "ibucketstatecalculator.h"
#include <vespa/searchcore/proton/attribute/attribute_writer.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/flushengine/threadedflushtarget.h>
#include <vespa/searchcore/proton/index/index_manager_initializer.h>
#include <vespa/searchcore/proton/index/index_writer.h>
@@ -143,7 +144,8 @@ IReprocessingTask::List
SearchableDocSubDB::applyConfig(const DocumentDBConfig &newConfigSnapshot, const DocumentDBConfig &oldConfigSnapshot,
SerialNum serialNum, const ReconfigParams &params, IDocumentDBReferenceResolver &resolver)
{
- StoreOnlyDocSubDB::reconfigure(newConfigSnapshot.getStoreConfig());
+ AllocStrategy alloc_strategy = newConfigSnapshot.get_alloc_config().make_alloc_strategy(_subDbType);
+ StoreOnlyDocSubDB::reconfigure(newConfigSnapshot.getStoreConfig(), alloc_strategy);
IReprocessingTask::List tasks;
applyFlushConfig(newConfigSnapshot.getMaintenanceConfigSP()->getFlushConfig());
if (params.shouldMatchersChange() && _addMetrics) {
@@ -152,7 +154,7 @@ SearchableDocSubDB::applyConfig(const DocumentDBConfig &newConfigSnapshot, const
if (params.shouldAttributeManagerChange()) {
proton::IAttributeManager::SP oldMgr = getAttributeManager();
AttributeCollectionSpec::UP attrSpec =
- createAttributeSpec(newConfigSnapshot.getAttributesConfig(), serialNum);
+ createAttributeSpec(newConfigSnapshot.getAttributesConfig(), alloc_strategy, serialNum);
IReprocessingInitializer::UP initializer =
_configurer.reconfigure(newConfigSnapshot, oldConfigSnapshot, *attrSpec, params, resolver);
if (initializer && initializer->hasReprocessors()) {
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp b/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp
index 2aeece204fb..9b504c74635 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.cpp
@@ -10,6 +10,7 @@
#include "storeonlydocsubdb.h"
#include <vespa/searchcore/proton/attribute/attribute_writer.h>
#include <vespa/searchcore/proton/bucketdb/ibucketdbhandlerinitializer.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/docsummary/summaryflushtarget.h>
#include <vespa/searchcore/proton/docsummary/summarymanagerinitializer.h>
#include <vespa/searchcore/proton/documentmetastore/documentmetastoreinitializer.h>
@@ -28,6 +29,7 @@
#include <vespa/log/log.h>
LOG_SETUP(".proton.server.storeonlydocsubdb");
+using search::CompactionStrategy;
using search::GrowStrategy;
using search::AttributeGuard;
using search::AttributeVector;
@@ -56,13 +58,10 @@ IIndexWriter::SP nullIndexWriter;
StoreOnlyDocSubDB::Config::Config(const DocTypeName &docTypeName, const vespalib::string &subName,
const vespalib::string &baseDir,
- const search::GrowStrategy &attributeGrow, size_t attributeGrowNumDocs,
uint32_t subDbId, SubDbType subDbType)
: _docTypeName(docTypeName),
_subName(subName),
_baseDir(baseDir + "/" + subName),
- _attributeGrow(attributeGrow),
- _attributeGrowNumDocs(attributeGrowNumDocs),
_subDbId(subDbId),
_subDbType(subDbType)
{ }
@@ -99,8 +98,6 @@ StoreOnlyDocSubDB::StoreOnlyDocSubDB(const Config &cfg, const Context &ctx)
_bucketDB(ctx._bucketDB),
_bucketDBHandlerInitializer(ctx._bucketDBHandlerInitializer),
_metaStoreCtx(),
- _attributeGrow(cfg._attributeGrow),
- _attributeGrowNumDocs(cfg._attributeGrowNumDocs),
_flushedDocumentMetaStoreSerialNum(0u),
_flushedDocumentStoreSerialNum(0u),
_dms(),
@@ -222,11 +219,12 @@ StoreOnlyDocSubDB::getNewestFlushedSerial()
initializer::InitializerTask::SP
StoreOnlyDocSubDB::
createSummaryManagerInitializer(const search::LogDocumentStore::Config & storeCfg,
+ const AllocStrategy& alloc_strategy,
const search::TuneFileSummary &tuneFile,
search::IBucketizer::SP bucketizer,
std::shared_ptr<SummaryManager::SP> result) const
{
- GrowStrategy grow = _attributeGrow;
+ GrowStrategy grow = alloc_strategy.get_grow_strategy();
vespalib::string baseDir(_baseDir + "/summary");
return std::make_shared<SummaryManagerInitializer>
(grow, baseDir, getSubDbName(), _docTypeName, _writeService.shared(),
@@ -245,12 +243,13 @@ StoreOnlyDocSubDB::setupSummaryManager(SummaryManager::SP summaryManager)
InitializerTask::SP
StoreOnlyDocSubDB::
-createDocumentMetaStoreInitializer(const search::TuneFileAttributes &tuneFile,
+createDocumentMetaStoreInitializer(const AllocStrategy& alloc_strategy,
+ const search::TuneFileAttributes &tuneFile,
std::shared_ptr<DocumentMetaStoreInitializerResult::SP> result) const
{
- GrowStrategy grow = _attributeGrow;
+ GrowStrategy grow = alloc_strategy.get_grow_strategy();
// Amortize memory spike cost over N docs
- grow.setDocsGrowDelta(grow.getDocsGrowDelta() + _attributeGrowNumDocs);
+ grow.setDocsGrowDelta(grow.getDocsGrowDelta() + alloc_strategy.get_amortize_count());
vespalib::string baseDir(_baseDir + "/documentmetastore");
vespalib::string name = DocumentMetaStore::getFixedName();
vespalib::string attrFileName = baseDir + "/" + name; // XXX: Wrong
@@ -292,10 +291,13 @@ StoreOnlyDocSubDB::createInitializer(const DocumentDBConfig &configSnapshot, Ser
{
auto result = std::make_unique<DocumentSubDbInitializer>(const_cast<StoreOnlyDocSubDB &>(*this),
_writeService.master());
- auto dmsInitTask = createDocumentMetaStoreInitializer(configSnapshot.getTuneFileDocumentDBSP()->_attr,
+ AllocStrategy alloc_strategy = configSnapshot.get_alloc_config().make_alloc_strategy(_subDbType);
+ auto dmsInitTask = createDocumentMetaStoreInitializer(alloc_strategy,
+ configSnapshot.getTuneFileDocumentDBSP()->_attr,
result->writableResult().writableDocumentMetaStore());
result->addDocumentMetaStoreInitTask(dmsInitTask);
auto summaryTask = createSummaryManagerInitializer(configSnapshot.getStoreConfig(),
+ alloc_strategy,
configSnapshot.getTuneFileDocumentDBSP()->_summary,
result->result().documentMetaStore()->documentMetaStore(),
result->writableResult().writableSummaryManager());
@@ -409,14 +411,22 @@ StoreOnlyDocSubDB::applyConfig(const DocumentDBConfig &newConfigSnapshot, const
(void) params;
(void) resolver;
assert(_writeService.master().isCurrentThread());
- reconfigure(newConfigSnapshot.getStoreConfig());
+ AllocStrategy alloc_strategy = newConfigSnapshot.get_alloc_config().make_alloc_strategy(_subDbType);
+ reconfigure(newConfigSnapshot.getStoreConfig(), alloc_strategy);
initFeedView(newConfigSnapshot);
return IReprocessingTask::List();
}
void
-StoreOnlyDocSubDB::reconfigure(const search::LogDocumentStore::Config & config)
+StoreOnlyDocSubDB::reconfigure(const search::LogDocumentStore::Config & config, const AllocStrategy& alloc_strategy)
{
+ auto cfg = _dms->getConfig();
+ GrowStrategy grow = alloc_strategy.get_grow_strategy();
+ // Amortize memory spike cost over N docs
+ grow.setDocsGrowDelta(grow.getDocsGrowDelta() + alloc_strategy.get_amortize_count());
+ cfg.setGrowStrategy(grow);
+ cfg.setCompactionStrategy(alloc_strategy.get_compaction_strategy());
+ _dms->update_config(cfg); // Update grow and compaction config
_rSummaryMgr->reconfigure(config);
}
diff --git a/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.h b/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.h
index 7c3f7c82eb0..37229bd551c 100644
--- a/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.h
+++ b/searchcore/src/vespa/searchcore/proton/server/storeonlydocsubdb.h
@@ -20,6 +20,7 @@
namespace proton {
+class AllocStrategy;
struct DocumentDBTaggedMetrics;
class DocumentMetaStoreInitializerResult;
class FeedHandler;
@@ -85,14 +86,12 @@ public:
const DocTypeName _docTypeName;
const vespalib::string _subName;
const vespalib::string _baseDir;
- const search::GrowStrategy _attributeGrow;
- const size_t _attributeGrowNumDocs;
const uint32_t _subDbId;
const SubDbType _subDbType;
Config(const DocTypeName &docTypeName, const vespalib::string &subName,
- const vespalib::string &baseDir, const search::GrowStrategy &attributeGrow,
- size_t attributeGrowNumDocs, uint32_t subDbId, SubDbType subDbType);
+ const vespalib::string &baseDir,
+ uint32_t subDbId, SubDbType subDbType);
~Config();
};
@@ -130,8 +129,6 @@ protected:
BucketDBOwner::SP _bucketDB;
bucketdb::IBucketDBHandlerInitializer &_bucketDBHandlerInitializer;
IDocumentMetaStoreContext::SP _metaStoreCtx;
- const search::GrowStrategy _attributeGrow;
- const size_t _attributeGrowNumDocs;
// The following two serial numbers reflect state at program startup
// and are used by replay logic.
SerialNum _flushedDocumentMetaStoreSerialNum;
@@ -164,6 +161,7 @@ protected:
std::shared_ptr<initializer::InitializerTask>
createSummaryManagerInitializer(const search::LogDocumentStore::Config & protonSummaryCfg,
+ const AllocStrategy& alloc_strategy,
const search::TuneFileSummary &tuneFile,
search::IBucketizer::SP bucketizer,
std::shared_ptr<SummaryManager::SP> result) const;
@@ -171,7 +169,8 @@ protected:
void setupSummaryManager(SummaryManager::SP summaryManager);
std::shared_ptr<initializer::InitializerTask>
- createDocumentMetaStoreInitializer(const search::TuneFileAttributes &tuneFile,
+ createDocumentMetaStoreInitializer(const AllocStrategy& alloc_strategy,
+ const search::TuneFileAttributes &tuneFile,
std::shared_ptr<std::shared_ptr<DocumentMetaStoreInitializerResult>> result) const;
void setupDocumentMetaStore(std::shared_ptr<DocumentMetaStoreInitializerResult> dmsResult);
@@ -181,7 +180,8 @@ protected:
StoreOnlyFeedView::PersistentParams getFeedViewPersistentParams();
vespalib::string getSubDbName() const;
- void reconfigure(const search::LogDocumentStore::Config & protonConfig);
+ void reconfigure(const search::LogDocumentStore::Config & protonConfig,
+ const AllocStrategy& alloc_strategy);
public:
StoreOnlyDocSubDB(const Config &cfg, const Context &ctx);
~StoreOnlyDocSubDB() override;
diff --git a/searchcore/src/vespa/searchcore/proton/server/transactionlogmanager.cpp b/searchcore/src/vespa/searchcore/proton/server/transactionlogmanager.cpp
index 9c928f49ca8..b776e32e0db 100644
--- a/searchcore/src/vespa/searchcore/proton/server/transactionlogmanager.cpp
+++ b/searchcore/src/vespa/searchcore/proton/server/transactionlogmanager.cpp
@@ -20,7 +20,7 @@ void
TransactionLogManager::doLogReplayComplete(const vespalib::string &domainName,
vespalib::duration elapsedTime) const
{
- EventLogger::transactionLogReplayComplete(domainName, vespalib::count_ms(elapsedTime));
+ EventLogger::transactionLogReplayComplete(domainName, elapsedTime);
}
diff --git a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp
index 335c7aca24c..17cb91ac4ce 100644
--- a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp
+++ b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.cpp
@@ -10,6 +10,7 @@
#include <vespa/searchsummary/config/config-juniperrc.h>
#include <vespa/document/config/config-documenttypes.h>
#include <vespa/config-imported-fields.h>
+#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/server/threading_service_config.h>
using document::DocumenttypesConfig;
@@ -47,6 +48,7 @@ DocumentDBConfigBuilder::DocumentDBConfigBuilder(int64_t generation,
_maintenance(std::make_shared<DocumentDBMaintenanceConfig>()),
_store(),
_threading_service_config(std::make_shared<const ThreadingServiceConfig>(ThreadingServiceConfig::make(1))),
+ _alloc_config(std::make_shared<const AllocConfig>()),
_configId(configId),
_docTypeName(docTypeName)
{ }
@@ -70,6 +72,7 @@ DocumentDBConfigBuilder::DocumentDBConfigBuilder(const DocumentDBConfig &cfg)
_maintenance(cfg.getMaintenanceConfigSP()),
_store(cfg.getStoreConfig()),
_threading_service_config(cfg.get_threading_service_config_shared_ptr()),
+ _alloc_config(cfg.get_alloc_config_shared_ptr()),
_configId(cfg.getConfigId()),
_docTypeName(cfg.getDocTypeName())
{}
@@ -97,6 +100,7 @@ DocumentDBConfigBuilder::build()
_maintenance,
_store,
_threading_service_config,
+ _alloc_config,
_configId,
_docTypeName);
}
diff --git a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h
index 218a7c56fa9..706e14e73db 100644
--- a/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h
+++ b/searchcore/src/vespa/searchcore/proton/test/documentdb_config_builder.h
@@ -28,6 +28,7 @@ private:
DocumentDBConfig::MaintenanceConfigSP _maintenance;
search::LogDocumentStore::Config _store;
std::shared_ptr<const ThreadingServiceConfig> _threading_service_config;
+ std::shared_ptr<const AllocConfig> _alloc_config;
vespalib::string _configId;
vespalib::string _docTypeName;
diff --git a/searchcore/src/vespa/searchcore/proton/test/simple_job_tracker.h b/searchcore/src/vespa/searchcore/proton/test/simple_job_tracker.h
index cf4decf0f4a..9048fb7a356 100644
--- a/searchcore/src/vespa/searchcore/proton/test/simple_job_tracker.h
+++ b/searchcore/src/vespa/searchcore/proton/test/simple_job_tracker.h
@@ -11,7 +11,7 @@ struct SimpleJobTracker : public IJobTracker
typedef std::shared_ptr<SimpleJobTracker> SP;
vespalib::CountDownLatch _started;
vespalib::CountDownLatch _ended;
- SimpleJobTracker(uint32_t numJobTrackings)
+ SimpleJobTracker(uint32_t numJobTrackings) noexcept
: _started(numJobTrackings),
_ended(numJobTrackings)
{}
diff --git a/searchlib/src/tests/attribute/compaction/attribute_compaction_test.cpp b/searchlib/src/tests/attribute/compaction/attribute_compaction_test.cpp
index dde6b14121d..36a697eaa12 100644
--- a/searchlib/src/tests/attribute/compaction/attribute_compaction_test.cpp
+++ b/searchlib/src/tests/attribute/compaction/attribute_compaction_test.cpp
@@ -226,4 +226,24 @@ TEST_F("Compaction is not executed when free lists are used",
EXPECT_EQUAL(1001u, afterSpace.dead());
}
+TEST_F("Compaction is peformed when compaction strategy is changed to enable compaction",
+ Fixture(compactAddressSpaceAttributeConfig(false)))
+{
+ populate_and_hammer(f, true);
+ AddressSpace after1 = f.getMultiValueAddressSpaceUsage("after1");
+ // 100 * 1000 dead arrays due to new values for docids
+ // 1 reserved array accounted as dead
+ EXPECT_EQUAL(100001u, after1.dead());
+ f._v->update_config(compactAddressSpaceAttributeConfig(true));
+ auto old_dead = after1.dead();
+ AddressSpace after2 = f.getMultiValueAddressSpaceUsage("after2");
+ while (after2.dead() < old_dead) {
+ old_dead = after2.dead();
+ f._v->commit(); // new commit might trigger further compaction
+ after2 = f.getMultiValueAddressSpaceUsage("after2");
+ }
+ // DEAD_ARRAYS_SLACK in multi value mapping is is 64k
+ EXPECT_GREATER(65536u, after2.dead());
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/tests/transactionlog/translogclient_test.cpp b/searchlib/src/tests/transactionlog/translogclient_test.cpp
index e097eebd42c..fd598eb2d31 100644
--- a/searchlib/src/tests/transactionlog/translogclient_test.cpp
+++ b/searchlib/src/tests/transactionlog/translogclient_test.cpp
@@ -6,6 +6,7 @@
#include <vespa/searchlib/index/dummyfileheadercontext.h>
#include <vespa/document/util/bytebuffer.h>
#include <vespa/fastos/file.h>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("translogclient_test");
diff --git a/searchlib/src/vespa/searchlib/attribute/attributesaver.h b/searchlib/src/vespa/searchlib/attribute/attributesaver.h
index f90f9492487..c1bf18e775c 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributesaver.h
+++ b/searchlib/src/vespa/searchlib/attribute/attributesaver.h
@@ -2,11 +2,10 @@
#pragma once
-#include <vespa/vespalib/util/generationhandler.h>
#include "attribute_header.h"
+#include <vespa/vespalib/util/generationhandler.h>
-namespace search
-{
+namespace search {
class IAttributeSaveTarget;
diff --git a/searchlib/src/vespa/searchlib/attribute/attributevector.cpp b/searchlib/src/vespa/searchlib/attribute/attributevector.cpp
index 2168bbe4276..d2574bd32a2 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributevector.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attributevector.cpp
@@ -21,6 +21,7 @@
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/searchlib/util/logutil.h>
#include <vespa/searchcommon/attribute/attribute_utils.h>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP(".searchlib.attribute.attributevector");
@@ -769,6 +770,31 @@ AttributeVector::logEnumStoreEvent(const char *reason, const char *stage)
EV_STATE(eventName.c_str(), jstr.toString().data());
}
+void
+AttributeVector::drain_hold(uint64_t hold_limit)
+{
+ incGeneration();
+ for (int retry = 0; ; ++retry) {
+ removeAllOldGenerations();
+ updateStat(true);
+ if (_status.getOnHold() <= hold_limit) {
+ return;
+ }
+ std::this_thread::sleep_for(retry < 20 ? 20ms : 100ms);
+ }
+}
+
+void
+AttributeVector::update_config(const Config& cfg)
+{
+ commit(true);
+ drain_hold(1024 * 1024); // Wait until 1MiB or less on hold
+ _config.setGrowStrategy(cfg.getGrowStrategy());
+ _config.setCompactionStrategy(cfg.getCompactionStrategy());
+ commit(); // might trigger compaction if compaction strategy changed
+ drain_hold(1024 * 1024); // Wait until 1MiB or less on hold
+}
+
template bool AttributeVector::append<StringChangeData>(ChangeVectorT< ChangeTemplate<StringChangeData> > &changes, uint32_t , const StringChangeData &, int32_t, bool);
template bool AttributeVector::update<StringChangeData>(ChangeVectorT< ChangeTemplate<StringChangeData> > &changes, uint32_t , const StringChangeData &);
template bool AttributeVector::remove<StringChangeData>(ChangeVectorT< ChangeTemplate<StringChangeData> > &changes, uint32_t , const StringChangeData &, int32_t);
diff --git a/searchlib/src/vespa/searchlib/attribute/attributevector.h b/searchlib/src/vespa/searchlib/attribute/attributevector.h
index 4fc6589850e..f308ee8d024 100644
--- a/searchlib/src/vespa/searchlib/attribute/attributevector.h
+++ b/searchlib/src/vespa/searchlib/attribute/attributevector.h
@@ -393,6 +393,7 @@ public:
/** Return the fixed length of the attribute. If 0 then you must inquire each document. */
size_t getFixedWidth() const override { return _config.basicType().fixedSize(); }
const Config &getConfig() const { return _config; }
+ void update_config(const Config& cfg);
BasicType getInternalBasicType() const { return _config.basicType(); }
CollectionType getInternalCollectionType() const { return _config.collectionType(); }
const BaseName & getBaseFileName() const { return _baseFileName; }
@@ -667,6 +668,8 @@ public:
static bool isEnumerated(const vespalib::GenericHeader &header);
virtual vespalib::MemoryUsage getChangeVectorMemoryUsage() const;
+
+ void drain_hold(uint64_t hold_limit);
};
}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
index def3e49be4d..d2a42d21973 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
@@ -43,10 +43,6 @@ public class DefaultTlsContext implements TlsContext {
this(sslContext, TlsContext.ALLOWED_CIPHER_SUITES, peerAuthentication);
}
- public DefaultTlsContext(SSLContext sslContext) {
- this(sslContext, TlsContext.ALLOWED_CIPHER_SUITES, PeerAuthentication.NEED);
- }
-
DefaultTlsContext(SSLContext sslContext, Set<String> acceptedCiphers, PeerAuthentication peerAuthentication) {
this.sslContext = sslContext;
this.peerAuthentication = peerAuthentication;
diff --git a/slobrok/src/tests/configure/configure.cpp b/slobrok/src/tests/configure/configure.cpp
index 0e5b68053c8..98c716b3a48 100644
--- a/slobrok/src/tests/configure/configure.cpp
+++ b/slobrok/src/tests/configure/configure.cpp
@@ -12,6 +12,7 @@
#include <vespa/vespalib/util/host_name.h>
#include <algorithm>
#include <iostream>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("configure_test");
diff --git a/slobrok/src/tests/mirrorapi/mirrorapi.cpp b/slobrok/src/tests/mirrorapi/mirrorapi.cpp
index 4126f34716f..ae309f8c661 100644
--- a/slobrok/src/tests/mirrorapi/mirrorapi.cpp
+++ b/slobrok/src/tests/mirrorapi/mirrorapi.cpp
@@ -8,6 +8,7 @@
#include <vespa/fnet/frt/supervisor.h>
#include <vespa/fnet/frt/target.h>
#include <vespa/fnet/transport.h>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("mirrorapi_test");
diff --git a/slobrok/src/tests/registerapi/registerapi.cpp b/slobrok/src/tests/registerapi/registerapi.cpp
index 92f08ee41cb..59bc4690985 100644
--- a/slobrok/src/tests/registerapi/registerapi.cpp
+++ b/slobrok/src/tests/registerapi/registerapi.cpp
@@ -8,6 +8,7 @@
#include <vespa/fnet/frt/supervisor.h>
#include <sstream>
#include <algorithm>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("registerapi_test");
diff --git a/slobrok/src/tests/standalone/standalone.cpp b/slobrok/src/tests/standalone/standalone.cpp
index 65553c57530..883aeed1494 100644
--- a/slobrok/src/tests/standalone/standalone.cpp
+++ b/slobrok/src/tests/standalone/standalone.cpp
@@ -3,6 +3,7 @@
#include <vespa/slobrok/server/slobrokserver.h>
#include <vespa/fnet/frt/supervisor.h>
#include <vespa/fnet/frt/target.h>
+#include <thread>
//-----------------------------------------------------------------------------
diff --git a/staging_vespalib/src/tests/clock/clock_test.cpp b/staging_vespalib/src/tests/clock/clock_test.cpp
index 06eee21d2b9..b1572d11dd7 100644
--- a/staging_vespalib/src/tests/clock/clock_test.cpp
+++ b/staging_vespalib/src/tests/clock/clock_test.cpp
@@ -3,6 +3,7 @@
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/util/clock.h>
#include <vespa/fastos/thread.h>
+#include <thread>
using vespalib::Clock;
using vespalib::duration;
diff --git a/staging_vespalib/src/tests/shutdownguard/shutdownguard_test.cpp b/staging_vespalib/src/tests/shutdownguard/shutdownguard_test.cpp
index 04a811494c9..24607add153 100644
--- a/staging_vespalib/src/tests/shutdownguard/shutdownguard_test.cpp
+++ b/staging_vespalib/src/tests/shutdownguard/shutdownguard_test.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/vespalib/testkit/testapp.h>
#include <vespa/vespalib/util/shutdownguard.h>
+#include <thread>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdlib>
diff --git a/staging_vespalib/src/tests/timer/timer_test.cpp b/staging_vespalib/src/tests/timer/timer_test.cpp
index 04b541e75e9..2031e3cdcbe 100644
--- a/staging_vespalib/src/tests/timer/timer_test.cpp
+++ b/staging_vespalib/src/tests/timer/timer_test.cpp
@@ -37,10 +37,10 @@ void Test::testScheduling()
vespalib::CountDownLatch latch1(3);
vespalib::CountDownLatch latch2(2);
ScheduledExecutor timer;
- timer.scheduleAtFixedRate(Task::UP(new TestTask(latch1)), 100ms, 200ms);
- timer.scheduleAtFixedRate(Task::UP(new TestTask(latch2)), 500ms, 500ms);
- EXPECT_TRUE(latch1.await(60000));
- EXPECT_TRUE(latch2.await(60000));
+ timer.scheduleAtFixedRate(std::make_unique<TestTask>(latch1), 100ms, 200ms);
+ timer.scheduleAtFixedRate(std::make_unique<TestTask>(latch2), 500ms, 500ms);
+ EXPECT_TRUE(latch1.await(60s));
+ EXPECT_TRUE(latch2.await(60s));
}
void Test::testReset()
@@ -49,9 +49,9 @@ void Test::testReset()
ScheduledExecutor timer;
timer.scheduleAtFixedRate(std::make_unique<TestTask>(latch1), 2s, 3s);
timer.reset();
- EXPECT_TRUE(!latch1.await(3000));
+ EXPECT_TRUE(!latch1.await(3s));
timer.scheduleAtFixedRate(std::make_unique<TestTask>(latch1), 200ms, 300ms);
- EXPECT_TRUE(latch1.await(60000));
+ EXPECT_TRUE(latch1.await(60s));
}
TEST_APPHOOK(Test)
diff --git a/staging_vespalib/src/vespa/vespalib/util/adaptive_sequenced_executor.h b/staging_vespalib/src/vespa/vespalib/util/adaptive_sequenced_executor.h
index ed2209d130a..4e0388caf8a 100644
--- a/staging_vespalib/src/vespa/vespalib/util/adaptive_sequenced_executor.h
+++ b/staging_vespalib/src/vespa/vespalib/util/adaptive_sequenced_executor.h
@@ -6,7 +6,6 @@
#include <vespa/vespalib/util/arrayqueue.hpp>
#include <vespa/vespalib/util/gate.h>
#include <vespa/vespalib/util/eventbarrier.hpp>
-#include <vespa/vespalib/util/gate.h>
#include <vespa/fastos/thread.h>
#include <mutex>
#include <condition_variable>
diff --git a/storage/src/tests/persistence/filestorage/service_layer_host_info_reporter_test.cpp b/storage/src/tests/persistence/filestorage/service_layer_host_info_reporter_test.cpp
index 86595fb44e8..a54c5c8ccf9 100644
--- a/storage/src/tests/persistence/filestorage/service_layer_host_info_reporter_test.cpp
+++ b/storage/src/tests/persistence/filestorage/service_layer_host_info_reporter_test.cpp
@@ -102,6 +102,28 @@ TEST_F(ServiceLayerHostInfoReporterTest, request_almost_immediate_node_state_as_
EXPECT_EQ(ResourceUsage(0.8, 0.7, {0.1, attr_es_name}, {0.2, attr_mv_name}), get_usage());
}
+TEST_F(ServiceLayerHostInfoReporterTest,
+ first_valid_attribute_enum_store_sample_triggers_immediate_node_state_when_below_slack_diff)
+{
+ // TODO: Assert this is below slack diff when that becomes configurable.
+ constexpr double usage_below_slack_diff = 0.00001;
+ notify(0.0, 0.0, {usage_below_slack_diff, attr_es_name}, {});
+ EXPECT_EQ(1, requested_almost_immediate_replies());
+ EXPECT_EQ(ResourceUsage(0.0, 0.0, {usage_below_slack_diff, attr_es_name}, {}), get_old_usage());
+ EXPECT_EQ(ResourceUsage(0.0, 0.0, {usage_below_slack_diff, attr_es_name}, {}), get_usage());
+}
+
+TEST_F(ServiceLayerHostInfoReporterTest,
+ first_valid_attribute_multi_value_sample_triggers_immediate_node_state_when_below_slack_diff)
+{
+ // TODO: Assert this is below slack diff when that becomes configurable.
+ constexpr double usage_below_slack_diff = 0.00001;
+ notify(0.0, 0.0, {}, {usage_below_slack_diff, attr_mv_name});
+ EXPECT_EQ(1, requested_almost_immediate_replies());
+ EXPECT_EQ(ResourceUsage(0.0, 0.0, {}, {usage_below_slack_diff, attr_mv_name}), get_old_usage());
+ EXPECT_EQ(ResourceUsage(0.0, 0.0, {}, {usage_below_slack_diff, attr_mv_name}), get_usage());
+}
+
TEST_F(ServiceLayerHostInfoReporterTest, json_report_generated)
{
EXPECT_EQ(ResourceUsage(0.0, 0.0), get_slime_usage());
diff --git a/storage/src/vespa/storage/bucketdb/bucketcopy.h b/storage/src/vespa/storage/bucketdb/bucketcopy.h
index 94a1e63e53e..3e93e4594a6 100644
--- a/storage/src/vespa/storage/bucketdb/bucketcopy.h
+++ b/storage/src/vespa/storage/bucketdb/bucketcopy.h
@@ -28,9 +28,9 @@ public:
{
}
- bool trusted() const { return _flags & TRUSTED; }
+ bool trusted() const noexcept { return _flags & TRUSTED; }
- BucketCopy& setTrusted(bool val = true) {
+ BucketCopy& setTrusted(bool val = true) noexcept {
if (!val) {
clearTrusted();
} else {
@@ -40,46 +40,44 @@ public:
return *this;
}
- void clearTrusted() { _flags &= ~TRUSTED; }
+ void clearTrusted() noexcept { _flags &= ~TRUSTED; }
- bool valid() const { return getBucketInfo().valid(); }
- bool empty() const { return getBucketInfo().empty(); }
- bool wasRecentlyCreated() const {
+ bool valid() const noexcept { return getBucketInfo().valid(); }
+ bool empty() const noexcept { return getBucketInfo().empty(); }
+ bool wasRecentlyCreated() const noexcept {
return (getChecksum() == 1
&& getDocumentCount() == 0
&& getTotalDocumentSize() == 0);
}
- static BucketCopy recentlyCreatedCopy(uint64_t timestamp, uint16_t nodeIdx)
- {
+ static BucketCopy recentlyCreatedCopy(uint64_t timestamp, uint16_t nodeIdx) noexcept {
return BucketCopy(timestamp, nodeIdx, api::BucketInfo(1, 0, 0, 0, 0));
}
- uint16_t getNode() const { return _node; }
- uint64_t getTimestamp() const { return _timestamp; }
+ uint16_t getNode() const noexcept { return _node; }
+ uint64_t getTimestamp() const noexcept { return _timestamp; }
- uint32_t getChecksum() const { return _info.getChecksum(); }
- uint32_t getDocumentCount() const { return _info.getDocumentCount(); }
- uint32_t getTotalDocumentSize() const
- { return _info.getTotalDocumentSize(); }
- uint32_t getMetaCount() const { return _info.getMetaCount(); }
- uint32_t getUsedFileSize() const { return _info.getUsedFileSize(); }
- bool active() const { return _info.isActive(); }
- bool ready() const { return _info.isReady(); }
+ uint32_t getChecksum() const noexcept { return _info.getChecksum(); }
+ uint32_t getDocumentCount() const noexcept { return _info.getDocumentCount(); }
+ uint32_t getTotalDocumentSize() const noexcept { return _info.getTotalDocumentSize(); }
+ uint32_t getMetaCount() const noexcept { return _info.getMetaCount(); }
+ uint32_t getUsedFileSize() const noexcept { return _info.getUsedFileSize(); }
+ bool active() const noexcept { return _info.isActive(); }
+ bool ready() const noexcept { return _info.isReady(); }
- const api::BucketInfo& getBucketInfo() const { return _info; }
+ const api::BucketInfo& getBucketInfo() const noexcept { return _info; }
- void setBucketInfo(uint64_t timestamp, const api::BucketInfo& bInfo) {
+ void setBucketInfo(uint64_t timestamp, const api::BucketInfo& bInfo) noexcept {
_info = bInfo;
_timestamp = timestamp;
}
- void setActive(bool setactive) {
+ void setActive(bool setactive) noexcept {
_info.setActive(setactive);
}
bool consistentWith(const BucketCopy& other,
- bool countInvalidAsConsistent = false) const
+ bool countInvalidAsConsistent = false) const noexcept
{
// If both are valid, check checksum and doc count.
if (valid() && other.valid()) {
@@ -94,7 +92,7 @@ public:
std::string toString() const;
- bool operator==(const BucketCopy& other) const {
+ bool operator==(const BucketCopy& other) const noexcept {
return
getBucketInfo() == other.getBucketInfo() &&
_flags == other._flags;
diff --git a/storage/src/vespa/storage/bucketdb/bucketmanager.cpp b/storage/src/vespa/storage/bucketdb/bucketmanager.cpp
index 5be6f310c71..2d70ee8d3ba 100644
--- a/storage/src/vespa/storage/bucketdb/bucketmanager.cpp
+++ b/storage/src/vespa/storage/bucketdb/bucketmanager.cpp
@@ -166,17 +166,17 @@ namespace {
uint64_t active;
uint64_t ready;
- Count() : docs(0), bytes(0), buckets(0), active(0), ready(0) {}
+ Count() noexcept : docs(0), bytes(0), buckets(0), active(0), ready(0) {}
};
Count count;
uint32_t lowestUsedBit;
- MetricsUpdater()
+ MetricsUpdater() noexcept
: count(), lowestUsedBit(58) {}
void operator()(document::BucketId::Type bucketId,
- const StorBucketDatabase::Entry& data)
+ const StorBucketDatabase::Entry& data) noexcept
{
document::BucketId bucket(
document::BucketId::keyToBucketId(bucketId));
@@ -198,7 +198,7 @@ namespace {
}
};
- void add(const MetricsUpdater& rhs) {
+ void add(const MetricsUpdater& rhs) noexcept {
auto& d = count;
auto& s = rhs.count;
d.buckets += s.buckets;
diff --git a/storage/src/vespa/storage/distributor/pending_bucket_space_db_transition_entry.h b/storage/src/vespa/storage/distributor/pending_bucket_space_db_transition_entry.h
index 124ee1bdf45..785419e78cf 100644
--- a/storage/src/vespa/storage/distributor/pending_bucket_space_db_transition_entry.h
+++ b/storage/src/vespa/storage/distributor/pending_bucket_space_db_transition_entry.h
@@ -8,7 +8,7 @@ namespace storage::distributor::dbtransition {
struct Entry {
Entry(const document::BucketId& bid,
- const BucketCopy& copy_)
+ const BucketCopy& copy_) noexcept
: bucket_key(bid.toKey()),
copy(copy_)
{}
diff --git a/storage/src/vespa/storage/distributor/pendingmessagetracker.h b/storage/src/vespa/storage/distributor/pendingmessagetracker.h
index 51971a276b4..13d83157150 100644
--- a/storage/src/vespa/storage/distributor/pendingmessagetracker.h
+++ b/storage/src/vespa/storage/distributor/pendingmessagetracker.h
@@ -196,18 +196,6 @@ private:
// to be present for that exact purpose.
mutable std::mutex _lock;
- /**
- * Increment latency and operation count stats for the node the message
- * was sent towards based on the registered send time and the current time.
- *
- * In the event that system time has moved backwards across sending a
- * command and reciving its reply, the latency will not be recorded but
- * the total number of messages will increase.
- *
- * _lock MUST be held upon invocation.
- */
- void updateNodeStatsOnReply(const MessageEntry& entry);
-
void getStatusStartPage(std::ostream& out) const;
void getStatusPerNode(std::ostream& out) const;
void getStatusPerBucket(std::ostream& out) const;
diff --git a/storage/src/vespa/storage/persistence/asynchandler.cpp b/storage/src/vespa/storage/persistence/asynchandler.cpp
index b8ed6b8ec91..7f8da8e76e7 100644
--- a/storage/src/vespa/storage/persistence/asynchandler.cpp
+++ b/storage/src/vespa/storage/persistence/asynchandler.cpp
@@ -104,7 +104,7 @@ AsyncHandler::handleRunTask(RunTaskCommand& cmd, MessageTracker::UP tracker) con
});
spi::Bucket bucket(cmd.getBucket());
auto onDone = std::make_unique<ResultTaskOperationDone>(_sequencedExecutor, cmd.getBucketId(), std::move(task));
- cmd.task().run(bucket, std::make_shared<vespalib::KeepAlive<decltype(onDone)>>(std::move(onDone)));
+ cmd.run(bucket, std::make_shared<vespalib::KeepAlive<decltype(onDone)>>(std::move(onDone)));
return tracker;
}
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
index 67fa22ada03..c71a7fee424 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.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 "filestorhandlerimpl.h"
#include "filestormanager.h"
+#include "filestorhandlerimpl.h"
#include <vespa/storage/bucketdb/minimumusedbitstracker.h>
#include <vespa/storage/common/bucketmessages.h>
#include <vespa/storage/common/content_bucket_space_repo.h>
@@ -76,9 +76,12 @@ FileStorManager(const config::ConfigUri & configUri, spi::PersistenceProvider& p
_configFetcher(configUri.getContext()),
_use_async_message_handling_on_schedule(false),
_metrics(std::make_unique<FileStorMetrics>()),
+ _filestorHandler(),
+ _sequencedExecutor(),
_closed(false),
_lock(),
- _host_info_reporter(_component.getStateUpdater())
+ _host_info_reporter(_component.getStateUpdater()),
+ _resource_usage_listener_registration(provider.register_resource_usage_listener(_host_info_reporter))
{
_configFetcher.subscribe(configUri.getConfigId(), this);
_configFetcher.start();
@@ -809,6 +812,8 @@ FileStorManager::sendUp(const std::shared_ptr<api::StorageMessage>& msg)
void FileStorManager::onClose()
{
LOG(debug, "Start closing");
+ _bucketExecutorRegistration.reset();
+ _resource_usage_listener_registration.reset();
// Avoid getting config during shutdown
_configFetcher.close();
LOG(debug, "Closed _configFetcher.");
@@ -977,7 +982,10 @@ FileStorManager::execute(const spi::Bucket &bucket, std::unique_ptr<spi::BucketT
StorBucketDatabase::WrappedEntry entry(_component.getBucketDatabase(bucket.getBucketSpace()).get(
bucket.getBucketId(), "FileStorManager::execute"));
if (entry.exist()) {
- _filestorHandler->schedule(std::make_shared<RunTaskCommand>(bucket, std::move(task)));
+ auto cmd = std::make_shared<RunTaskCommand>(bucket, std::move(task));
+ if ( ! _filestorHandler->schedule(cmd) ) {
+ task = cmd->stealTask();
+ }
}
return task;
}
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.h b/storage/src/vespa/storage/persistence/filestorage/filestormanager.h
index ae298d70a29..6eaef45e9bd 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.h
+++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.h
@@ -77,6 +77,7 @@ class FileStorManager : public StorageLinkQueued,
std::mutex _lock;
std::unique_ptr<vespalib::IDestructorCallback> _bucketExecutorRegistration;
ServiceLayerHostInfoReporter _host_info_reporter;
+ std::unique_ptr<vespalib::IDestructorCallback> _resource_usage_listener_registration;
public:
FileStorManager(const config::ConfigUri &, spi::PersistenceProvider&,
diff --git a/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp b/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp
index c78b24bee78..88cdcc2b42f 100644
--- a/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.cpp
@@ -1,8 +1,13 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "service_layer_host_info_reporter.h"
+#include <vespa/persistence/spi/resource_usage.h>
#include <vespa/storage/common/nodestateupdater.h>
#include <cmath>
+#include <sstream>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".persistence.filestor.service_layer_host_info_reporter");
namespace storage {
@@ -33,13 +38,22 @@ void write_attribute_usage(vespalib::JsonStream& output, const vespalib::string
output << End();
}
-bool want_immediate_report(const spi::ResourceUsage& old_resource_usage, const spi::ResourceUsage& resource_usage)
+bool want_immediate_report(const spi::ResourceUsage& old_usage, const spi::ResourceUsage& new_usage)
{
- auto disk_usage_diff = fabs(resource_usage.get_disk_usage() - old_resource_usage.get_disk_usage());
- auto memory_usage_diff = fabs(resource_usage.get_memory_usage() - old_resource_usage.get_memory_usage());
- auto attribute_enum_store_diff = fabs(resource_usage.get_attribute_enum_store_usage().get_usage() - old_resource_usage.get_attribute_enum_store_usage().get_usage());
- auto attribute_multivalue_diff = fabs(resource_usage.get_attribute_multivalue_usage().get_usage() - old_resource_usage.get_attribute_multivalue_usage().get_usage());
- return (disk_usage_diff > diff_slack || memory_usage_diff > diff_slack || attribute_enum_store_diff > diff_slack || attribute_multivalue_diff > diff_slack);
+ auto disk_usage_diff = fabs(new_usage.get_disk_usage() - old_usage.get_disk_usage());
+ auto memory_usage_diff = fabs(new_usage.get_memory_usage() - old_usage.get_memory_usage());
+ auto enum_store_diff = fabs(new_usage.get_attribute_enum_store_usage().get_usage() - old_usage.get_attribute_enum_store_usage().get_usage());
+ auto multivalue_diff = fabs(new_usage.get_attribute_multivalue_usage().get_usage() - old_usage.get_attribute_multivalue_usage().get_usage());
+ bool enum_store_got_valid = !old_usage.get_attribute_enum_store_usage().valid() &&
+ new_usage.get_attribute_enum_store_usage().valid();
+ bool multivalue_got_valid = !old_usage.get_attribute_multivalue_usage().valid() &&
+ new_usage.get_attribute_multivalue_usage().valid();
+ return ((disk_usage_diff > diff_slack) ||
+ (memory_usage_diff > diff_slack) ||
+ (enum_store_diff > diff_slack) ||
+ (multivalue_diff > diff_slack) ||
+ enum_store_got_valid ||
+ multivalue_got_valid);
}
}
@@ -58,10 +72,25 @@ ServiceLayerHostInfoReporter::~ServiceLayerHostInfoReporter()
spi::ResourceUsageListener::reset(); // detach
}
+namespace {
+
+vespalib::string
+to_string(const spi::ResourceUsage& usage)
+{
+ std::ostringstream oss;
+ oss << usage;
+ return oss.str();
+}
+
+}
+
void
ServiceLayerHostInfoReporter::update_resource_usage(const spi::ResourceUsage& resource_usage)
{
bool immediate_report = want_immediate_report(_old_resource_usage, resource_usage);
+ LOG(debug, "update_resource_usage(): immediate_report=%s, old_usage=%s, new_usage=%s",
+ (immediate_report ? "true" : "false"), to_string(_old_resource_usage).c_str(),
+ to_string(resource_usage).c_str());
if (immediate_report) {
_old_resource_usage = resource_usage;
}
@@ -82,6 +111,7 @@ ServiceLayerHostInfoReporter::report(vespalib::JsonStream& output)
{
std::lock_guard guard(_lock);
auto& usage = get_usage();
+ LOG(debug, "report(): usage=%s", to_string(usage).c_str());
write_usage(output, memory_label, usage.get_memory_usage());
write_usage(output, disk_label, usage.get_disk_usage());
write_attribute_usage(output, attribute_enum_store_label, usage.get_attribute_enum_store_usage());
diff --git a/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.h b/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.h
index be0abc94987..b58e047d0af 100644
--- a/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.h
+++ b/storage/src/vespa/storage/persistence/filestorage/service_layer_host_info_reporter.h
@@ -25,7 +25,7 @@ public:
ServiceLayerHostInfoReporter(const ServiceLayerHostInfoReporter&) = delete;
ServiceLayerHostInfoReporter& operator=(const ServiceLayerHostInfoReporter&) = delete;
- ~ServiceLayerHostInfoReporter();
+ ~ServiceLayerHostInfoReporter() override;
void report(vespalib::JsonStream& output) override;
const spi::ResourceUsage &get_old_resource_usage() noexcept { return _old_resource_usage; }
diff --git a/storage/src/vespa/storage/persistence/messages.cpp b/storage/src/vespa/storage/persistence/messages.cpp
index 7ccb3ee895d..beae22a429b 100644
--- a/storage/src/vespa/storage/persistence/messages.cpp
+++ b/storage/src/vespa/storage/persistence/messages.cpp
@@ -187,9 +187,7 @@ RunTaskCommand::RunTaskCommand(const spi::Bucket &bucket, std::unique_ptr<spi::B
: api::InternalCommand(ID),
_task(std::move(task)),
_bucket(bucket)
-{
- assert(_task);
-}
+{ }
RunTaskCommand::~RunTaskCommand() = default;
@@ -203,6 +201,14 @@ RunTaskCommand::print(std::ostream& out, bool verbose, const std::string& indent
}
}
+void
+RunTaskCommand::run(const spi::Bucket & bucket, std::shared_ptr<vespalib::IDestructorCallback> onComplete)
+{
+ if (_task) {
+ _task->run(bucket, std::move(onComplete));
+ }
+}
+
RunTaskReply::RunTaskReply(const RunTaskCommand& cmd)
: api::InternalReply(ID, cmd)
{}
diff --git a/storage/src/vespa/storage/persistence/messages.h b/storage/src/vespa/storage/persistence/messages.h
index 043747d10d2..50834782f39 100644
--- a/storage/src/vespa/storage/persistence/messages.h
+++ b/storage/src/vespa/storage/persistence/messages.h
@@ -249,11 +249,10 @@ public:
document::Bucket getBucket() const override { return _bucket.getBucket(); }
std::unique_ptr<api::StorageReply> makeReply() override;
- spi::BucketTask & task() & {
- return *_task;
- }
+ void run(const spi::Bucket & bucket, std::shared_ptr<vespalib::IDestructorCallback> onComplete);
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
+ std::unique_ptr<spi::BucketTask> stealTask() { return std::move(_task); }
private:
std::unique_ptr<spi::BucketTask> _task;
spi::Bucket _bucket;
diff --git a/storageapi/src/vespa/storageapi/buckets/bucketinfo.cpp b/storageapi/src/vespa/storageapi/buckets/bucketinfo.cpp
index 9e4e48d67f4..4c3b290d4d7 100644
--- a/storageapi/src/vespa/storageapi/buckets/bucketinfo.cpp
+++ b/storageapi/src/vespa/storageapi/buckets/bucketinfo.cpp
@@ -19,8 +19,7 @@ BucketInfo::BucketInfo() noexcept
_active(false)
{}
-BucketInfo::BucketInfo(uint32_t checksum, uint32_t docCount,
- uint32_t totDocSize) noexcept
+BucketInfo::BucketInfo(uint32_t checksum, uint32_t docCount, uint32_t totDocSize) noexcept
: _lastModified(0),
_checksum(checksum),
_docCount(docCount),
@@ -73,7 +72,7 @@ BucketInfo::BucketInfo(uint32_t checksum, uint32_t docCount,
{}
bool
-BucketInfo::operator==(const BucketInfo& info) const
+BucketInfo::operator==(const BucketInfo& info) const noexcept
{
return (_checksum == info._checksum &&
_docCount == info._docCount &&
diff --git a/storageapi/src/vespa/storageapi/buckets/bucketinfo.h b/storageapi/src/vespa/storageapi/buckets/bucketinfo.h
index 5b2de4b4d61..7e6e7a2aed2 100644
--- a/storageapi/src/vespa/storageapi/buckets/bucketinfo.h
+++ b/storageapi/src/vespa/storageapi/buckets/bucketinfo.h
@@ -42,37 +42,37 @@ public:
uint32_t metaCount, uint32_t usedFileSize,
bool ready, bool active, Timestamp lastModified) noexcept;
- Timestamp getLastModified() const { return _lastModified; }
- uint32_t getChecksum() const { return _checksum; }
- uint32_t getDocumentCount() const { return _docCount; }
- uint32_t getTotalDocumentSize() const { return _totDocSize; }
- uint32_t getMetaCount() const { return _metaCount; }
- uint32_t getUsedFileSize() const { return _usedFileSize; }
- bool isReady() const { return _ready; }
- bool isActive() const { return _active; }
+ Timestamp getLastModified() const noexcept { return _lastModified; }
+ uint32_t getChecksum() const noexcept { return _checksum; }
+ uint32_t getDocumentCount() const noexcept { return _docCount; }
+ uint32_t getTotalDocumentSize() const noexcept { return _totDocSize; }
+ uint32_t getMetaCount() const noexcept { return _metaCount; }
+ uint32_t getUsedFileSize() const noexcept { return _usedFileSize; }
+ bool isReady() const noexcept { return _ready; }
+ bool isActive() const noexcept { return _active; }
- void setChecksum(uint32_t crc) { _checksum = crc; }
- void setDocumentCount(uint32_t count) { _docCount = count; }
- void setTotalDocumentSize(uint32_t size) { _totDocSize = size; }
- void setMetaCount(uint32_t count) { _metaCount = count; }
- void setUsedFileSize(uint32_t size) { _usedFileSize = size; }
- void setReady(bool ready = true) { _ready = ready; }
- void setActive(bool active = true) { _active = active; }
- void setLastModified(Timestamp lastModified) { _lastModified = lastModified; }
+ void setChecksum(uint32_t crc) noexcept { _checksum = crc; }
+ void setDocumentCount(uint32_t count) noexcept { _docCount = count; }
+ void setTotalDocumentSize(uint32_t size) noexcept { _totDocSize = size; }
+ void setMetaCount(uint32_t count) noexcept { _metaCount = count; }
+ void setUsedFileSize(uint32_t size) noexcept { _usedFileSize = size; }
+ void setReady(bool ready = true) noexcept { _ready = ready; }
+ void setActive(bool active = true) noexcept { _active = active; }
+ void setLastModified(Timestamp lastModified) noexcept { _lastModified = lastModified; }
/**
* Only compare checksum, total document count and document
* size, not meta count or used file size.
*/
- bool equalDocumentInfo(const BucketInfo& other) const {
+ bool equalDocumentInfo(const BucketInfo& other) const noexcept {
return (_checksum == other._checksum
&& _docCount == other._docCount
&& _totDocSize == other._totDocSize);
}
- bool operator==(const BucketInfo& info) const;
- bool valid() const { return (_docCount > 0 || _totDocSize == 0); }
- bool empty() const {
+ bool operator==(const BucketInfo& info) const noexcept;
+ bool valid() const noexcept { return (_docCount > 0 || _totDocSize == 0); }
+ bool empty() const noexcept {
return _metaCount == 0 && _usedFileSize == 0 && _checksum == 0;
}
vespalib::string toString() const;
diff --git a/vbench/src/apps/vbench/vbench.cpp b/vbench/src/apps/vbench/vbench.cpp
index ab695f0b728..4b3419b2f50 100644
--- a/vbench/src/apps/vbench/vbench.cpp
+++ b/vbench/src/apps/vbench/vbench.cpp
@@ -45,8 +45,8 @@ int run(const std::string &cfg_name) {
vespalib::RunnablePair runBoth(vbench, notify);
vespalib::Thread thread(runBoth);
thread.start();
- while (!SIG::INT.check() && !SIG::TERM.check() && !done.await(1000)) {}
- if (!done.await(0)) {
+ while (!SIG::INT.check() && !SIG::TERM.check() && !done.await(1s)) {}
+ if (!done.await(vespalib::duration::zero())) {
vbench.abort();
done.await();
}
diff --git a/vbench/src/tests/time_queue/time_queue_test.cpp b/vbench/src/tests/time_queue/time_queue_test.cpp
index 5ce4feab747..47c1afbfaa9 100644
--- a/vbench/src/tests/time_queue/time_queue_test.cpp
+++ b/vbench/src/tests/time_queue/time_queue_test.cpp
@@ -16,8 +16,8 @@ TEST_MT_FFF("time queue", 2, TimeQueue<int>(10.0, 5.0), vespalib::Gate(), vespal
} else {
double delay;
std::vector<std::unique_ptr<int> > list;
- EXPECT_TRUE(f2.await(20000));
- EXPECT_FALSE(f3.await(20));
+ EXPECT_TRUE(f2.await(20s));
+ EXPECT_FALSE(f3.await(20ms));
{
f1.extract(1.5, list, delay);
ASSERT_EQUAL(1u, list.size());
@@ -38,7 +38,7 @@ TEST_MT_FFF("time queue", 2, TimeQueue<int>(10.0, 5.0), vespalib::Gate(), vespal
EXPECT_EQUAL(0u, list.size());
EXPECT_EQUAL(5.0, delay);
}
- EXPECT_TRUE(f3.await(20000));
+ EXPECT_TRUE(f3.await(20s));
{
f1.extract(99.25, list, delay);
EXPECT_EQUAL(0u, list.size());
diff --git a/vespalib/src/tests/barrier/barrier_test.cpp b/vespalib/src/tests/barrier/barrier_test.cpp
index f829b7fa5d8..2340476d4e0 100644
--- a/vespalib/src/tests/barrier/barrier_test.cpp
+++ b/vespalib/src/tests/barrier/barrier_test.cpp
@@ -13,9 +13,9 @@ struct Fixture {
TEST_MT_F("require that barriers are satisfied by the appropriate number of threads", 3, Fixture(num_threads)) {
if (thread_id == 0) {
f1.latch.countDown();
- EXPECT_FALSE(f.latch.await(250));
+ EXPECT_FALSE(f.latch.await(250ms));
EXPECT_TRUE(f.barrier.await());
- EXPECT_TRUE(f.latch.await(25000));
+ EXPECT_TRUE(f.latch.await(25s));
} else {
EXPECT_TRUE(f1.barrier.await());
f1.latch.countDown();
@@ -27,9 +27,9 @@ TEST_MT_F("require that barriers can be used multiple times", 3, Fixture(num_thr
EXPECT_TRUE(f1.barrier.await());
if (thread_id == 0) {
f1.latch.countDown();
- EXPECT_FALSE(f.latch.await(250));
+ EXPECT_FALSE(f.latch.await(250ms));
EXPECT_TRUE(f.barrier.await());
- EXPECT_TRUE(f.latch.await(25000));
+ EXPECT_TRUE(f.latch.await(25s));
} else {
EXPECT_TRUE(f1.barrier.await());
f1.latch.countDown();
@@ -40,9 +40,9 @@ TEST_MT_F("require that barriers can be broken", 3, Fixture(num_threads)) {
EXPECT_TRUE(f1.barrier.await());
if (thread_id == 0) {
f1.latch.countDown();
- EXPECT_FALSE(f.latch.await(250));
+ EXPECT_FALSE(f.latch.await(250ms));
f1.barrier.destroy();
- EXPECT_TRUE(f.latch.await(25000));
+ EXPECT_TRUE(f.latch.await(25s));
} else {
EXPECT_FALSE(f1.barrier.await());
f1.latch.countDown();
diff --git a/vespalib/src/tests/benchmark_timer/benchmark_timer_test.cpp b/vespalib/src/tests/benchmark_timer/benchmark_timer_test.cpp
index 3f7187458d8..a1781abca81 100644
--- a/vespalib/src/tests/benchmark_timer/benchmark_timer_test.cpp
+++ b/vespalib/src/tests/benchmark_timer/benchmark_timer_test.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/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/util/benchmark_timer.h>
+#include <thread>
using namespace vespalib;
diff --git a/vespalib/src/tests/executor/blockingthreadstackexecutor_test.cpp b/vespalib/src/tests/executor/blockingthreadstackexecutor_test.cpp
index 46db619516a..a0ef200ab83 100644
--- a/vespalib/src/tests/executor/blockingthreadstackexecutor_test.cpp
+++ b/vespalib/src/tests/executor/blockingthreadstackexecutor_test.cpp
@@ -1,6 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
-
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/util/blockingthreadstackexecutor.h>
#include <vespa/vespalib/util/executor.h>
#include <vespa/vespalib/util/backtrace.h>
@@ -8,7 +8,7 @@
using namespace vespalib;
-constexpr int msWait = 30000;
+constexpr vespalib::duration waitTime = 30s;
class MyTask : public Executor::Task
{
@@ -21,8 +21,8 @@ public:
: _entryGate(entryGate),
_exitLatch(exitLatch)
{}
- virtual void run() override {
- _entryGate.await(msWait);
+ void run() override {
+ _entryGate.await(waitTime);
_exitLatch.countDown();
}
static Task::UP create(Gate &entryGate, CountDownLatch &exitLatch) {
@@ -64,14 +64,14 @@ struct Fixture
workersEntryGate.countDown();
}
void waitForWorkers() {
- workersExitLatch.await(msWait);
+ workersExitLatch.await(waitTime);
}
void assertExecuteIsBlocked() {
- blockedExecuteGate.await(10);
+ blockedExecuteGate.await(10ms);
EXPECT_EQUAL(1u, blockedExecuteGate.getCount());
}
void waitForExecuteIsFinished() {
- blockedExecuteGate.await(msWait);
+ blockedExecuteGate.await(waitTime);
EXPECT_EQUAL(0u, blockedExecuteGate.getCount());
}
ThreadUP blockedExecuteThread() {
diff --git a/vespalib/src/tests/executor/stress_test.cpp b/vespalib/src/tests/executor/stress_test.cpp
index 615e7addfd5..01873fa0480 100644
--- a/vespalib/src/tests/executor/stress_test.cpp
+++ b/vespalib/src/tests/executor/stress_test.cpp
@@ -4,6 +4,7 @@
#include <vespa/vespalib/util/executor.h>
#include <vespa/vespalib/util/threadstackexecutor.h>
#include <vespa/vespalib/locale/c.h>
+#include <thread>
using namespace vespalib;
using namespace std::literals;
diff --git a/vespalib/src/tests/executor/threadstackexecutor_test.cpp b/vespalib/src/tests/executor/threadstackexecutor_test.cpp
index b43a75f8244..e860a25c83c 100644
--- a/vespalib/src/tests/executor/threadstackexecutor_test.cpp
+++ b/vespalib/src/tests/executor/threadstackexecutor_test.cpp
@@ -147,9 +147,9 @@ TEST_MT_F("require that threads can wait for a specific task count", 7, WaitStat
if (next_done < f1.block_task.size()) {
f1.block_task[f1.block_task.size() - 1 - next_done].countDown();
}
- EXPECT_TRUE(f1.wait_done[next_done].await(25000));
+ EXPECT_TRUE(f1.wait_done[next_done].await(25s));
for (size_t i = 0; i < next_done; ++i) {
- EXPECT_TRUE(!f1.wait_done[i].await(20));
+ EXPECT_TRUE(!f1.wait_done[i].await(20ms));
}
}
} else {
diff --git a/vespalib/src/tests/latch/latch_test.cpp b/vespalib/src/tests/latch/latch_test.cpp
index f29b673e508..a0daf7ef7d5 100644
--- a/vespalib/src/tests/latch/latch_test.cpp
+++ b/vespalib/src/tests/latch/latch_test.cpp
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/util/gate.h>
#include <vespa/vespalib/util/latch.h>
@@ -17,9 +18,9 @@ TEST("require that write then read works") {
TEST_MT_FFF("require that read waits for write", 2, Latch<int>(), Gate(), TimeBomb(60)) {
if (thread_id == 0) {
- EXPECT_TRUE(!f2.await(10));
+ EXPECT_TRUE(!f2.await(10ms));
f1.write(123);
- EXPECT_TRUE(f2.await(60000));
+ EXPECT_TRUE(f2.await(60s));
} else {
EXPECT_EQUAL(f1.read(), 123);
f2.countDown();
@@ -32,9 +33,9 @@ TEST_MT_FFF("require that write waits for read", 2, Latch<int>(), Gate(), TimeBo
f1.write(456);
f2.countDown();
} else {
- EXPECT_TRUE(!f2.await(10));
+ EXPECT_TRUE(!f2.await(10ms));
EXPECT_EQUAL(f1.read(), 123);
- EXPECT_TRUE(f2.await(60000));
+ EXPECT_TRUE(f2.await(60s));
EXPECT_EQUAL(f1.read(), 456);
}
}
diff --git a/vespalib/src/tests/net/async_resolver/async_resolver_test.cpp b/vespalib/src/tests/net/async_resolver/async_resolver_test.cpp
index f04fe549c09..8f3cad135ec 100644
--- a/vespalib/src/tests/net/async_resolver/async_resolver_test.cpp
+++ b/vespalib/src/tests/net/async_resolver/async_resolver_test.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/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/net/async_resolver.h>
#include <vespa/vespalib/net/socket_spec.h>
#include <atomic>
diff --git a/vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp b/vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp
index e938e15f4e6..7230f97818f 100644
--- a/vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp
+++ b/vespalib/src/tests/net/crypto_socket/crypto_socket_test.cpp
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/net/crypto_engine.h>
#include <vespa/vespalib/net/tls/tls_crypto_engine.h>
#include <vespa/vespalib/net/tls/maybe_tls_crypto_engine.h>
diff --git a/vespalib/src/tests/net/selector/selector_test.cpp b/vespalib/src/tests/net/selector/selector_test.cpp
index 5c91dfdc122..d13821ce2d0 100644
--- a/vespalib/src/tests/net/selector/selector_test.cpp
+++ b/vespalib/src/tests/net/selector/selector_test.cpp
@@ -1,5 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/net/socket_address.h>
#include <vespa/vespalib/net/selector.h>
#include <vespa/vespalib/net/socket_utils.h>
diff --git a/vespalib/src/tests/net/send_fd/send_fd_test.cpp b/vespalib/src/tests/net/send_fd/send_fd_test.cpp
index e9921f75207..cc367996bd6 100644
--- a/vespalib/src/tests/net/send_fd/send_fd_test.cpp
+++ b/vespalib/src/tests/net/send_fd/send_fd_test.cpp
@@ -1,5 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
+
#include <vespa/vespalib/net/selector.h>
#include <vespa/vespalib/net/socket_spec.h>
#include <vespa/vespalib/net/server_socket.h>
diff --git a/vespalib/src/tests/net/socket/socket_test.cpp b/vespalib/src/tests/net/socket/socket_test.cpp
index 08893c9273b..e3b13b69884 100644
--- a/vespalib/src/tests/net/socket/socket_test.cpp
+++ b/vespalib/src/tests/net/socket/socket_test.cpp
@@ -1,5 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/net/selector.h>
#include <vespa/vespalib/net/socket_spec.h>
#include <vespa/vespalib/net/server_socket.h>
@@ -9,7 +10,6 @@
#include <vespa/vespalib/test/socket_options_verifier.h>
#include <thread>
#include <functional>
-#include <chrono>
#include <unistd.h>
#include <sys/stat.h>
diff --git a/vespalib/src/tests/net/sync_crypto_socket/sync_crypto_socket_test.cpp b/vespalib/src/tests/net/sync_crypto_socket/sync_crypto_socket_test.cpp
index 56767051dad..8a4961b786b 100644
--- a/vespalib/src/tests/net/sync_crypto_socket/sync_crypto_socket_test.cpp
+++ b/vespalib/src/tests/net/sync_crypto_socket/sync_crypto_socket_test.cpp
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/net/crypto_engine.h>
#include <vespa/vespalib/net/tls/tls_crypto_engine.h>
#include <vespa/vespalib/net/tls/maybe_tls_crypto_engine.h>
diff --git a/vespalib/src/tests/net/tls/auto_reloading_tls_crypto_engine/auto_reloading_tls_crypto_engine_test.cpp b/vespalib/src/tests/net/tls/auto_reloading_tls_crypto_engine/auto_reloading_tls_crypto_engine_test.cpp
index 5dc85bc567f..4631f19807e 100644
--- a/vespalib/src/tests/net/tls/auto_reloading_tls_crypto_engine/auto_reloading_tls_crypto_engine_test.cpp
+++ b/vespalib/src/tests/net/tls/auto_reloading_tls_crypto_engine/auto_reloading_tls_crypto_engine_test.cpp
@@ -7,9 +7,7 @@
#include <vespa/vespalib/net/tls/transport_security_options_reading.h>
#include <vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h>
#include <vespa/vespalib/testkit/test_kit.h>
-
-#include <chrono>
-
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <openssl/ssl.h>
using namespace vespalib;
diff --git a/vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp b/vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp
index 4ce25aa0a7a..844622577e2 100644
--- a/vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp
+++ b/vespalib/src/tests/portal/handle_manager/handle_manager_test.cpp
@@ -1,11 +1,8 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/portal/handle_manager.h>
-#include <vespa/vespalib/util/gate.h>
-
-#include <thread>
-#include <chrono>
#include <atomic>
using namespace vespalib;
@@ -85,9 +82,9 @@ TEST_MT_FF("require that destroy waits for active handle guards", 2, Fixture(),
{
auto guard = f1.manager.lock(f1.handle);
TEST_BARRIER(); // #1
- EXPECT_TRUE(!f1.gate.await(20));
+ EXPECT_TRUE(!f1.gate.await(20ms));
}
- EXPECT_TRUE(f1.gate.await(60000));
+ EXPECT_TRUE(f1.gate.await(60s));
} else {
TEST_BARRIER(); // #1
EXPECT_TRUE(f1.manager.destroy(f1.handle));
diff --git a/vespalib/src/tests/portal/portal_test.cpp b/vespalib/src/tests/portal/portal_test.cpp
index 07d3b9e1bfb..d708f81bdde 100644
--- a/vespalib/src/tests/portal/portal_test.cpp
+++ b/vespalib/src/tests/portal/portal_test.cpp
@@ -1,6 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/portal/portal.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -305,7 +306,7 @@ TEST_MT_FF("require that GET requests can be completed in another thread", 2,
if (thread_id == 0) {
Portal::GetRequest req = f1.latch.read();
f1.exit_callback.countDown();
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ std::this_thread::sleep_for(5ms);
req.respond_with_content("text/plain", "hello");
} else {
auto result = fetch(f1.portal->listen_port(), null_crypto(), "/test");
@@ -318,9 +319,9 @@ TEST_MT_FFF("require that bind token destruction waits for active callbacks", 3,
{
if (thread_id == 0) {
Portal::GetRequest req = f1.latch.read();
- EXPECT_TRUE(!f2.await(20));
+ EXPECT_TRUE(!f2.await(20ms));
f1.exit_callback.countDown();
- EXPECT_TRUE(f2.await(60000));
+ EXPECT_TRUE(f2.await(60s));
req.respond_with_content("application/json", "[1,2,3]");
} else if (thread_id == 1) {
f1.enter_callback.await();
@@ -338,9 +339,9 @@ TEST_MT_FFF("require that portal destruction waits for request completion", 3,
if (thread_id == 0) {
Portal::GetRequest req = f1.latch.read();
f1.exit_callback.countDown();
- EXPECT_TRUE(!f2.await(20));
+ EXPECT_TRUE(!f2.await(20ms));
req.respond_with_content("application/json", "[1,2,3]");
- EXPECT_TRUE(f2.await(60000));
+ EXPECT_TRUE(f2.await(60s));
} else if (thread_id == 1) {
f1.enter_callback.await();
f1.bound.reset();
diff --git a/vespalib/src/tests/portal/reactor/reactor_test.cpp b/vespalib/src/tests/portal/reactor/reactor_test.cpp
index b0abc2c9e46..b14bb1dbd3f 100644
--- a/vespalib/src/tests/portal/reactor/reactor_test.cpp
+++ b/vespalib/src/tests/portal/reactor/reactor_test.cpp
@@ -1,14 +1,11 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/net/socket_handle.h>
#include <vespa/vespalib/net/socket_utils.h>
#include <vespa/vespalib/portal/reactor.h>
#include <vespa/vespalib/util/gate.h>
-
-#include <thread>
-#include <chrono>
-
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
@@ -150,9 +147,9 @@ TEST_MT_FFFF("require that reactor token destruction waits for io event handling
if (thread_id == 0) {
f2.enter_callback.await();
TEST_BARRIER(); // #1
- EXPECT_TRUE(!f3.await(20));
+ EXPECT_TRUE(!f3.await(20ms));
f2.exit_callback.countDown();
- EXPECT_TRUE(f3.await(60000));
+ EXPECT_TRUE(f3.await(60s));
} else {
TEST_BARRIER(); // #1
f2.token.reset();
diff --git a/vespalib/src/tests/rendezvous/rendezvous_test.cpp b/vespalib/src/tests/rendezvous/rendezvous_test.cpp
index f4ec7870ad5..6a3e87563ed 100644
--- a/vespalib/src/tests/rendezvous/rendezvous_test.cpp
+++ b/vespalib/src/tests/rendezvous/rendezvous_test.cpp
@@ -149,13 +149,13 @@ TEST_MT_FFFF("require that mingle is not called until all threads are present",
for (bool ext_id: {false, true}) {
CountDownLatch &latch = ext_id ? f4 : f2;
if (thread_id == 0) {
- EXPECT_FALSE(latch.await(20));
+ EXPECT_FALSE(latch.await(20ms));
if (ext_id) {
EXPECT_EQUAL(3u, f3.rendezvous(thread_id, thread_id).first);
} else {
EXPECT_EQUAL(3u, f1.rendezvous(thread_id).first);
}
- EXPECT_TRUE(latch.await(25000));
+ EXPECT_TRUE(latch.await(25s));
} else {
if (ext_id) {
EXPECT_EQUAL(3u, f3.rendezvous(thread_id, thread_id).first);
@@ -183,7 +183,7 @@ TEST_MT_FF("require that rendezvous can be run with additional threads", 100, Ad
EXPECT_EQUAL(4950u, f1.rendezvous(res.first).first);
f2.countDown();
}
- EXPECT_TRUE(f2.await(25000));
+ EXPECT_TRUE(f2.await(25s));
}
TEST_MT_FF("require that mingle can modify its own copy of input values", 10, Modify<false>(num_threads), Modify<true>(num_threads)) {
diff --git a/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp b/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp
index d3c562c583c..178a6c49fca 100644
--- a/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp
+++ b/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp
@@ -3,6 +3,7 @@
#include <vespa/vespalib/util/simple_thread_bundle.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/util/box.h>
+#include <thread>
using namespace vespalib;
using namespace vespalib::fixed_thread_bundle;
@@ -112,9 +113,9 @@ TEST_MT_FFF("require that bundle run waits for all targets", 2, SimpleThreadBund
f2.check(Box<size_t>().add(1).add(1).add(1));
f3.done.countDown();
} else {
- EXPECT_FALSE(f3.done.await(20));
+ EXPECT_FALSE(f3.done.await(20ms));
f3.start.countDown();
- EXPECT_TRUE(f3.done.await(10000));
+ EXPECT_TRUE(f3.done.await(10s));
}
}
diff --git a/vespalib/src/tests/simple_thread_bundle/threading_speed_test.cpp b/vespalib/src/tests/simple_thread_bundle/threading_speed_test.cpp
index 71bae66ed2f..f1021ba3b09 100644
--- a/vespalib/src/tests/simple_thread_bundle/threading_speed_test.cpp
+++ b/vespalib/src/tests/simple_thread_bundle/threading_speed_test.cpp
@@ -2,6 +2,7 @@
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/util/simple_thread_bundle.h>
#include <vespa/vespalib/util/box.h>
+#include <thread>
using namespace vespalib;
diff --git a/vespalib/src/tests/sync/sync_test.cpp b/vespalib/src/tests/sync/sync_test.cpp
index 417a9779f9d..e58135d8723 100644
--- a/vespalib/src/tests/sync/sync_test.cpp
+++ b/vespalib/src/tests/sync/sync_test.cpp
@@ -37,12 +37,12 @@ TEST("test gate dropping below zero") {
TEST("test gate non blocking await return correct states") {
Gate gate;
EXPECT_EQUAL(gate.getCount(), 1u);
- EXPECT_EQUAL(gate.await(0), false);
- EXPECT_EQUAL(gate.await(10), false);
+ EXPECT_EQUAL(gate.await(0ms), false);
+ EXPECT_EQUAL(gate.await(10ms), false);
gate.countDown();
EXPECT_EQUAL(gate.getCount(), 0u);
- EXPECT_EQUAL(gate.await(0), true);
- EXPECT_EQUAL(gate.await(10), true);
+ EXPECT_EQUAL(gate.await(0ms), true);
+ EXPECT_EQUAL(gate.await(10ms), true);
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/tests/testkit-time_bomb/testkit-time_bomb_test.cpp b/vespalib/src/tests/testkit-time_bomb/testkit-time_bomb_test.cpp
index af595db5ae7..5496fedb703 100644
--- a/vespalib/src/tests/testkit-time_bomb/testkit-time_bomb_test.cpp
+++ b/vespalib/src/tests/testkit-time_bomb/testkit-time_bomb_test.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/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/testkit/time_bomb.h>
TEST_MT_F("use time bomb in multi-threaded test", 4, vespalib::TimeBomb(60)) {
EXPECT_TRUE(true);
diff --git a/vespalib/src/tests/thread/thread_test.cpp b/vespalib/src/tests/thread/thread_test.cpp
index bcd38190c7e..5820f329b4f 100644
--- a/vespalib/src/tests/thread/thread_test.cpp
+++ b/vespalib/src/tests/thread/thread_test.cpp
@@ -2,6 +2,7 @@
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/util/thread.h>
+#include <thread>
using namespace vespalib;
diff --git a/vespalib/src/tests/time/time_box_test.cpp b/vespalib/src/tests/time/time_box_test.cpp
index 3c58b929487..32ac68e7254 100644
--- a/vespalib/src/tests/time/time_box_test.cpp
+++ b/vespalib/src/tests/time/time_box_test.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/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/time/time_box.h>
+#include <thread>
TEST("require that long-lived timebox returns falling time left numbers") {
vespalib::TimeBox box(3600);
diff --git a/vespalib/src/tests/websocket/websocket_test.cpp b/vespalib/src/tests/websocket/websocket_test.cpp
index db031b247b4..d9cd70c09d1 100644
--- a/vespalib/src/tests/websocket/websocket_test.cpp
+++ b/vespalib/src/tests/websocket/websocket_test.cpp
@@ -5,8 +5,7 @@
#include <vespa/vespalib/websocket/acceptor.h>
#include <vespa/vespalib/websocket/key.h>
#include <vespa/vespalib/websocket/buffer.h>
-#include <thread>
-#include <chrono>
+#include <vespa/vespalib/util/gate.h>
using namespace vespalib;
using namespace vespalib::ws;
@@ -103,7 +102,7 @@ TEST("require that an acceptor can accept connections asynchronously") {
Receptor<Socket> server;
Acceptor acceptor(0, server);
Socket::UP client = SimpleSocket::connect(SocketSpec::from_port(acceptor.port()));
- server.gate.await(60000);
+ server.gate.await(60s);
ASSERT_TRUE(server.obj.get() != nullptr);
ASSERT_TRUE(client.get() != nullptr);
TEST_DO(verify_socket_io_async(*server.obj, *client));
diff --git a/vespalib/src/vespa/vespalib/gtest/gtest.h b/vespalib/src/vespa/vespalib/gtest/gtest.h
index 87362687103..f4aaa670e76 100644
--- a/vespalib/src/vespa/vespalib/gtest/gtest.h
+++ b/vespalib/src/vespa/vespalib/gtest/gtest.h
@@ -26,3 +26,16 @@ main(int argc, char* argv[]) \
#else
#define VESPA_GTEST_TYPED_TEST_SUITE TYPED_TEST_CASE
#endif
+
+#define VESPA_EXPECT_EXCEPTION(TRY_BLOCK, EXCEPTION_TYPE, MESSAGE) \
+ try { \
+ TRY_BLOCK; \
+ FAIL() << "exception '" << MESSAGE << "' not thrown at all!"; \
+ } catch(EXCEPTION_TYPE& e) { \
+ EXPECT_TRUE(contains(stringref(e.what()), stringref(MESSAGE))) << \
+ " e.what(): " << e.what() << "\n"; \
+ } catch(...) { \
+ FAIL() << "wrong exception type thrown"; \
+ throw; \
+ }
+
diff --git a/vespalib/src/vespa/vespalib/testkit/test_kit.h b/vespalib/src/vespa/vespalib/testkit/test_kit.h
index 17746c5b0fc..0587396ceda 100644
--- a/vespalib/src/vespa/vespalib/testkit/test_kit.h
+++ b/vespalib/src/vespa/vespalib/testkit/test_kit.h
@@ -9,5 +9,4 @@
#include "test_master.h"
#include "test_hook.h"
#include "test_state_guard.h"
-#include "time_bomb.h"
#include <vespa/vespalib/util/time.h>
diff --git a/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp b/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp
index cbefa285384..2d75fcdd18f 100644
--- a/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp
+++ b/vespalib/src/vespa/vespalib/testkit/time_bomb.cpp
@@ -10,14 +10,14 @@ namespace {
void bomb(Gate &gate, size_t seconds) {
if (seconds > 5) {
- if (gate.await((seconds - 5) * 1000)) {
+ if (gate.await(from_s(seconds - 5))) {
return;
}
}
size_t countdown = std::min(seconds, size_t(5));
while (countdown > 0) {
fprintf(stderr, "...%zu...\n", countdown--);
- if (gate.await(1000)) {
+ if (gate.await(1s)) {
return;
}
}
diff --git a/vespalib/src/vespa/vespalib/util/address_space.cpp b/vespalib/src/vespa/vespalib/util/address_space.cpp
index 058c00e835e..113e4ba1478 100644
--- a/vespalib/src/vespa/vespalib/util/address_space.cpp
+++ b/vespalib/src/vespa/vespalib/util/address_space.cpp
@@ -16,7 +16,7 @@ AddressSpace::AddressSpace(size_t used_, size_t dead_, size_t limit_)
std::ostream &operator << (std::ostream &out, const AddressSpace &rhs)
{
- return out << "used=" << rhs.used() << ", dead=" << rhs.dead() << ", limit=" << rhs.limit();
+ return out << "{used=" << rhs.used() << ", dead=" << rhs.dead() << ", limit=" << rhs.limit() << "}";
}
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/util/count_down_latch.h b/vespalib/src/vespa/vespalib/util/count_down_latch.h
index 420be8f7ab2..54b36b414d6 100644
--- a/vespalib/src/vespa/vespalib/util/count_down_latch.h
+++ b/vespalib/src/vespa/vespalib/util/count_down_latch.h
@@ -2,9 +2,9 @@
#pragma once
+#include "time.h"
#include <mutex>
#include <condition_variable>
-#include <chrono>
namespace vespalib {
@@ -71,10 +71,9 @@ public:
* @param maxwait the maximum number of milliseconds to wait
* @return true if the counter reached 0, false if we timed out
**/
- bool await(int maxwait) {
- auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(maxwait);
+ bool await(vespalib::duration maxwait) {
std::unique_lock<std::mutex> guard(_lock);
- return _cond.wait_until(guard, deadline, [this]() { return (_count == 0); });
+ return _cond.wait_for(guard, maxwait, [this]() { return (_count == 0); });
}
/**
diff --git a/vespalib/src/vespa/vespalib/util/executor.h b/vespalib/src/vespa/vespalib/util/executor.h
index 57ad28344b9..ce610f4e84c 100644
--- a/vespalib/src/vespa/vespalib/util/executor.h
+++ b/vespalib/src/vespa/vespalib/util/executor.h
@@ -20,7 +20,7 @@ public:
struct Task {
typedef std::unique_ptr<Task> UP;
virtual void run() = 0;
- virtual ~Task() {}
+ virtual ~Task() = default;
};
enum class OptimizeFor {LATENCY, THROUGHPUT, ADAPTIVE};
@@ -41,7 +41,7 @@ public:
* In case you have a lazy executor that naps inbetween.
**/
virtual void wakeup() = 0;
- virtual ~Executor() =default;
+ virtual ~Executor() = default;
};
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/util/growstrategy.h b/vespalib/src/vespa/vespalib/util/growstrategy.h
index bb3b9196997..c7ada75f317 100644
--- a/vespalib/src/vespa/vespalib/util/growstrategy.h
+++ b/vespalib/src/vespa/vespalib/util/growstrategy.h
@@ -2,7 +2,7 @@
#pragma once
-#include <cstdint>
+#include <cstddef>
namespace vespalib {
diff --git a/vespamalloc/src/tests/allocfree/allocfree.cpp b/vespamalloc/src/tests/allocfree/allocfree.cpp
index 86050d4aee9..4f79ba6bccb 100644
--- a/vespamalloc/src/tests/allocfree/allocfree.cpp
+++ b/vespamalloc/src/tests/allocfree/allocfree.cpp
@@ -2,6 +2,7 @@
#include "producerconsumer.h"
#include <vespa/vespalib/testkit/testapp.h>
#include <map>
+#include <thread>
#include <vespa/log/log.h>
LOG_SETUP("allocfree_test");
diff --git a/vespamalloc/src/tests/allocfree/linklist.cpp b/vespamalloc/src/tests/allocfree/linklist.cpp
index 74af380458a..af44ec3430b 100644
--- a/vespamalloc/src/tests/allocfree/linklist.cpp
+++ b/vespamalloc/src/tests/allocfree/linklist.cpp
@@ -3,6 +3,8 @@
#include <vespa/vespalib/testkit/testapp.h>
#include <vespamalloc/malloc/allocchunk.h>
#include <vespamalloc/util/callstack.h>
+#include <thread>
+
#include <vespa/log/log.h>
LOG_SETUP("linklist_test");
diff --git a/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java b/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java
index e57e83c644f..4d638847ed9 100644
--- a/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java
+++ b/yolean/src/main/java/com/yahoo/yolean/chain/Chain.java
@@ -3,10 +3,8 @@ package com.yahoo.yolean.chain;
import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
diff --git a/zookeeper-server/CMakeLists.txt b/zookeeper-server/CMakeLists.txt
index 2fa0caeb5fb..401ac3ffd9d 100644
--- a/zookeeper-server/CMakeLists.txt
+++ b/zookeeper-server/CMakeLists.txt
@@ -1,4 +1,3 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
add_subdirectory(zookeeper-server-common)
-add_subdirectory(zookeeper-server-3.5.6)
add_subdirectory(zookeeper-server-3.6.2)
diff --git a/zookeeper-server/pom.xml b/zookeeper-server/pom.xml
index 9a3425230a5..56b66903098 100644
--- a/zookeeper-server/pom.xml
+++ b/zookeeper-server/pom.xml
@@ -13,7 +13,6 @@
<version>7-SNAPSHOT</version>
<modules>
<module>zookeeper-server-common</module>
- <module>zookeeper-server-3.5.6</module>
<module>zookeeper-server-3.6.2</module>
</modules>
<dependencies>
diff --git a/zookeeper-server/zookeeper-server-3.5.6/CMakeLists.txt b/zookeeper-server/zookeeper-server-3.5.6/CMakeLists.txt
deleted file mode 100644
index e5b439c9f4e..00000000000
--- a/zookeeper-server/zookeeper-server-3.5.6/CMakeLists.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-install_fat_java_artifact(zookeeper-server-3.5.6)
diff --git a/zookeeper-server/zookeeper-server-3.5.6/pom.xml b/zookeeper-server/zookeeper-server-3.5.6/pom.xml
deleted file mode 100644
index 060d1df7b2e..00000000000
--- a/zookeeper-server/zookeeper-server-3.5.6/pom.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0"?>
-<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>zookeeper-server</artifactId>
- <version>7-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>zookeeper-server-3.5.6</artifactId>
- <packaging>container-plugin</packaging>
- <version>7-SNAPSHOT</version>
- <dependencies>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>zookeeper-server-common</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.zookeeper</groupId>
- <artifactId>zookeeper</artifactId>
- <version>3.5.6</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-jdk14</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.7.5</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <compilerArgs>
- <arg>-Xlint:all</arg>
- <arg>-Werror</arg>
- </compilerArgs>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <redirectTestOutputToFile>${test.hide}</redirectTestOutputToFile>
- <forkMode>once</forkMode>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-install-plugin</artifactId>
- <configuration>
- <updateReleaseInfo>true</updateReleaseInfo>
- </configuration>
- </plugin>
- <plugin>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>bundle-plugin</artifactId>
- <extensions>true</extensions>
- <configuration>
- <importPackage>com.sun.management</importPackage>
- <bundleSymbolicName>zookeeper-server</bundleSymbolicName>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java b/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java
deleted file mode 100644
index d614aecbad2..00000000000
--- a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/ReconfigurableVespaZooKeeperServer.java
+++ /dev/null
@@ -1,39 +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.zookeeper;
-
-import com.google.inject.Inject;
-import com.yahoo.cloud.config.ZookeeperServerConfig;
-import com.yahoo.component.AbstractComponent;
-
-import java.nio.file.Path;
-
-/**
- * Starts or reconfigures zookeeper cluster
- *
- * @author hmusum
- */
-public class ReconfigurableVespaZooKeeperServer extends AbstractComponent implements VespaZooKeeperServer {
-
- private final VespaQuorumPeer peer;
-
- @Inject
- public ReconfigurableVespaZooKeeperServer(Reconfigurer reconfigurer, ZookeeperServerConfig zookeeperServerConfig) {
- this.peer = new VespaQuorumPeer();
- reconfigurer.startOrReconfigure(zookeeperServerConfig, this);
- }
-
- @Override
- public void shutdown() {
- peer.shutdown();
- }
-
- public void start(Path configFilePath) {
- peer.start(configFilePath);
- }
-
- @Override
- public boolean reconfigurable() {
- return true;
- }
-
-}
diff --git a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java b/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java
deleted file mode 100644
index 86325f587eb..00000000000
--- a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaQuorumPeer.java
+++ /dev/null
@@ -1,50 +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.zookeeper;
-
-import com.yahoo.protect.Process;
-import org.apache.zookeeper.server.admin.AdminServer;
-import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
-import org.apache.zookeeper.server.quorum.QuorumPeerMain;
-
-import java.io.IOException;
-import java.nio.file.Path;
-
-/**
- * Starts/stops a ZooKeeper server. Extends QuorumPeerMain to be able to call initializeAndRun() and wraps
- * exceptions so it can be used by code that does not depend on ZooKeeper.
- *
- * @author hmusum
- */
-class VespaQuorumPeer extends QuorumPeerMain {
-
- public void start(Path path) {
- initializeAndRun(new String[]{ path.toFile().getAbsolutePath()});
- }
-
- public void shutdown() {
- if (quorumPeer != null) {
- try {
- quorumPeer.shutdown();
- quorumPeer.join(); // Wait for shutdown to complete
- } catch (RuntimeException|InterruptedException e) {
- // If shutdown fails, we have no other option than forcing the JVM to stop and letting it be restarted.
- //
- // When a VespaZooKeeperServer component receives a new config, the container will try to start a new
- // server with the new config, this will fail until the old server is deconstructed. If the old server
- // fails to deconstruct/shut down, the new one will never start and if that happens forcing a restart is
- // the better option.
- Process.logAndDie("Failed to shut down ZooKeeper properly, forcing shutdown", e);
- }
- }
- }
-
- @Override
- protected void initializeAndRun(String[] args) {
- try {
- super.initializeAndRun(args);
- } catch (QuorumPeerConfig.ConfigException | IOException | AdminServer.AdminServerException e) {
- throw new RuntimeException("Exception when initializing or running ZooKeeper server", e);
- }
- }
-
-}
diff --git a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java b/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java
deleted file mode 100644
index 57ae62c0ebc..00000000000
--- a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperAdminImpl.java
+++ /dev/null
@@ -1,47 +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.zookeeper;
-
-import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.admin.ZooKeeperAdmin;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * @author hmusum
- */
-@SuppressWarnings("unused") // Created by injection
-public class VespaZooKeeperAdminImpl implements VespaZooKeeperAdmin {
-
- private static final Logger log = java.util.logging.Logger.getLogger(VespaZooKeeperAdminImpl.class.getName());
-
- @Override
- public void reconfigure(String connectionSpec, String joiningServers, String leavingServers) throws ReconfigException {
- try {
- ZooKeeperAdmin zooKeeperAdmin = new ZooKeeperAdmin(connectionSpec,
- (int) sessionTimeout().toMillis(),
- (event) -> log.log(Level.INFO, event.toString()));
- long fromConfig = -1;
- // Using string parameters because the List variant of reconfigure fails to join empty lists (observed on 3.5.6, fixed in 3.7.0)
- byte[] appliedConfig = zooKeeperAdmin.reconfigure(joiningServers, leavingServers, null, fromConfig, null);
- log.log(Level.INFO, "Applied ZooKeeper config: " + new String(appliedConfig, StandardCharsets.UTF_8));
- } catch (KeeperException e) {
- if (retryOn(e))
- throw new ReconfigException(e);
- else
- throw new RuntimeException(e);
- } catch (IOException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static boolean retryOn(KeeperException e) {
- return e instanceof KeeperException.ReconfigInProgress ||
- e instanceof KeeperException.ConnectionLossException ||
- e instanceof KeeperException.NewConfigNoQuorum;
- }
-
-}
-
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
deleted file mode 100644
index 4dfcbeea444..00000000000
--- a/zookeeper-server/zookeeper-server-3.5.6/src/main/java/com/yahoo/vespa/zookeeper/VespaZooKeeperServerImpl.java
+++ /dev/null
@@ -1,46 +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.zookeeper;
-
-import com.google.inject.Inject;
-import com.yahoo.cloud.config.ZookeeperServerConfig;
-import com.yahoo.component.AbstractComponent;
-
-import java.nio.file.Path;
-
-/**
- * @author Ulf Lilleengen
- * @author hmusum
- */
-public class VespaZooKeeperServerImpl extends AbstractComponent implements VespaZooKeeperServer {
-
- private final VespaQuorumPeer peer;
- private final ZooKeeperRunner runner;
-
- @Inject
- public VespaZooKeeperServerImpl(ZookeeperServerConfig zookeeperServerConfig) {
- this.peer = new VespaQuorumPeer();
- this.runner = new ZooKeeperRunner(zookeeperServerConfig, this);
- }
-
- @Override
- public void deconstruct() {
- runner.shutdown();
- super.deconstruct();
- }
-
- @Override
- public void shutdown() {
- peer.shutdown();
- }
-
- @Override
- public void start(Path configFilePath) {
- peer.start(configFilePath);
- }
-
- @Override
- public boolean reconfigurable() {
- return false;
- }
-
-}