summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--cloud-tenant-cd/CMakeLists.txt2
-rw-r--r--cloud-tenant-cd/OWNERS2
-rw-r--r--cloud-tenant-cd/pom.xml10
-rw-r--r--cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/cloud/impl/VespaTestRuntime.java (renamed from cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/VespaTestRuntime.java)10
-rw-r--r--cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/cloud/impl/VespaTestRuntimeProvider.java (renamed from cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/VespaTestRuntimeProvider.java)2
-rw-r--r--cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime2
-rw-r--r--config-model/pom.xml2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java26
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java151
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithOnnxTestCase.java5
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java16
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java44
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/ml/MlModelsTest.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java11
-rw-r--r--container-core/pom.xml19
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/LogHandler.java8
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/metrics/PrometheusV1Handler.java6
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java1
-rw-r--r--container-documentapi/.gitignore3
-rw-r--r--container-documentapi/OWNERS1
-rw-r--r--container-documentapi/README.md8
-rw-r--r--container-documentapi/pom.xml51
-rw-r--r--container-messagebus/pom.xml8
-rw-r--r--container-search-and-docproc/pom.xml2
-rw-r--r--container-search/abi-spec.json8
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/InterleavedSearchInvoker.java6
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/SearchErrorInvoker.java5
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/SearchInvoker.java11
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/rpc/Client.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcSearchInvoker.java35
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/AllReferencesQueryProfileVisitor.java46
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/AllTypesQueryProfileVisitor.java57
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/AllUnoverridableQueryProfileVisitor.java54
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java21
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java35
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java29
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileRegistry.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileVariants.java8
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/compiled/ValueWithSource.java36
-rw-r--r--container-search/src/test/java/com/yahoo/search/dispatch/MockInvoker.java3
-rw-r--r--container-search/src/test/java/com/yahoo/search/dispatch/rpc/RpcSearchInvokerTest.java13
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java1
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java40
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java8
-rw-r--r--dist/vespa.spec3
-rw-r--r--document/src/tests/CMakeLists.txt4
-rw-r--r--documentapi-dependencies/.gitignore3
-rw-r--r--documentapi-dependencies/OWNERS1
-rw-r--r--documentapi-dependencies/README.md6
-rw-r--r--documentapi-dependencies/pom.xml75
-rw-r--r--documentapi/OWNERS3
-rw-r--r--documentapi/pom.xml79
-rw-r--r--documentapi/src/tests/loadtypes/CMakeLists.txt3
-rw-r--r--eval/src/tests/eval/inline_operation/CMakeLists.txt3
-rw-r--r--eval/src/tests/eval/multiply_add/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/dense_pow_as_map_optimizer/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/dense_simple_expand_function/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/dense_simple_map_function/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/index_lookup_table/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/tensor_add_operation/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/tensor_modify_operation/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/tensor_remove_operation/CMakeLists.txt3
-rw-r--r--fat-model-dependencies/pom.xml2
-rw-r--r--fbench/src/test/authority/CMakeLists.txt3
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/DefaultApiAuthenticator.java (renamed from tenant-auth/src/main/java/ai/vespa/hosted/auth/ApiAuthenticator.java)7
-rw-r--r--logd/src/logd/proto_converter.cpp3
-rw-r--r--logd/src/tests/empty_forwarder/CMakeLists.txt3
-rw-r--r--logd/src/tests/proto_converter/CMakeLists.txt3
-rw-r--r--logd/src/tests/proto_converter/proto_converter_test.cpp16
-rw-r--r--logd/src/tests/rpc_forwarder/CMakeLists.txt3
-rw-r--r--logd/src/tests/watcher/CMakeLists.txt3
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java6
-rw-r--r--metrics/src/tests/CMakeLists.txt4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java19
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java10
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java1
-rw-r--r--persistence/CMakeLists.txt3
-rw-r--r--persistence/src/vespa/persistence/CMakeLists.txt2
-rw-r--r--pom.xml6
-rw-r--r--processing/src/main/java/com/yahoo/processing/request/CompoundName.java6
-rw-r--r--searchcommon/src/tests/schema/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/attribute/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_aspect_delayer/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/attribute/attribute_usage_sampler_functor/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/common/operation_rate_tracker/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/documentdb/lid_space_compaction/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/documentmetastore/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/index/CMakeLists.txt5
-rw-r--r--searchcore/src/tests/proton/matching/handle_recorder/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/matching/request_context/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/matching/unpacking_iterators_optimizer/CMakeLists.txt3
-rw-r--r--searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/CMakeLists.txt3
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp3
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/query.cpp5
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/query.h2
-rw-r--r--searchlib/CMakeLists.txt3
-rw-r--r--searchlib/abi-spec.json1
-rw-r--r--searchlib/src/tests/attribute/attribute_header/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/attribute/document_weight_or_filter_search/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/attribute/document_weight_or_filter_search/document_weight_or_filter_search_test.cpp60
-rw-r--r--searchlib/src/tests/attribute/enumstore/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/attribute/multi_value_mapping/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/attribute/save_target/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/attribute/searchable/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/attribute/searchcontextelementiterator/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/common/matching_elements/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/common/matching_elements_fields/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/engine/proto_converter/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/engine/proto_rpc_adapter/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/features/bm25/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/features/prod_features.cpp27
-rw-r--r--searchlib/src/tests/index/field_length_calculator/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/memoryindex/compact_words_store/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/memoryindex/datastore/CMakeLists.txt5
-rw-r--r--searchlib/src/tests/memoryindex/document_inverter/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/memoryindex/field_index/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/memoryindex/field_index_remover/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/memoryindex/field_inverter/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/memoryindex/memory_index/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/memoryindex/url_field_inverter/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/postinglistbm/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.cvsignore3
-rw-r--r--searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.gitignore4
-rw-r--r--searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/CMakeLists.txt9
-rw-r--r--searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/booleanmatchiteratorwrapper_test.cpp139
-rw-r--r--searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/queryeval/filter_wrapper/CMakeLists.txt10
-rw-r--r--searchlib/src/tests/queryeval/filter_wrapper/filter_wrapper_test.cpp124
-rw-r--r--searchlib/src/tests/queryeval/wrappers/CMakeLists.txt11
-rw-r--r--searchlib/src/tests/queryeval/wrappers/wrappers_test.cpp197
-rw-r--r--searchlib/src/tests/tensor/distance_functions/CMakeLists.txt3
-rw-r--r--searchlib/src/tests/tensor/hnsw_index/CMakeLists.txt5
-rw-r--r--searchlib/src/tests/tensor/hnsw_saver/CMakeLists.txt3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp16
-rw-r--r--searchlib/src/vespa/searchlib/attribute/document_weight_or_filter_search.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/features/CMakeLists.txt2
-rw-r--r--searchlib/src/vespa/searchlib/features/global_sequence_feature.cpp64
-rw-r--r--searchlib/src/vespa/searchlib/features/global_sequence_feature.h (renamed from searchlib/src/vespa/searchlib/features/uniquefeature.h)8
-rw-r--r--searchlib/src/vespa/searchlib/features/setup.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/features/uniquefeature.cpp64
-rw-r--r--searchlib/src/vespa/searchlib/fef/indexproperties.cpp16
-rw-r--r--searchlib/src/vespa/searchlib/fef/indexproperties.h13
-rw-r--r--searchlib/src/vespa/searchlib/fef/ranksetup.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/fef/ranksetup.h4
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h1
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp10
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h1
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp20
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.h1
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp11
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h1
-rw-r--r--searchlib/src/vespa/searchlib/test/CMakeLists.txt11
-rw-r--r--searchlib/src/vespa/searchlib/test/initrange.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/test/initrange.h4
-rw-r--r--searchlib/src/vespa/searchlib/test/searchiteratorverifier.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/test/searchiteratorverifier.h4
-rw-r--r--searchlib/src/vespa/searchlib/uca/CMakeLists.txt5
-rw-r--r--simplemetrics/pom.xml6
-rw-r--r--storage/src/tests/bucketdb/CMakeLists.txt3
-rw-r--r--storage/src/tests/bucketdb/initializertest.cpp12
-rw-r--r--storage/src/tests/bucketdb/lockablemaptest.cpp401
-rw-r--r--storage/src/tests/common/CMakeLists.txt3
-rw-r--r--storage/src/tests/common/hostreporter/CMakeLists.txt3
-rw-r--r--storage/src/tests/common/teststorageapp.cpp12
-rw-r--r--storage/src/tests/distributor/CMakeLists.txt3
-rw-r--r--storage/src/tests/frameworkimpl/status/CMakeLists.txt3
-rw-r--r--storage/src/tests/persistence/CMakeLists.txt3
-rw-r--r--storage/src/tests/persistence/common/CMakeLists.txt3
-rw-r--r--storage/src/tests/persistence/filestorage/CMakeLists.txt3
-rw-r--r--storage/src/tests/storageserver/CMakeLists.txt3
-rw-r--r--storage/src/tests/visiting/CMakeLists.txt3
-rw-r--r--storage/src/vespa/storage/bucketdb/CMakeLists.txt2
-rw-r--r--storage/src/vespa/storage/bucketdb/abstract_bucket_map.h244
-rw-r--r--storage/src/vespa/storage/bucketdb/btree_bucket_database.cpp66
-rw-r--r--storage/src/vespa/storage/bucketdb/btree_bucket_database.h2
-rw-r--r--storage/src/vespa/storage/bucketdb/btree_lockable_map.cpp8
-rw-r--r--storage/src/vespa/storage/bucketdb/btree_lockable_map.h163
-rw-r--r--storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp507
-rw-r--r--storage/src/vespa/storage/bucketdb/bucketmanager.cpp26
-rw-r--r--storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.cpp36
-rw-r--r--storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.h242
-rw-r--r--storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.hpp494
-rw-r--r--storage/src/vespa/storage/bucketdb/lockablemap.h197
-rw-r--r--storage/src/vespa/storage/bucketdb/lockablemap.hpp254
-rw-r--r--storage/src/vespa/storage/bucketdb/stdmapwrapper.h95
-rw-r--r--storage/src/vespa/storage/bucketdb/storagebucketdbinitializer.cpp15
-rw-r--r--storage/src/vespa/storage/bucketdb/storbucketdb.cpp107
-rw-r--r--storage/src/vespa/storage/bucketdb/storbucketdb.h91
-rw-r--r--storage/src/vespa/storage/common/content_bucket_space.cpp4
-rw-r--r--storage/src/vespa/storage/common/content_bucket_space.h2
-rw-r--r--storage/src/vespa/storage/common/content_bucket_space_repo.cpp8
-rw-r--r--storage/src/vespa/storage/common/content_bucket_space_repo.h6
-rw-r--r--storage/src/vespa/storage/config/stor-distributormanager.def4
-rw-r--r--storage/src/vespa/storage/config/stor-server.def4
-rw-r--r--storage/src/vespa/storage/distributor/distributor.cpp4
-rw-r--r--storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.cpp4
-rw-r--r--storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.h2
-rw-r--r--storage/src/vespa/storage/frameworkimpl/component/storagecomponentregisterimpl.cpp2
-rw-r--r--storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp5
-rw-r--r--storage/src/vespa/storage/storageserver/servicelayernodecontext.cpp5
-rw-r--r--storage/src/vespa/storage/storageserver/servicelayernodecontext.h3
-rw-r--r--storageapi/src/tests/CMakeLists.txt3
-rw-r--r--storageapi/src/tests/buckets/CMakeLists.txt3
-rw-r--r--storageapi/src/tests/mbusprot/CMakeLists.txt3
-rw-r--r--storageframework/src/tests/CMakeLists.txt4
-rw-r--r--storageframework/src/tests/clock/CMakeLists.txt3
-rw-r--r--storageframework/src/tests/thread/CMakeLists.txt3
-rw-r--r--storageserver/src/tests/CMakeLists.txt3
-rw-r--r--storageserver/src/vespa/storageserver/app/distributorprocess.h4
-rw-r--r--storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp19
-rw-r--r--storageserver/src/vespa/storageserver/app/servicelayerprocess.h4
-rw-r--r--tenant-auth/OWNERS1
-rw-r--r--tenant-auth/README.md2
-rw-r--r--tenant-auth/src/test/java/ai/vespa/hosted/auth/AuthenticatorTest.java6
-rw-r--r--tenant-cd-api/CMakeLists.txt2
-rw-r--r--tenant-cd-api/pom.xml4
-rw-r--r--tenant-cd-commons/OWNERS2
-rw-r--r--tenant-cd-commons/README.md3
-rw-r--r--tenant-cd-commons/pom.xml (renamed from tenant-auth/pom.xml)42
-rw-r--r--tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/DefaultEndpointAuthenticator.java (renamed from tenant-auth/src/main/java/ai/vespa/hosted/auth/EndpointAuthenticator.java)8
-rw-r--r--tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/EndpointAuthenticator.java (renamed from hosted-api/src/main/java/ai/vespa/hosted/api/EndpointAuthenticator.java)3
-rw-r--r--tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/HttpDeployment.java (renamed from cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/http/HttpDeployment.java)3
-rw-r--r--tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/HttpEndpoint.java (renamed from cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/http/HttpEndpoint.java)3
-rw-r--r--vdslib/src/tests/CMakeLists.txt4
-rw-r--r--vdslib/src/tests/bucketdistribution/CMakeLists.txt3
-rw-r--r--vdslib/src/tests/container/CMakeLists.txt3
-rw-r--r--vdslib/src/tests/distribution/CMakeLists.txt3
-rw-r--r--vdslib/src/tests/distribution/distributiontest.cpp106
-rw-r--r--vdslib/src/tests/state/CMakeLists.txt3
-rw-r--r--vdslib/src/tests/thread/CMakeLists.txt3
-rw-r--r--vdslib/src/vespa/vdslib/distribution/distribution.cpp45
-rw-r--r--vespa-osgi-testrunner/CMakeLists.txt2
-rw-r--r--vespa-osgi-testrunner/pom.xml4
-rw-r--r--vespajlib/abi-spec.json2
-rw-r--r--vespalib/src/tests/alloc/alloc_test.cpp16
-rw-r--r--vespalib/src/tests/crypto/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/datastore/datastore/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/datastore/unique_store/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/datastore/unique_store_dictionary/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/datastore/unique_store_string_allocator/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/overload/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/stllike/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/time/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/typify/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/util/reusable_set/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/visit_ranges/CMakeLists.txt3
-rw-r--r--vespalib/src/vespa/vespalib/text/utf8.cpp25
-rw-r--r--vespalib/src/vespa/vespalib/text/utf8.h16
-rw-r--r--vespalog/src/test/log_message/CMakeLists.txt3
256 files changed, 3784 insertions, 1814 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index da9cb6da6a4..e1e3219a5d9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,6 +66,7 @@ add_subdirectory(container-messagebus)
add_subdirectory(container-search)
add_subdirectory(container-search-gui)
add_subdirectory(container-search-and-docproc)
+add_subdirectory(cloud-tenant-cd)
add_subdirectory(clustercontroller-apps)
add_subdirectory(clustercontroller-apputil)
add_subdirectory(clustercontroller-utils)
@@ -127,11 +128,13 @@ add_subdirectory(storageframework)
add_subdirectory(storageserver)
add_subdirectory(statistics)
add_subdirectory(streamingvisitors)
+add_subdirectory(tenant-cd-api)
add_subdirectory(vbench)
add_subdirectory(vdslib)
add_subdirectory(vdstestlib)
add_subdirectory(vespa-athenz)
add_subdirectory(vespa-http-client)
+add_subdirectory(vespa-osgi-testrunner)
add_subdirectory(vespa-testrunner-components)
add_subdirectory(vespa_feed_perf)
add_subdirectory(vespa_jersey2)
diff --git a/cloud-tenant-cd/CMakeLists.txt b/cloud-tenant-cd/CMakeLists.txt
new file mode 100644
index 00000000000..2d30b9c4611
--- /dev/null
+++ b/cloud-tenant-cd/CMakeLists.txt
@@ -0,0 +1,2 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+install_fat_java_artifact(cloud-tenant-cd)
diff --git a/cloud-tenant-cd/OWNERS b/cloud-tenant-cd/OWNERS
new file mode 100644
index 00000000000..ff9741f2060
--- /dev/null
+++ b/cloud-tenant-cd/OWNERS
@@ -0,0 +1,2 @@
+mortent
+bjorncs \ No newline at end of file
diff --git a/cloud-tenant-cd/pom.xml b/cloud-tenant-cd/pom.xml
index c771e2dd1c3..d7e5a4a9642 100644
--- a/cloud-tenant-cd/pom.xml
+++ b/cloud-tenant-cd/pom.xml
@@ -53,19 +53,19 @@
<!-- compile scope -->
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>tenant-auth</artifactId>
+ <artifactId>hosted-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>hosted-api</artifactId>
+ <artifactId>config-provisioning</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>config-provisioning</artifactId>
+ <artifactId>tenant-cd-commons</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
@@ -78,9 +78,7 @@
<artifactId>bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
- <attachBundleArtifact>true</attachBundleArtifact>
- <bundleClassifierName>deploy</bundleClassifierName>
- <useCommonAssemblyIds>false</useCommonAssemblyIds>
+ <useCommonAssemblyIds>true</useCommonAssemblyIds>
</configuration>
</plugin>
<plugin>
diff --git a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/VespaTestRuntime.java b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/cloud/impl/VespaTestRuntime.java
index 3a70a1ed531..5f6cc252d85 100644
--- a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/VespaTestRuntime.java
+++ b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/cloud/impl/VespaTestRuntime.java
@@ -1,13 +1,15 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.cd.impl;
+package ai.vespa.hosted.cd.cloud.impl;
import ai.vespa.cloud.Zone;
import ai.vespa.hosted.api.ControllerHttpClient;
+import ai.vespa.hosted.api.DefaultApiAuthenticator;
+import ai.vespa.hosted.cd.commons.DefaultEndpointAuthenticator;
import ai.vespa.hosted.api.Properties;
import ai.vespa.hosted.api.TestConfig;
import ai.vespa.hosted.cd.Deployment;
import ai.vespa.hosted.cd.TestRuntime;
-import ai.vespa.hosted.cd.impl.http.HttpDeployment;
+import ai.vespa.hosted.cd.commons.HttpDeployment;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.zone.ZoneId;
@@ -37,7 +39,7 @@ public class VespaTestRuntime implements TestRuntime {
}
private VespaTestRuntime(TestConfig config) {
this.config = config;
- this.deploymentToTest = new HttpDeployment(config.deployments().get(config.zone()), new ai.vespa.hosted.auth.EndpointAuthenticator(config.system()));
+ this.deploymentToTest = new HttpDeployment(config.deployments().get(config.zone()), new DefaultEndpointAuthenticator(config.system()));
}
@Override
@@ -69,7 +71,7 @@ public class VespaTestRuntime implements TestRuntime {
}
private static TestConfig fromController() {
- ControllerHttpClient controller = new ai.vespa.hosted.auth.ApiAuthenticator().controller();
+ ControllerHttpClient controller = new DefaultApiAuthenticator().controller();
ApplicationId id = Properties.application();
Environment environment = Properties.environment().orElse(Environment.dev);
ZoneId zone = Properties.region().map(region -> ZoneId.from(environment, region))
diff --git a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/VespaTestRuntimeProvider.java b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/cloud/impl/VespaTestRuntimeProvider.java
index c27e1d4a0c9..d90a2d595c7 100644
--- a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/VespaTestRuntimeProvider.java
+++ b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/cloud/impl/VespaTestRuntimeProvider.java
@@ -1,5 +1,5 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.cd.impl;
+package ai.vespa.hosted.cd.cloud.impl;
import ai.vespa.hosted.cd.internal.TestRuntimeProvider;
import com.yahoo.component.AbstractComponent;
diff --git a/cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime b/cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime
index 35cb2ed7c25..884d7d4b171 100644
--- a/cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime
+++ b/cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime
@@ -1,2 +1,2 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-ai.vespa.hosted.cd.impl.VespaTestRuntime \ No newline at end of file
+ai.vespa.hosted.cd.cloud.impl.VespaTestRuntime \ No newline at end of file
diff --git a/config-model/pom.xml b/config-model/pom.xml
index 897236f78e7..95e79fd09fb 100644
--- a/config-model/pom.xml
+++ b/config-model/pom.xml
@@ -137,7 +137,7 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>documentapi</artifactId>
+ <artifactId>container-documentapi</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
index 71d16303573..04fe77d9e05 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
@@ -13,6 +13,7 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.handler.ThreadpoolConfig;
import com.yahoo.container.handler.metrics.MetricsProxyApiConfig;
import com.yahoo.container.handler.metrics.MetricsV2Handler;
+import com.yahoo.container.handler.metrics.PrometheusV1Handler;
import com.yahoo.container.jdisc.ContainerMbusConfig;
import com.yahoo.container.jdisc.messagebus.MbusServerProvider;
import com.yahoo.jdisc.http.ServletPathsConfig;
@@ -58,6 +59,10 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
public static final String METRICS_V2_HANDLER_BINDING_1 = "http://*" + MetricsV2Handler.V2_PATH;
public static final String METRICS_V2_HANDLER_BINDING_2 = METRICS_V2_HANDLER_BINDING_1 + "/*";
+ public static final String PROMETHEUS_V1_HANDLER_CLASS = PrometheusV1Handler.class.getName();
+ private static final String PROMETHEUS_V1_HANDLER_BINDING_1 = "http://*" + PrometheusV1Handler.V1_PATH;
+ private static final String PROMETHEUS_V1_HANDLER_BINDING_2 = PROMETHEUS_V1_HANDLER_BINDING_1 + "/*";
+
public static final int heapSizePercentageOfTotalNodeMemory = 60;
public static final int heapSizePercentageOfTotalNodeMemoryWhenCombinedCluster = 17;
@@ -88,7 +93,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
addSimpleComponent("com.yahoo.container.jdisc.CertificateStoreProvider");
addSimpleComponent("com.yahoo.container.jdisc.AthenzIdentityProviderProvider");
addSimpleComponent("com.yahoo.container.jdisc.SystemInfoProvider");
- addMetricsV2Handler();
+ addMetricsHandlers();
addTestrunnerComponentsIfTester(deployState);
}
@@ -116,16 +121,27 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
}
}
- public void addMetricsV2Handler() {
+ private void addMetricsHandlers() {
+ addMetricsHandler(METRICS_V2_HANDLER_CLASS, METRICS_V2_HANDLER_BINDING_1, METRICS_V2_HANDLER_BINDING_2);
+ addMetricsHandler(PROMETHEUS_V1_HANDLER_CLASS, PROMETHEUS_V1_HANDLER_BINDING_1, PROMETHEUS_V1_HANDLER_BINDING_2);
+ }
+
+ private void addMetricsHandler(String handlerClass, String rootBinding, String innerBinding) {
Handler<AbstractConfigProducer<?>> handler = new Handler<>(
- new ComponentModel(METRICS_V2_HANDLER_CLASS, null, null, null));
- handler.addServerBindings(METRICS_V2_HANDLER_BINDING_1, METRICS_V2_HANDLER_BINDING_2);
+ new ComponentModel(handlerClass, null, null, null));
+ handler.addServerBindings(rootBinding, innerBinding);
addComponent(handler);
}
private void addTestrunnerComponentsIfTester(DeployState deployState) {
- if (deployState.isHosted() && deployState.getProperties().applicationId().instance().isTester())
+ if (deployState.isHosted() && deployState.getProperties().applicationId().instance().isTester()) {
addPlatformBundle(Paths.get(Defaults.getDefaults().underVespaHome("lib/jars/vespa-testrunner-components-jar-with-dependencies.jar")));
+ addPlatformBundle(Paths.get(Defaults.getDefaults().underVespaHome("lib/jars/vespa-osgi-testrunner-jar-with-dependencies.jar")));
+ addPlatformBundle(Paths.get(Defaults.getDefaults().underVespaHome("lib/jars/tenant-cd-api-jar-with-dependencies.jar")));
+ if(deployState.zone().system().isPublic()) {
+ addPlatformBundle(Paths.get(Defaults.getDefaults().underVespaHome("lib/jars/cloud-tenant-cd-jar-with-dependencies.jar")));
+ }
+ }
}
public void setModelEvaluation(ContainerModelEvaluation modelEvaluation) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
index dd48e65c340..9676b8b1e4a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
@@ -35,7 +35,8 @@ public final class AccessControl {
ContainerCluster.BINDINGS_OVERVIEW_HANDLER_CLASS,
ContainerCluster.STATE_HANDLER_CLASS,
ContainerCluster.LOG_HANDLER_CLASS,
- ApplicationContainerCluster.METRICS_V2_HANDLER_CLASS
+ ApplicationContainerCluster.METRICS_V2_HANDLER_CLASS,
+ ApplicationContainerCluster.PROMETHEUS_V1_HANDLER_CLASS
);
public static final class Builder {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java b/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java
index e618326eff5..943fcbf6c1d 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/ml/ConvertedModel.java
@@ -12,7 +12,6 @@ import com.yahoo.io.IOUtils;
import com.yahoo.path.Path;
import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.searchdefinition.FeatureNames;
-import com.yahoo.searchdefinition.MapEvaluationTypeContext;
import com.yahoo.searchdefinition.RankProfile;
import com.yahoo.searchdefinition.RankingConstant;
import com.yahoo.searchdefinition.expressiontransforms.RankProfileTransformContext;
@@ -24,19 +23,10 @@ import com.yahoo.searchlib.rankingexpression.evaluation.TensorValue;
import com.yahoo.searchlib.rankingexpression.evaluation.Value;
import com.yahoo.searchlib.rankingexpression.parser.ParseException;
import com.yahoo.searchlib.rankingexpression.rule.CompositeNode;
-import com.yahoo.searchlib.rankingexpression.rule.ConstantNode;
import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
-import com.yahoo.searchlib.rankingexpression.rule.GeneratorLambdaFunctionNode;
import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode;
-import com.yahoo.searchlib.rankingexpression.rule.TensorFunctionNode;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
-import com.yahoo.tensor.functions.Generate;
-import com.yahoo.tensor.functions.Join;
-import com.yahoo.tensor.functions.Reduce;
-import com.yahoo.tensor.functions.Rename;
-import com.yahoo.tensor.functions.ScalarFunctions;
-import com.yahoo.tensor.functions.TensorFunction;
import com.yahoo.tensor.serialization.TypedBinaryFormat;
import java.io.BufferedReader;
@@ -261,7 +251,6 @@ public class ConvertedModel {
QueryProfileRegistry queryProfiles,
Map<String, ExpressionFunction> expressions) {
expression = expression.withBody(replaceConstantsByFunctions(expression.getBody(), constantsReplacedByFunctions));
- reduceBatchDimensions(expression.getBody(), model, profile, queryProfiles);
store.writeExpression(expressionName, expression);
expressions.put(expressionName, expression);
}
@@ -380,146 +369,6 @@ public class ConvertedModel {
}
/**
- * Check if batch dimensions of inputs can be reduced out. If the input
- * function specifies that a single exemplar should be evaluated, we can
- * reduce the batch dimension out.
- */
- private static void reduceBatchDimensions(RankingExpression expression, ImportedMlModel model,
- RankProfile profile, QueryProfileRegistry queryProfiles) {
- MapEvaluationTypeContext typeContext = profile.typeContext(queryProfiles);
-
- // Add any missing inputs for type resolution
- Set<String> functionNames = new HashSet<>();
- addFunctionNamesIn(expression.getRoot(), functionNames, model);
- for (String functionName : functionNames) {
- Optional<TensorType> requiredType = model.inputTypeSpec(functionName).map(TensorType::fromSpec);
- if (requiredType.isPresent()) {
- Reference ref = Reference.fromIdentifier(functionName);
- if (typeContext.getType(ref).equals(TensorType.empty)) {
- typeContext.setType(ref, requiredType.get());
- }
- }
- }
- typeContext.forgetResolvedTypes();
-
- TensorType typeBeforeReducing = expression.getRoot().type(typeContext);
-
- // Check generated functions for inputs to reduce
- for (String functionName : functionNames) {
- if ( ! model.functions().containsKey(functionName)) continue;
-
- RankProfile.RankingExpressionFunction rankingExpressionFunction = profile.getFunctions().get(functionName);
- if (rankingExpressionFunction == null) {
- throw new IllegalArgumentException("Model refers to generated function '" + functionName +
- "but this function is not present in " + profile);
- }
- RankingExpression functionExpression = rankingExpressionFunction.function().getBody();
- functionExpression.setRoot(reduceBatchDimensionsAtInput(functionExpression.getRoot(), model, typeContext));
- }
-
- // Check expression for inputs to reduce
- ExpressionNode root = expression.getRoot();
- root = reduceBatchDimensionsAtInput(root, model, typeContext);
- TensorType typeAfterReducing = root.type(typeContext);
- root = expandBatchDimensionsAtOutput(root, typeBeforeReducing, typeAfterReducing);
- expression.setRoot(root);
- }
-
- private static ExpressionNode reduceBatchDimensionsAtInput(ExpressionNode node, ImportedMlModel model,
- MapEvaluationTypeContext typeContext) {
- if (node instanceof TensorFunctionNode) {
- TensorFunction tensorFunction = ((TensorFunctionNode) node).function();
- if (tensorFunction instanceof Rename) {
- List<ExpressionNode> children = ((TensorFunctionNode)node).children();
- if (children.size() == 1 && children.get(0) instanceof ReferenceNode) {
- ReferenceNode referenceNode = (ReferenceNode) children.get(0);
- if (model.inputTypeSpec(referenceNode.getName()).isPresent()) {
- return reduceBatchDimensionExpression(tensorFunction, typeContext);
- }
- }
- // Modify any renames in expression to disregard batch dimension
- else if (children.size() == 1 && children.get(0) instanceof TensorFunctionNode) {
- TensorFunction<Reference> childFunction = (((TensorFunctionNode) children.get(0)).function());
- TensorType childType = childFunction.type(typeContext);
- Rename rename = (Rename) tensorFunction;
- List<String> from = new ArrayList<>();
- List<String> to = new ArrayList<>();
- for (TensorType.Dimension dimension : childType.dimensions()) {
- int i = rename.fromDimensions().indexOf(dimension.name());
- if (i < 0) {
- throw new IllegalArgumentException("Rename does not contain dimension '" +
- dimension + "' in child expression type: " + childType);
- }
- from.add((String)rename.fromDimensions().get(i));
- to.add((String)rename.toDimensions().get(i));
- }
- return new TensorFunctionNode(new Rename<>(childFunction, from, to));
- }
- }
- }
- if (node instanceof ReferenceNode) {
- ReferenceNode referenceNode = (ReferenceNode) node;
- if (model.inputTypeSpec(referenceNode.getName()).isPresent()) {
- return reduceBatchDimensionExpression(TensorFunctionNode.wrap(node), typeContext);
- }
- }
- if (node instanceof CompositeNode) {
- List<ExpressionNode> children = ((CompositeNode)node).children();
- List<ExpressionNode> transformedChildren = new ArrayList<>(children.size());
- for (ExpressionNode child : children) {
- transformedChildren.add(reduceBatchDimensionsAtInput(child, model, typeContext));
- }
- return ((CompositeNode)node).setChildren(transformedChildren);
- }
- return node;
- }
-
- private static ExpressionNode reduceBatchDimensionExpression(TensorFunction function, MapEvaluationTypeContext context) {
- TensorFunction result = function;
- TensorType type = function.type(context);
- if (type.dimensions().size() > 1) {
- List<String> reduceDimensions = new ArrayList<>();
- for (TensorType.Dimension dimension : type.dimensions()) {
- if (dimension.size().orElse(-1L) == 1) {
- reduceDimensions.add(dimension.name());
- }
- }
- if (reduceDimensions.size() > 0) {
- result = new Reduce(function, Reduce.Aggregator.sum, reduceDimensions);
- context.forgetResolvedTypes(); // We changed types
- }
- }
- return new TensorFunctionNode(result);
- }
-
- /**
- * If batch dimensions have been reduced away above, bring them back here
- * for any following computation of the tensor.
- */
- // TODO: determine when this is not necessary!
- private static ExpressionNode expandBatchDimensionsAtOutput(ExpressionNode node, TensorType before, TensorType after) {
- if (after.equals(before)) return node;
-
- TensorType.Builder typeBuilder = new TensorType.Builder(after.valueType());
- for (TensorType.Dimension dimension : before.dimensions()) {
- if (dimension.size().orElse(-1L) == 1 && !after.dimensionNames().contains(dimension.name())) {
- typeBuilder.indexed(dimension.name(), 1);
- }
- }
- TensorType expandDimensionsType = typeBuilder.build();
- if (expandDimensionsType.dimensions().size() > 0) {
- ExpressionNode generatedExpression = new ConstantNode(new DoubleValue(1.0));
- Generate generatedFunction = new Generate(expandDimensionsType,
- new GeneratorLambdaFunctionNode(expandDimensionsType,
- generatedExpression)
- .asLongListToDoubleOperator());
- Join expand = new Join(TensorFunctionNode.wrap(node), generatedFunction, ScalarFunctions.multiply());
- return new TensorFunctionNode(expand);
- }
- return node;
- }
-
- /**
* If a constant c is overridden by a function, we need to replace instances of "constant(c)" by "c" in expressions.
* This method does that for the given expression and returns the result.
*/
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithOnnxTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithOnnxTestCase.java
index 1fe1ebf2bb3..dffdc3b4a34 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithOnnxTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithOnnxTestCase.java
@@ -29,7 +29,6 @@ public class RankingExpressionWithOnnxTestCase {
private final static String name = "mnist_softmax";
private final static String vespaExpression = "join(reduce(join(rename(Placeholder, (d0, d1), (d0, d2)), constant(" + name + "_Variable), f(a,b)(a * b)), sum, d2), constant(" + name + "_Variable_1), f(a,b)(a + b))";
- private final static String vespaExpressionWithBatchReduce = "join(join(reduce(join(reduce(rename(Placeholder, (d0, d1), (d0, d2)), sum, d0), constant(mnist_softmax_Variable), f(a,b)(a * b)), sum, d2), constant(mnist_softmax_Variable_1), f(a,b)(a + b)), tensor<float>(d0[1])(1.0), f(a,b)(a * b))";
@After
public void removeGeneratedModelFiles() {
@@ -97,7 +96,7 @@ public class RankingExpressionWithOnnxTestCase {
"field mytensor type tensor<float>(d0[1],d1[784]) { indexing: attribute }",
"Placeholder",
application);
- search.assertFirstPhaseExpression(vespaExpressionWithBatchReduce, "my_profile");
+ search.assertFirstPhaseExpression(vespaExpression, "my_profile");
}
@@ -115,7 +114,7 @@ public class RankingExpressionWithOnnxTestCase {
"field mytensor type tensor<float>(d0[1],d1[784]) { indexing: attribute }",
"Placeholder",
application);
- search.assertFirstPhaseExpression(vespaExpressionWithBatchReduce, "my_profile");
+ search.assertFirstPhaseExpression(vespaExpression, "my_profile");
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java
index 126a41e14ad..610d92144ad 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/RankingExpressionWithTensorFlowTestCase.java
@@ -118,7 +118,7 @@ public class RankingExpressionWithTensorFlowTestCase {
"field mytensor type tensor(d0[1],d1[784]) { indexing: attribute }",
"Placeholder",
application);
- search.assertFirstPhaseExpression(vespaExpressionWithBatchReduce, "my_profile");
+ search.assertFirstPhaseExpression(vespaExpression, "my_profile");
}
@Test
@@ -136,7 +136,7 @@ public class RankingExpressionWithTensorFlowTestCase {
"field mytensor type tensor(d0[1],d1[784]) { indexing: attribute }",
"Placeholder",
application);
- search.assertFirstPhaseExpression(vespaExpressionWithBatchReduce, "my_profile");
+ search.assertFirstPhaseExpression(vespaExpression, "my_profile");
}
@Test
@@ -310,18 +310,10 @@ public class RankingExpressionWithTensorFlowTestCase {
}
@Test
- public void testTensorFlowReduceBatchDimension() {
- final String expression = "join(join(reduce(join(reduce(rename(Placeholder, (d0, d1), (d0, d2)), sum, d0), constant(" + name + "_layer_Variable_read), f(a,b)(a * b)), sum, d2), constant(" + name + "_layer_Variable_1_read), f(a,b)(a + b)), tensor(d0[1])(1.0), f(a,b)(a * b))";
- RankProfileSearchFixture search = fixtureWith("tensor(d0[1],d1[784])(0.0)",
- "tensorflow('mnist_softmax/saved')");
- search.assertFirstPhaseExpression(expression, "my_profile");
- }
-
- @Test
public void testFunctionGeneration() {
final String name = "mnist_saved";
final String expression = "join(reduce(join(join(join(reduce(constant(" + name + "_dnn_hidden2_Const), sum, d2), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_outputs_weights_read), f(a,b)(a * b)), sum, d2), constant(" + name + "_dnn_outputs_bias_read), f(a,b)(a + b))";
- final String functionExpression1 = "join(reduce(join(reduce(rename(input, (d0, d1), (d0, d4)), sum, d0), constant(" + name + "_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(" + name + "_dnn_hidden1_bias_read), f(a,b)(a + b))";
+ final String functionExpression1 = "join(reduce(join(rename(input, (d0, d1), (d0, d4)), constant(" + name + "_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(" + name + "_dnn_hidden1_bias_read), f(a,b)(a + b))";
final String functionExpression2 = "join(reduce(join(join(join(0.009999999776482582, imported_ml_function_" + name + "_dnn_hidden1_add, f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden1_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_hidden2_weights_read), f(a,b)(a * b)), sum, d3), constant(" + name + "_dnn_hidden2_bias_read), f(a,b)(a + b))";
RankProfileSearchFixture search = fixtureWith("tensor(d0[1],d1[784])(0.0)",
@@ -351,7 +343,7 @@ public class RankingExpressionWithTensorFlowTestCase {
" }";
final String expression = "join(reduce(join(join(join(reduce(constant(" + name + "_dnn_hidden2_Const), sum, d2), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden2_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_outputs_weights_read), f(a,b)(a * b)), sum, d2), constant(" + name + "_dnn_outputs_bias_read), f(a,b)(a + b))";
- final String functionExpression1 = "join(reduce(join(reduce(rename(input, (d0, d1), (d0, d4)), sum, d0), constant(" + name + "_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(" + name + "_dnn_hidden1_bias_read), f(a,b)(a + b))";
+ final String functionExpression1 = "join(reduce(join(rename(input, (d0, d1), (d0, d4)), constant(" + name + "_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(" + name + "_dnn_hidden1_bias_read), f(a,b)(a + b))";
final String functionExpression2 = "join(reduce(join(join(join(0.009999999776482582, imported_ml_function_" + name + "_dnn_hidden1_add, f(a,b)(a * b)), imported_ml_function_" + name + "_dnn_hidden1_add, f(a,b)(max(a,b))), constant(" + name + "_dnn_hidden2_weights_read), f(a,b)(a * b)), sum, d3), constant(" + name + "_dnn_hidden2_bias_read), f(a,b)(a + b))";
RankProfileSearchFixture search = fixtureWithUncompiled(rankProfiles, new StoringApplicationPackage(applicationDir));
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
index 739ae055ec7..03c05af1145 100755
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
@@ -4,16 +4,19 @@ package com.yahoo.vespa.model.container;
import com.yahoo.cloud.config.ClusterInfoConfig;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.cloud.config.RoutingProviderConfig;
+import com.yahoo.config.FileReference;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.config.model.test.MockRoot;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.config.provisioning.FlavorsConfig;
+import com.yahoo.container.BundlesConfig;
import com.yahoo.container.handler.ThreadpoolConfig;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.vespa.model.Host;
@@ -24,13 +27,19 @@ import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.docproc.ContainerDocproc;
import com.yahoo.vespa.model.container.search.ContainerSearch;
import com.yahoo.vespa.model.container.search.searchchain.SearchChains;
+import org.hamcrest.CoreMatchers;
import org.junit.Test;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
/**
* @author Simon Thoresen Hult
@@ -259,6 +268,41 @@ public class ContainerClusterTest {
assertFalse(config.enabled());
}
+ @Test
+ public void requireThatBundlesForTesterApplicationAreInstalled() {
+ List<String> expectedOnpremBundles =
+ List.of("vespa-testrunner-components-jar-with-dependencies.jar",
+ "vespa-osgi-testrunner-jar-with-dependencies.jar",
+ "tenant-cd-api-jar-with-dependencies.jar");
+ verifyTesterApplicationInstalledBundles(Zone.defaultZone(), expectedOnpremBundles);
+
+ List<String> expectedPublicBundles = new ArrayList<>(expectedOnpremBundles);
+ expectedPublicBundles.add("cloud-tenant-cd-jar-with-dependencies.jar");
+ Zone publicZone = new Zone(SystemName.PublicCd, Environment.dev, RegionName.defaultName());
+ verifyTesterApplicationInstalledBundles(publicZone, expectedPublicBundles);
+
+ }
+
+ private void verifyTesterApplicationInstalledBundles(Zone zone, List<String> expectedBundleNames) {
+ ApplicationId appId = ApplicationId.from("tenant", "application", "instance-t");
+ DeployState state = new DeployState.Builder().properties(
+ new TestProperties()
+ .setHostedVespa(true)
+ .setApplicationId(appId))
+ .zone(zone).build();
+ MockRoot root = new MockRoot("foo", state);
+ ApplicationContainerCluster cluster = new ApplicationContainerCluster(root, "container0", "container1", state);
+ BundlesConfig.Builder bundleBuilder = new BundlesConfig.Builder();
+ cluster.getConfig(bundleBuilder);
+ List<String> installedBundles = bundleBuilder.build().bundle().stream().map(FileReference::value).collect(Collectors.toList());
+
+ assertEquals(expectedBundleNames.size(), installedBundles.size());
+ assertThat(installedBundles, containsInAnyOrder(
+ expectedBundleNames.stream().map(CoreMatchers::endsWith).collect(Collectors.toList())
+ ));
+ }
+
+
private static void addContainer(DeployLogger deployLogger, ApplicationContainerCluster cluster, String name, String hostName) {
ApplicationContainer container = new ApplicationContainer(cluster, name, 0, cluster.isHostedVespa());
container.setHostResource(new HostResource(new Host(null, hostName)));
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/ml/MlModelsTest.java b/config-model/src/test/java/com/yahoo/vespa/model/ml/MlModelsTest.java
index 5b38e09537d..cad0ad7bae0 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/ml/MlModelsTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/ml/MlModelsTest.java
@@ -64,12 +64,12 @@ public class MlModelsTest {
private final String testProfile =
"rankingExpression(input).rankingScript: attribute(argument)\n" +
"rankingExpression(input).type: tensor<float>(d0[1],d1[784])\n" +
- "rankingExpression(imported_ml_function_mnist_saved_dnn_hidden1_add).rankingScript: join(reduce(join(reduce(rename(rankingExpression(input), (d0, d1), (d0, d4)), sum, d0), constant(mnist_saved_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(mnist_saved_dnn_hidden1_bias_read), f(a,b)(a + b))\n" +
+ "rankingExpression(imported_ml_function_mnist_saved_dnn_hidden1_add).rankingScript: join(reduce(join(rename(rankingExpression(input), (d0, d1), (d0, d4)), constant(mnist_saved_dnn_hidden1_weights_read), f(a,b)(a * b)), sum, d4), constant(mnist_saved_dnn_hidden1_bias_read), f(a,b)(a + b))\n" +
"rankingExpression(mnist_tensorflow).rankingScript: join(reduce(join(map(join(reduce(join(join(join(0.009999999776482582, rankingExpression(imported_ml_function_mnist_saved_dnn_hidden1_add), f(a,b)(a * b)), rankingExpression(imported_ml_function_mnist_saved_dnn_hidden1_add), f(a,b)(max(a,b))), constant(mnist_saved_dnn_hidden2_weights_read), f(a,b)(a * b)), sum, d3), constant(mnist_saved_dnn_hidden2_bias_read), f(a,b)(a + b)), f(a)(1.0507009873554805 * if (a >= 0, a, 1.6732632423543772 * (exp(a) - 1)))), constant(mnist_saved_dnn_outputs_weights_read), f(a,b)(a * b)), sum, d2), constant(mnist_saved_dnn_outputs_bias_read), f(a,b)(a + b))\n" +
"rankingExpression(Placeholder).rankingScript: attribute(argument)\n" +
"rankingExpression(Placeholder).type: tensor<float>(d0[1],d1[784])\n" +
- "rankingExpression(mnist_softmax_tensorflow).rankingScript: join(join(reduce(join(reduce(rename(rankingExpression(Placeholder), (d0, d1), (d0, d2)), sum, d0), constant(mnist_softmax_saved_layer_Variable_read), f(a,b)(a * b)), sum, d2), constant(mnist_softmax_saved_layer_Variable_1_read), f(a,b)(a + b)), tensor(d0[1])(1.0), f(a,b)(a * b))\n" +
- "rankingExpression(mnist_softmax_onnx).rankingScript: join(join(reduce(join(reduce(rename(rankingExpression(Placeholder), (d0, d1), (d0, d2)), sum, d0), constant(mnist_softmax_Variable), f(a,b)(a * b)), sum, d2), constant(mnist_softmax_Variable_1), f(a,b)(a + b)), tensor<float>(d0[1])(1.0), f(a,b)(a * b))\n" +
+ "rankingExpression(mnist_softmax_tensorflow).rankingScript: join(reduce(join(rename(rankingExpression(Placeholder), (d0, d1), (d0, d2)), constant(mnist_softmax_saved_layer_Variable_read), f(a,b)(a * b)), sum, d2), constant(mnist_softmax_saved_layer_Variable_1_read), f(a,b)(a + b))\n" +
+ "rankingExpression(mnist_softmax_onnx).rankingScript: join(reduce(join(rename(rankingExpression(Placeholder), (d0, d1), (d0, d2)), constant(mnist_softmax_Variable), f(a,b)(a * b)), sum, d2), constant(mnist_softmax_Variable_1), f(a,b)(a + b))\n" +
"rankingExpression(my_xgboost).rankingScript: if (f29 < -0.1234567, if (!(f56 >= -0.242398), 1.71218, -1.70044), if (f109 < 0.8723473, -1.94071, 1.85965)) + if (!(f60 >= -0.482947), if (f29 < -4.2387498, 0.784718, -0.96853), -6.23624)\n" +
"rankingExpression(my_lightgbm).rankingScript: if (!(numerical_2 >= 0.46643291586559305), 2.1594397038037663, if (categorical_2 in [\"k\", \"l\", \"m\"], 2.235297305276056, 2.1792953471546546)) + if (categorical_1 in [\"d\", \"e\"], 0.03070842919354316, if (!(numerical_1 >= 0.5102250691730842), -0.04439151147520909, 0.005117411709368601)) + if (!(numerical_2 >= 0.668665477622446), if (!(numerical_2 >= 0.008118820676863816), -0.15361238490967524, -0.01192330846157292), 0.03499044894987518) + if (!(numerical_1 >= 0.5201391072644542), -0.02141000620783247, if (categorical_1 in [\"a\", \"b\"], -0.004121485787596721, 0.04534090904886873)) + if (categorical_2 in [\"k\", \"l\", \"m\"], if (!(numerical_2 >= 0.27283279016959255), -0.01924803254356527, 0.03643772842347651), -0.02701711918923075)\n" +
"vespa.rank.firstphase: rankingExpression(firstphase)\n" +
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
index 56496b06c14..b0ca6ba67f5 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java
@@ -52,13 +52,14 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer {
@Override
protected void maintain() {
- log.fine(() -> "Running");
if (! distributeApplicationPackage.value()) return;
try (var fileDownloader = new FileDownloader(createConnectionPool(configserverConfig), downloadDirectory)) {
for (var applicationId : applicationRepository.listApplications()) {
log.fine(() -> "Verifying application package for " + applicationId);
RemoteSession session = applicationRepository.getActiveSession(applicationId);
+ if (session == null) continue; // App might be deleted after call to listApplications()
+
FileReference applicationPackage = session.getApplicationPackageReference();
long sessionId = session.getSessionId();
log.fine(() -> "Verifying application package file reference " + applicationPackage + " for session " + sessionId);
@@ -85,7 +86,6 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer {
sessionRepository.createLocalSessionUsingDistributedApplicationPackage(sessionId);
}
-
private boolean missingOnDisk(FileReference applicationPackageReference) {
Set<String> fileReferencesOnDisk = getFileReferencesOnDisk(downloadDirectory);
return ! fileReferencesOnDisk.contains(applicationPackageReference.value());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
index bb3a49f25da..f8299bdba1a 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/Tenant.java
@@ -28,11 +28,12 @@ public class Tenant {
private final RequestHandler requestHandler;
private final Instant created;
- Tenant(TenantName name,
- SessionRepository sessionRepository,
- RequestHandler requestHandler,
- TenantApplications applicationRepo,
- Instant created) {
+ // Protected due to being subclassed in a system test
+ protected Tenant(TenantName name,
+ SessionRepository sessionRepository,
+ RequestHandler requestHandler,
+ TenantApplications applicationRepo,
+ Instant created) {
this.name = name;
this.path = TenantRepository.getTenantPath(name);
this.requestHandler = requestHandler;
diff --git a/container-core/pom.xml b/container-core/pom.xml
index 64e5ebb00d3..0fbb590a1de 100644
--- a/container-core/pom.xml
+++ b/container-core/pom.xml
@@ -150,21 +150,18 @@
</exclusion>
</exclusions>
</dependency>
+
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>documentapi</artifactId>
+ <artifactId>container-documentapi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vdslib</artifactId>
<version>${project.version}</version>
- <exclusions>
- <exclusion>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config</artifactId>
- </exclusion>
- </exclusions>
</dependency>
+
<dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>vespajlib</artifactId>
diff --git a/container-core/src/main/java/com/yahoo/container/handler/LogHandler.java b/container-core/src/main/java/com/yahoo/container/handler/LogHandler.java
index 1d6e1a0893d..f3149ed4998 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/LogHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/LogHandler.java
@@ -11,6 +11,7 @@ import java.io.OutputStream;
import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.Executor;
+import java.util.logging.Level;
public class LogHandler extends ThreadedHttpRequestHandler {
@@ -37,7 +38,12 @@ public class LogHandler extends ThreadedHttpRequestHandler {
return new HttpResponse(200) {
@Override
public void render(OutputStream outputStream) {
- logReader.writeLogs(outputStream, from, to);
+ try {
+ logReader.writeLogs(outputStream, from, to);
+ }
+ catch (Throwable t) {
+ log.log(Level.WARNING, "Failed reading logs from " + from + " to " + to, t);
+ }
}
};
}
diff --git a/container-core/src/main/java/com/yahoo/container/handler/metrics/PrometheusV1Handler.java b/container-core/src/main/java/com/yahoo/container/handler/metrics/PrometheusV1Handler.java
index 23b7a62ffa3..e33f2f47828 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/metrics/PrometheusV1Handler.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/metrics/PrometheusV1Handler.java
@@ -1,6 +1,7 @@
package com.yahoo.container.handler.metrics;
import ai.vespa.util.http.VespaHttpClientBuilder;
+import com.google.inject.Inject;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.restapi.Path;
import com.yahoo.restapi.StringResponse;
@@ -30,8 +31,9 @@ public class PrometheusV1Handler extends HttpHandlerBase{
private final String metricsProxyUri;
private final HttpClient httpClient = createHttpClient();
- protected PrometheusV1Handler(Executor executor,
- MetricsProxyApiConfig config) {
+ @Inject
+ public PrometheusV1Handler(Executor executor,
+ MetricsProxyApiConfig config) {
super(executor);
metricsProxyUri = "http://localhost:" + config.metricsPort() + config.prometheusApiPath();
}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java
index e11622b1fa3..05c3b88b788 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java
@@ -97,7 +97,6 @@ public class MetricUpdater extends AbstractComponent {
this.jrtMetrics = new JrtMetrics(metric);
}
- @SuppressWarnings("deprecation")
@Override
public void run() {
long freeMemory = runtime.freeMemory();
diff --git a/container-documentapi/.gitignore b/container-documentapi/.gitignore
new file mode 100644
index 00000000000..d054e91130e
--- /dev/null
+++ b/container-documentapi/.gitignore
@@ -0,0 +1,3 @@
+container-documentapi.iml
+target
+/pom.xml.build
diff --git a/container-documentapi/OWNERS b/container-documentapi/OWNERS
new file mode 100644
index 00000000000..3b2ba1ede81
--- /dev/null
+++ b/container-documentapi/OWNERS
@@ -0,0 +1 @@
+gjoranv
diff --git a/container-documentapi/README.md b/container-documentapi/README.md
new file mode 100644
index 00000000000..a30257d5b49
--- /dev/null
+++ b/container-documentapi/README.md
@@ -0,0 +1,8 @@
+<!-- Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+# Documentapi
+
+Dependency artifact for building container bundles.
+To build standalone document clients, use `documentapi` instead.
+
+The actual java code remains in `documentapi`, becuase it uses
+common test files with the C++ code in the same module. \ No newline at end of file
diff --git a/container-documentapi/pom.xml b/container-documentapi/pom.xml
new file mode 100644
index 00000000000..004ef75a4b6
--- /dev/null
+++ b/container-documentapi/pom.xml
@@ -0,0 +1,51 @@
+<?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>parent</artifactId>
+ <version>7-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>container-documentapi</artifactId>
+ <packaging>container-plugin</packaging>
+ <version>7-SNAPSHOT</version>
+
+ <dependencies>
+
+ <dependency>
+ <!-- NOTE: this is instead of moving the java code in documentapi to this module (and turning the deps around),
+ which is made difficult by using common test files with the C++ code. -->
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>documentapi</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>documentapi-dependencies</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/container-messagebus/pom.xml b/container-messagebus/pom.xml
index fc7546d32c0..bdb308832ac 100644
--- a/container-messagebus/pom.xml
+++ b/container-messagebus/pom.xml
@@ -34,6 +34,12 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>config</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>container-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
@@ -46,7 +52,7 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>documentapi</artifactId>
+ <artifactId>container-documentapi</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
diff --git a/container-search-and-docproc/pom.xml b/container-search-and-docproc/pom.xml
index cc66bd66922..04bf858ba3c 100644
--- a/container-search-and-docproc/pom.xml
+++ b/container-search-and-docproc/pom.xml
@@ -148,7 +148,7 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>documentapi</artifactId>
+ <artifactId>container-documentapi</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json
index ba52826cd3f..6a191248e13 100644
--- a/container-search/abi-spec.json
+++ b/container-search/abi-spec.json
@@ -5732,7 +5732,6 @@
"public final java.util.Map listValues(java.lang.String, java.util.Map)",
"public final java.util.Map listValues(com.yahoo.processing.request.CompoundName, java.util.Map)",
"public java.util.Map listValues(com.yahoo.processing.request.CompoundName, java.util.Map, com.yahoo.processing.request.Properties)",
- "public java.util.Map listTypes(com.yahoo.processing.request.CompoundName, java.util.Map)",
"public final java.lang.Object get(java.lang.String)",
"public final java.lang.Object get(java.lang.String, java.util.Map)",
"public final java.lang.Object get(java.lang.String, java.lang.String[])",
@@ -6064,8 +6063,8 @@
"public"
],
"methods": [
- "public void <init>()",
"public void <init>(com.yahoo.search.query.profile.config.QueryProfilesConfig)",
+ "public void <init>()",
"public void <init>(com.yahoo.search.query.profile.types.QueryProfileTypeRegistry)",
"public final void register(com.yahoo.search.query.profile.compiled.CompiledQueryProfile)",
"public com.yahoo.search.query.profile.types.QueryProfileTypeRegistry getTypeRegistry()",
@@ -6137,9 +6136,12 @@
"public"
],
"methods": [
- "public void <init>(java.lang.Object, java.lang.String, com.yahoo.search.query.profile.DimensionValues)",
+ "public void <init>(java.lang.Object, java.lang.String, boolean, boolean, com.yahoo.search.query.profile.types.QueryProfileType, com.yahoo.search.query.profile.DimensionValues)",
"public java.lang.Object value()",
"public java.lang.String source()",
+ "public boolean isUnoverridable()",
+ "public boolean isQueryProfile()",
+ "public com.yahoo.search.query.profile.types.QueryProfileType queryProfileType()",
"public com.yahoo.search.query.profile.compiled.ValueWithSource withValue(java.lang.Object)",
"public java.util.Optional variant()",
"public java.lang.String toString()"
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/InterleavedSearchInvoker.java b/container-search/src/main/java/com/yahoo/search/dispatch/InterleavedSearchInvoker.java
index e62848a7f9e..d8fb7b46440 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/InterleavedSearchInvoker.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/InterleavedSearchInvoker.java
@@ -74,7 +74,7 @@ public class InterleavedSearchInvoker extends SearchInvoker implements ResponseM
* will be adjusted accordingly.
*/
@Override
- protected void sendSearchRequest(Query query) throws IOException {
+ protected Object sendSearchRequest(Query query, Object unusedContext) throws IOException {
this.query = query;
invokers.forEach(invoker -> invoker.setMonitor(this));
deadline = currentTime() + query.getTimeLeft();
@@ -89,13 +89,15 @@ public class InterleavedSearchInvoker extends SearchInvoker implements ResponseM
query.setHits(q);
query.setOffset(0);
+ Object context = null;
for (SearchInvoker invoker : invokers) {
- invoker.sendSearchRequest(query);
+ context = invoker.sendSearchRequest(query, context);
askedNodes++;
}
query.setHits(originalHits);
query.setOffset(originalOffset);
+ return null;
}
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/SearchErrorInvoker.java b/container-search/src/main/java/com/yahoo/search/dispatch/SearchErrorInvoker.java
index a32931c43c8..256759360f7 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/SearchErrorInvoker.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/SearchErrorInvoker.java
@@ -35,11 +35,12 @@ public class SearchErrorInvoker extends SearchInvoker {
}
@Override
- protected void sendSearchRequest(Query query) throws IOException {
+ protected Object sendSearchRequest(Query query, Object context) throws IOException {
this.query = query;
- if(monitor != null) {
+ if (monitor != null) {
monitor.responseAvailable(this);
}
+ return context;
}
@Override
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/SearchInvoker.java b/container-search/src/main/java/com/yahoo/search/dispatch/SearchInvoker.java
index 45ed1b87746..b33e91189cc 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/SearchInvoker.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/SearchInvoker.java
@@ -32,14 +32,21 @@ public abstract class SearchInvoker extends CloseableInvoker {
* for correct result windowing.
*/
public Result search(Query query, Execution execution) throws IOException {
- sendSearchRequest(query);
+ sendSearchRequest(query, null);
InvokerResult result = getSearchResult(execution);
setFinalStatus(result.getResult().hits().getError() == null);
result.complete();
return result.getResult();
}
- protected abstract void sendSearchRequest(Query query) throws IOException;
+ /**
+ *
+ * @param query the query to send
+ * @param context a context object that can be used to pass context among different
+ * invokers, e.g for reuse of preserialized data.
+ * @return an object that can be passed to the next invocation of sendSearchRequest
+ */
+ protected abstract Object sendSearchRequest(Query query, Object context) throws IOException;
protected abstract InvokerResult getSearchResult(Execution execution) throws IOException;
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/Client.java b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/Client.java
index bc0a38617ee..f4536a7aa4e 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/Client.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/Client.java
@@ -87,7 +87,7 @@ interface Client {
RpcFillInvoker.GetDocsumsResponseReceiver responseReceiver, double timeoutSeconds);
void request(String rpcMethod, CompressionType compression, int uncompressedLength, byte[] compressedPayload,
- ResponseReceiver responseReceiver, double timeoutSeconds);
+ ResponseReceiver responseReceiver, double timeoutSeconds);
/** Closes this connection */
void close();
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcSearchInvoker.java b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcSearchInvoker.java
index 76240e55c98..4c0b77207d5 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcSearchInvoker.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/RpcSearchInvoker.java
@@ -45,21 +45,36 @@ public class RpcSearchInvoker extends SearchInvoker implements Client.ResponseRe
}
@Override
- protected void sendSearchRequest(Query query) {
+ protected Object sendSearchRequest(Query query, Object incomingContext) {
this.query = query;
Client.NodeConnection nodeConnection = resourcePool.getConnection(node.key());
if (nodeConnection == null) {
responses.add(Client.ResponseOrError.fromError("Could not send search to unknown node " + node.key()));
responseAvailable();
- return;
+ return incomingContext;
}
query.trace(false, 5, "Sending search request with jrt/protobuf to node with dist key ", node.key());
- var payload = ProtobufSerialization.serializeSearchRequest(query, Math.min(query.getHits(), maxHits), searcher.getServerId());
+ RpcContext context = getContext(incomingContext);
double timeoutSeconds = ((double) query.getTimeLeft() - 3.0) / 1000.0;
- Compressor.Compression compressionResult = resourcePool.compress(query, payload);
- nodeConnection.request(RPC_METHOD, compressionResult.type(), payload.length, compressionResult.data(), this, timeoutSeconds);
+ nodeConnection.request(RPC_METHOD,
+ context.compressedPayload.type(),
+ context.compressedPayload.uncompressedSize(),
+ context.compressedPayload.data(),
+ this,
+ timeoutSeconds);
+ return context;
+ }
+
+ private RpcContext getContext(Object incomingContext) {
+ if (incomingContext instanceof RpcContext)
+ return (RpcContext)incomingContext;
+
+ return new RpcContext(resourcePool, query,
+ ProtobufSerialization.serializeSearchRequest(query,
+ Math.min(query.getHits(), maxHits),
+ searcher.getServerId()));
}
@Override
@@ -106,4 +121,14 @@ public class RpcSearchInvoker extends SearchInvoker implements Client.ResponseRe
return searcher.getName();
}
+ static class RpcContext {
+
+ final Compressor.Compression compressedPayload;
+
+ RpcContext(RpcResourcePool resourcePool, Query query, byte[] payload) {
+ compressedPayload = resourcePool.compress(query, payload);
+ }
+
+ }
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/AllReferencesQueryProfileVisitor.java b/container-search/src/main/java/com/yahoo/search/query/profile/AllReferencesQueryProfileVisitor.java
deleted file mode 100644
index eda8bf78b68..00000000000
--- a/container-search/src/main/java/com/yahoo/search/query/profile/AllReferencesQueryProfileVisitor.java
+++ /dev/null
@@ -1,46 +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.search.query.profile;
-
-import com.yahoo.processing.request.CompoundName;
-import com.yahoo.search.query.profile.types.FieldDescription;
-import com.yahoo.search.query.profile.types.QueryProfileFieldType;
-import com.yahoo.search.query.profile.types.QueryProfileType;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author bratseth
- */
-final class AllReferencesQueryProfileVisitor extends PrefixQueryProfileVisitor {
-
- /** A map of query profile types */
- private Set<CompoundName> references = new HashSet<>();
-
- public AllReferencesQueryProfileVisitor(CompoundName prefix) {
- super(prefix);
- }
-
- @Override
- public void onValue(String name, Object value,
- DimensionBinding binding,
- QueryProfile owner,
- DimensionValues variant) {}
-
- @Override
- public void onQueryProfileInsidePrefix(QueryProfile profile,
- DimensionBinding binding,
- QueryProfile owner,
- DimensionValues variant) {
- references.add(currentPrefix);
- }
-
- /** Returns the values resulting from this visiting */
- public Set<CompoundName> getResult() { return references; }
-
- /** Returns false - we are not done until we have seen all */
- public boolean isDone() { return false; }
-
-}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/AllTypesQueryProfileVisitor.java b/container-search/src/main/java/com/yahoo/search/query/profile/AllTypesQueryProfileVisitor.java
deleted file mode 100644
index 6bf17d70c70..00000000000
--- a/container-search/src/main/java/com/yahoo/search/query/profile/AllTypesQueryProfileVisitor.java
+++ /dev/null
@@ -1,57 +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.search.query.profile;
-
-import com.yahoo.processing.request.CompoundName;
-import com.yahoo.search.query.profile.types.FieldDescription;
-import com.yahoo.search.query.profile.types.QueryProfileFieldType;
-import com.yahoo.search.query.profile.types.QueryProfileType;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author bratseth
- */
-final class AllTypesQueryProfileVisitor extends PrefixQueryProfileVisitor {
-
- /** A map of query profile types */
- private Map<CompoundName, QueryProfileType> types = new HashMap<>();
-
- public AllTypesQueryProfileVisitor(CompoundName prefix) {
- super(prefix);
- }
-
- @Override
- public void onValue(String name, Object value,
- DimensionBinding binding,
- QueryProfile owner,
- DimensionValues variant) {}
-
-
- @Override
- public void onQueryProfileInsidePrefix(QueryProfile profile,
- DimensionBinding binding,
- QueryProfile owner,
- DimensionValues variant) {
- if (profile.getType() != null)
- addReachableTypes(currentPrefix, profile.getType());
- }
-
- private void addReachableTypes(CompoundName name, QueryProfileType type) {
- types.putIfAbsent(name, type); // Types visited earlier has precedence: profile.type overrides profile.inherited.type
- for (FieldDescription fieldDescription : type.fields().values()) {
- if ( ! (fieldDescription.getType() instanceof QueryProfileFieldType)) continue;
- QueryProfileFieldType fieldType = (QueryProfileFieldType)fieldDescription.getType();
- if (fieldType.getQueryProfileType() !=null) {
- addReachableTypes(name.append(fieldDescription.getName()), fieldType.getQueryProfileType());
- }
- }
- }
-
- /** Returns the values resulting from this visiting */
- public Map<CompoundName, QueryProfileType> getResult() { return types; }
-
- /** Returns false - we are not done until we have seen all */
- public boolean isDone() { return false; }
-
-}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/AllUnoverridableQueryProfileVisitor.java b/container-search/src/main/java/com/yahoo/search/query/profile/AllUnoverridableQueryProfileVisitor.java
deleted file mode 100644
index 4bae6823500..00000000000
--- a/container-search/src/main/java/com/yahoo/search/query/profile/AllUnoverridableQueryProfileVisitor.java
+++ /dev/null
@@ -1,54 +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.search.query.profile;
-
-import com.yahoo.processing.request.CompoundName;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * @author bratseth
- */
-final class AllUnoverridableQueryProfileVisitor extends PrefixQueryProfileVisitor {
-
- /** A map of query profile types */
- private Set<CompoundName> unoverridables = new HashSet<>();
-
- public AllUnoverridableQueryProfileVisitor(CompoundName prefix) {
- super(prefix);
- }
-
- @Override
- public void onValue(String name, Object value,
- DimensionBinding binding,
- QueryProfile owner,
- DimensionValues variant) {
- addUnoverridable(name, currentPrefix.append(name), binding, owner);
- }
-
- @Override
- public void onQueryProfileInsidePrefix(QueryProfile profile,
- DimensionBinding binding,
- QueryProfile owner,
- DimensionValues variant) {
- addUnoverridable(currentPrefix.last(), currentPrefix, binding, owner);
- }
-
- private void addUnoverridable(String localName,
- CompoundName fullName,
- DimensionBinding binding,
- QueryProfile owner) {
- if (owner == null) return;
-
- Boolean isOverridable = owner.isLocalOverridable(localName, binding);
- if (isOverridable != null && ! isOverridable)
- unoverridables.add(fullName);
- }
-
- /** Returns the values resulting from this visiting */
- public Set<CompoundName> getResult() { return unoverridables; }
-
- /** Returns false - we are not done until we have seen all */
- public boolean isDone() { return false; }
-
-}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java b/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java
index f27500085e1..2b61dc4c0a6 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java
@@ -3,6 +3,9 @@ package com.yahoo.search.query.profile;
import com.yahoo.processing.request.CompoundName;
import com.yahoo.search.query.profile.compiled.ValueWithSource;
+import com.yahoo.search.query.profile.types.FieldDescription;
+import com.yahoo.search.query.profile.types.QueryProfileFieldType;
+import com.yahoo.search.query.profile.types.QueryProfileType;
import java.util.Collections;
import java.util.HashMap;
@@ -26,7 +29,7 @@ final class AllValuesQueryProfileVisitor extends PrefixQueryProfileVisitor {
DimensionBinding binding,
QueryProfile owner,
DimensionValues variant) {
- putValue(localName, value, owner, variant);
+ putValue(localName, value, null, owner, variant, binding);
}
@Override
@@ -34,17 +37,25 @@ final class AllValuesQueryProfileVisitor extends PrefixQueryProfileVisitor {
DimensionBinding binding,
QueryProfile owner,
DimensionValues variant) {
- putValue("", profile.getValue(), owner, variant);
+ putValue("", profile.getValue(), profile, owner, variant, binding);
}
- private void putValue(String key, Object value, QueryProfile owner, DimensionValues variant) {
- if (value == null) return;
+ private void putValue(String key,
+ Object value,
+ QueryProfile profile,
+ QueryProfile owner,
+ DimensionValues variant,
+ DimensionBinding binding) {
CompoundName fullName = currentPrefix.append(key);
- if (fullName.isEmpty()) return; // Avoid putting a non-leaf (subtree) root in the list
if (values.containsKey(fullName.toString())) return; // The first value encountered has priority
+ Boolean isOverridable = owner != null ? owner.isLocalOverridable(key, binding) : null;
+
values.put(fullName.toString(), new ValueWithSource(value,
owner == null ? "anonymous" : owner.getSource(),
+ isOverridable != null && ! isOverridable,
+ profile != null,
+ profile == null ? null : profile.getType(),
variant));
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java b/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java
index 0cbfdc5dca0..e0edf9f9894 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java
@@ -162,7 +162,7 @@ public class DimensionBinding {
combined.add(d2.get(d2Index++));
}
else {
- return null; // no independent and no agreement
+ return null; // not independent and no agreement
}
}
if (d1Index < d1.size())
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java
index ab0f129f1e9..b6b03d37da8 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java
@@ -265,37 +265,6 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable
}
/**
- * Lists types reachable from this, indexed by the prefix having that type.
- * If this is itself typed, this' type will be included with an empty prefix
- */
- public Map<CompoundName, QueryProfileType> listTypes(CompoundName prefix, Map<String, String> context) {
- DimensionBinding dimensionBinding = DimensionBinding.createFrom(getDimensions(), context);
- AllTypesQueryProfileVisitor visitor = new AllTypesQueryProfileVisitor(prefix);
- accept(visitor, dimensionBinding, null);
- return visitor.getResult();
- }
-
- /**
- * Lists references reachable from this.
- */
- Set<CompoundName> listReferences(CompoundName prefix, Map<String, String> context) {
- DimensionBinding dimensionBinding = DimensionBinding.createFrom(getDimensions(),context);
- AllReferencesQueryProfileVisitor visitor = new AllReferencesQueryProfileVisitor(prefix);
- accept(visitor,dimensionBinding,null);
- return visitor.getResult();
- }
-
- /**
- * Lists every entry (value or reference) reachable from this which is not overridable
- */
- Set<CompoundName> listUnoverridable(CompoundName prefix, Map<String, String> context) {
- DimensionBinding dimensionBinding = DimensionBinding.createFrom(getDimensions(),context);
- AllUnoverridableQueryProfileVisitor visitor = new AllUnoverridableQueryProfileVisitor(prefix);
- accept(visitor, dimensionBinding, null);
- return visitor.getResult();
- }
-
- /**
* Returns a value from this query profile by resolving the given name:
* <ul>
* <li>The name up to the first dot is the value looked up in the value of this profile
@@ -557,6 +526,7 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable
QueryProfileVisitor visitor,
DimensionBinding dimensionBinding,
QueryProfile owner) {
+ //System.out.println(" visiting " + this);
visitor.onQueryProfile(this, dimensionBinding, owner, null);
if (visitor.isDone()) return;
@@ -570,6 +540,7 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable
if (visitor.visitInherited())
visitInherited(allowContent, visitor, dimensionBinding, owner);
+ //System.out.println(" done visiting " + this);
}
protected void visitVariants(boolean allowContent, QueryProfileVisitor visitor, DimensionBinding dimensionBinding) {
@@ -759,7 +730,7 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable
* Sets the overridability of a field in this profile,
* this overrides the corresponding setting in the type (if any)
*/
- private void setOverridable(CompoundName fieldName,boolean overridable,DimensionBinding dimensionBinding) {
+ private void setOverridable(CompoundName fieldName, boolean overridable, DimensionBinding dimensionBinding) {
QueryProfile parent = lookupParentExact(fieldName, true, dimensionBinding);
if (parent.overridable == null)
parent.overridable = new HashMap<>();
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java
index f1fc90dee09..5dacd347c2c 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java
@@ -12,6 +12,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
/**
* Compile a set of query profiles into compiled profiles.
@@ -24,9 +25,8 @@ public class QueryProfileCompiler {
public static CompiledQueryProfileRegistry compile(QueryProfileRegistry input) {
CompiledQueryProfileRegistry output = new CompiledQueryProfileRegistry(input.getTypeRegistry());
- for (QueryProfile inputProfile : input.allComponents()) {
+ for (QueryProfile inputProfile : input.allComponents())
output.register(compile(inputProfile, output));
- }
return output;
}
@@ -40,18 +40,20 @@ public class QueryProfileCompiler {
// Resolve values for each existing variant and combine into a single data structure
Set<DimensionBindingForPath> variants = collectVariants(CompoundName.empty, in, DimensionBinding.nullBinding);
variants.add(new DimensionBindingForPath(DimensionBinding.nullBinding, CompoundName.empty)); // if this contains no variants
- log.fine(() -> "Compiling " + in.toString() + " having " + variants.size() + " variants");
+ log.fine(() -> "Compiling " + in + " having " + variants.size() + " variants");
+
for (DimensionBindingForPath variant : variants) {
log.finer(() -> " Compiling variant " + variant);
for (Map.Entry<String, ValueWithSource> entry : in.visitValues(variant.path(), variant.binding().getContext()).valuesWithSource().entrySet()) {
- values.put(variant.path().append(entry.getKey()), variant.binding(), entry.getValue());
+ CompoundName fullName = variant.path().append(entry.getKey());
+ values.put(fullName, variant.binding(), entry.getValue());
+ if (entry.getValue().isUnoverridable())
+ unoverridables.put(fullName, variant.binding(), Boolean.TRUE);
+ if (entry.getValue().isQueryProfile())
+ references.put(fullName, variant.binding(), Boolean.TRUE);
+ if (entry.getValue().queryProfileType() != null)
+ types.put(fullName, variant.binding(), entry.getValue().queryProfileType());
}
- for (Map.Entry<CompoundName, QueryProfileType> entry : in.listTypes(variant.path(), variant.binding().getContext()).entrySet())
- types.put(variant.path().append(entry.getKey()), variant.binding(), entry.getValue());
- for (CompoundName reference : in.listReferences(variant.path(), variant.binding().getContext()))
- references.put(variant.path().append(reference), variant.binding(), Boolean.TRUE); // Used as a set; value is ignored
- for (CompoundName name : in.listUnoverridable(variant.path(), variant.binding().getContext()))
- unoverridables.put(variant.path().append(name), variant.binding(), Boolean.TRUE); // Used as a set; value is ignored
}
return new CompiledQueryProfile(in.getId(), in.getType(),
@@ -100,6 +102,7 @@ public class QueryProfileCompiler {
*/
private static Set<DimensionBindingForPath> wildcardExpanded(Set<DimensionBindingForPath> variants) {
Set<DimensionBindingForPath> expanded = new HashSet<>();
+
for (var variant : variants) {
if (hasWildcardBeforeEnd(variant.binding()))
expanded.addAll(wildcardExpanded(variant, variants));
@@ -120,10 +123,10 @@ public class QueryProfileCompiler {
Set<DimensionBindingForPath> expanded = new HashSet<>();
for (var variant : variants) {
if (variant.binding().isNull()) continue;
+ if ( ! variant.path().hasPrefix(variantToExpand.path())) continue;
DimensionBinding combined = variantToExpand.binding().combineWith(variant.binding());
- if ( ! combined.isInvalid() ) {
+ if ( ! combined.isInvalid() )
expanded.add(new DimensionBindingForPath(combined, variantToExpand.path()));
- }
}
return expanded;
}
@@ -136,7 +139,7 @@ public class QueryProfileCompiler {
for (DimensionBindingForPath v1 : v1s) {
if (v1.binding().isNull()) continue;
for (DimensionBindingForPath v2 : v2s) {
- if (v1.binding().isNull()) continue;
+ if (v2.binding().isNull()) continue;
DimensionBinding combined = v1.binding().combineWith(v2.binding());
if ( combined.isInvalid() ) continue;
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileRegistry.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileRegistry.java
index 0363b50815b..eb7a6d19d91 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileRegistry.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileRegistry.java
@@ -62,7 +62,6 @@ public class QueryProfileRegistry extends ComponentRegistry<QueryProfile> {
int slashIndex=id.getName().lastIndexOf("/");
if (slashIndex<1) return null;
String parentName=id.getName().substring(0,slashIndex);
- if (parentName.equals("")) return null;
ComponentSpecification parentId=new ComponentSpecification(parentName,id.getVersionSpecification());
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileVariants.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileVariants.java
index 8dedda800ea..4c4d6778d86 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileVariants.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileVariants.java
@@ -30,7 +30,7 @@ public class QueryProfileVariants implements Freezable, Cloneable {
private Map<String,FieldValues> fieldValuesByName = new HashMap<>();
/** The inherited profiles for various dimensions settings - a set of fieldvalues of List&lt;QueryProfile&gt; */
- private FieldValues inheritedProfiles =new FieldValues();
+ private FieldValues inheritedProfiles = new FieldValues();
/**
* Field and inherited profiles sorted by specificity used for all-value visiting.
@@ -105,10 +105,14 @@ public class QueryProfileVariants implements Freezable, Cloneable {
if (contentName != null) {
if (type != null)
contentName = type.unalias(contentName);
+ //System.out.println(" accepting single value in " + this + " for local key " + contentName);
acceptSingleValue(contentName, allowContent, visitor, dimensionBinding); // Special cased for performance
+ //System.out.println(" done accepting single value in " + this + " for local key " + contentName);
}
else {
+ //System.out.println(" accepting all values in " + this);
acceptAllValues(allowContent, visitor, type, dimensionBinding);
+ //System.out.println(" done accepting all values in " + this);
}
}
@@ -144,7 +148,7 @@ public class QueryProfileVariants implements Freezable, Cloneable {
if (visitor.isDone()) return;
fieldIndex++;
}
- else if (inheritedProfileValue != null) { // Inherited is most specific at this point
+ else { // Inherited is most specific at this point
if (inheritedProfileValue.matches(dimensionBinding.getValues())) {
@SuppressWarnings("unchecked")
List<QueryProfile> inheritedProfileList = (List<QueryProfile>)inheritedProfileValue.getValue();
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java
index 1c7a7cf3e97..9be459ceeab 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfile.java
@@ -159,6 +159,7 @@ public class CompiledQueryProfile extends AbstractComponent implements Cloneable
ValueWithSource valueWithSource = entry.getValue().get(context);
if (valueWithSource == null) continue;
+ if (valueWithSource.value() == null) continue;
valueWithSource = valueWithSource.withValue(substitute(valueWithSource.value(), context, substitution));
CompoundName suffixName = entry.getKey().rest(prefix.size());
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/ValueWithSource.java b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/ValueWithSource.java
index 925d20903c6..bc49e116c6e 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/ValueWithSource.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/ValueWithSource.java
@@ -2,6 +2,8 @@
package com.yahoo.search.query.profile.compiled;
import com.yahoo.search.query.profile.DimensionValues;
+import com.yahoo.search.query.profile.QueryProfile;
+import com.yahoo.search.query.profile.types.QueryProfileType;
import java.util.Optional;
@@ -14,24 +16,52 @@ public class ValueWithSource {
private final Object value;
- /** The source of the query profile having a value */
private final String source;
+ private final boolean isUnoverridable;
+
+ private final boolean isQueryProfile;
+
+ private final QueryProfileType type;
+
/** The dimension values specifying a variant in that profile, or null if it is not in a variant */
private final DimensionValues variant;
- public ValueWithSource(Object value, String source, DimensionValues variant) {
+ public ValueWithSource(Object value,
+ String source,
+ boolean isUnoverridable, boolean isQueryProfile, QueryProfileType type,
+ DimensionValues variant) {
this.value = value;
this.source = source;
+ this.isUnoverridable = isUnoverridable;
+ this.isQueryProfile = isQueryProfile;
+ this.type = type;
this.variant = variant;
}
+ /**
+ * Returns the value at this key, or null if none
+ * (in which case this is references a query profile which has no value set).
+ */
public Object value() { return value; }
+ /** Returns the source of the query profile having a value */
public String source() { return source; }
+ /** Returns true if this value cannot be overridden in queries */
+ public boolean isUnoverridable() { return isUnoverridable; }
+
+ /**
+ * Returns true if this key references a query profile (i.e a non-leaf).
+ * In this case the value may or may not be null, as non-leafs may have values.
+ */
+ public boolean isQueryProfile() { return isQueryProfile; }
+
+ /** Returns tye type of this if it refers to a query profile (not a leaf value), and it has a type */
+ public QueryProfileType queryProfileType() { return type; }
+
public ValueWithSource withValue(Object value) {
- return new ValueWithSource(value, source, variant);
+ return new ValueWithSource(value, source, isUnoverridable, isQueryProfile, type, variant);
}
/** Returns the variant having this value, or empty if it's not in a variant */
diff --git a/container-search/src/test/java/com/yahoo/search/dispatch/MockInvoker.java b/container-search/src/test/java/com/yahoo/search/dispatch/MockInvoker.java
index c159293d7d9..459dcc83ab0 100644
--- a/container-search/src/test/java/com/yahoo/search/dispatch/MockInvoker.java
+++ b/container-search/src/test/java/com/yahoo/search/dispatch/MockInvoker.java
@@ -34,9 +34,10 @@ class MockInvoker extends SearchInvoker {
}
@Override
- protected void sendSearchRequest(Query query) throws IOException {
+ protected Object sendSearchRequest(Query query, Object context) throws IOException {
this.query = query;
hitsRequested = query.getHits();
+ return context;
}
@Override
diff --git a/container-search/src/test/java/com/yahoo/search/dispatch/rpc/RpcSearchInvokerTest.java b/container-search/src/test/java/com/yahoo/search/dispatch/rpc/RpcSearchInvokerTest.java
index ce19224b35f..c421e9523ed 100644
--- a/container-search/src/test/java/com/yahoo/search/dispatch/rpc/RpcSearchInvokerTest.java
+++ b/container-search/src/test/java/com/yahoo/search/dispatch/rpc/RpcSearchInvokerTest.java
@@ -20,6 +20,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -38,7 +39,9 @@ public class RpcSearchInvokerTest {
var invoker = new RpcSearchInvoker(mockSearcher(), new Node(7, "seven", 1), mockPool, 1000);
Query q = new Query("search/?query=test&hits=10&offset=3");
- invoker.sendSearchRequest(q);
+ RpcSearchInvoker.RpcContext context = (RpcSearchInvoker.RpcContext) invoker.sendSearchRequest(q, null);
+ assertEquals(lengthHolder.get(), context.compressedPayload.uncompressedSize());
+ assertSame(context.compressedPayload.data(), payloadHolder.get());
var bytes = mockPool.compressor().decompress(payloadHolder.get(), compressionTypeHolder.get(), lengthHolder.get());
var request = SearchProtocol.SearchRequest.newBuilder().mergeFrom(bytes).build();
@@ -46,6 +49,12 @@ public class RpcSearchInvokerTest {
assertEquals(10, request.getHits());
assertEquals(3, request.getOffset());
assertTrue(request.getQueryTreeBlob().size() > 0);
+
+ var invoker2 = new RpcSearchInvoker(mockSearcher(), new Node(8, "eight", 1), mockPool, 1000);
+ RpcSearchInvoker.RpcContext context2 = (RpcSearchInvoker.RpcContext)invoker2.sendSearchRequest(q, context);
+ assertSame(context, context2);
+ assertEquals(lengthHolder.get(), context.compressedPayload.uncompressedSize());
+ assertSame(context.compressedPayload.data(), payloadHolder.get());
}
@Test
@@ -59,7 +68,7 @@ public class RpcSearchInvokerTest {
var invoker = new RpcSearchInvoker(mockSearcher(), new Node(7, "seven", 1), mockPool, maxHits);
Query q = new Query("search/?query=test&hits=10&offset=3");
- invoker.sendSearchRequest(q);
+ invoker.sendSearchRequest(q, null);
var bytes = mockPool.compressor().decompress(payloadHolder.get(), compressionTypeHolder.get(), lengthHolder.get());
var request = SearchProtocol.SearchRequest.newBuilder().mergeFrom(bytes).build();
diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java
index 0485b4dbb62..06434da2478 100644
--- a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java
@@ -3,7 +3,6 @@ package com.yahoo.search.query.profile.config.test;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.container.jdisc.HttpRequest;
-import com.yahoo.processing.execution.Execution;
import com.yahoo.processing.request.CompoundName;
import com.yahoo.yolean.Exceptions;
import com.yahoo.search.Query;
diff --git a/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java b/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java
index a57ed07017f..5b16802a65e 100644
--- a/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchchain/config/test/SearchChainConfigurerTestCase.java
@@ -18,13 +18,29 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
-import java.io.*;
-import java.util.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertThat;
/**
* @author bratseth
@@ -32,8 +48,8 @@ import static org.junit.Assert.*;
*/
public class SearchChainConfigurerTestCase {
- private static Random random = new Random(1);
- private static String topCfgDir = System.getProperty("java.io.tmpdir") + File.separator +
+ private static final Random random = new Random(1);
+ private static final String topCfgDir = System.getProperty("java.io.tmpdir") + File.separator +
"SearchChainConfigurerTestCase" + File.separator;
private static final String testDir = "src/test/java/com/yahoo/search/searchchain/config/test/";
@@ -132,7 +148,7 @@ public class SearchChainConfigurerTestCase {
* that does not contain any bootstrap configs.
*/
@Test
- public void testSearcherConfigUpdate() throws IOException, InterruptedException {
+ public void testSearcherConfigUpdate() throws IOException {
File cfgDir = getCfgDir();
copyFile(testDir + "handlers.cfg", cfgDir + "/handlers.cfg");
copyFile(testDir + "qr-search.cfg", cfgDir + "/qr-search.cfg");
@@ -171,9 +187,9 @@ public class SearchChainConfigurerTestCase {
// Searchers with unchanged config (or that takes no config) are the same as before.
Searcher s = searchers.getComponent(DeclaredTestSearcher.class.getName());
- assertThat((DeclaredTestSearcher)s, sameInstance(noConfigSearcher));
+ assertThat(s, sameInstance(noConfigSearcher));
s = searchers.getComponent(StringSearcher.class.getName());
- assertThat((StringSearcher)s, sameInstance(stringSearcher));
+ assertThat(s, sameInstance(stringSearcher));
configurer.shutdown();
cleanup(cfgDir);
@@ -219,7 +235,7 @@ public class SearchChainConfigurerTestCase {
assertThat(getSearchChainRegistryFrom(configurer).getSearcherRegistry(), not(searchers));
searchers = getSearchChainRegistryFrom(configurer).getSearcherRegistry();
assertThat(searchers.getComponentCount(), is(3));
- assertThat((IntSearcher)searchers.getComponent(IntSearcher.class.getName()), sameInstance(intSearcher));
+ assertThat(searchers.getComponent(IntSearcher.class.getName()), sameInstance(intSearcher));
assertThat(searchers.getComponent(ConfigurableSearcher.class.getName()), instanceOf(ConfigurableSearcher.class));
assertThat(searchers.getComponent(DeclaredTestSearcher.class.getName()), instanceOf(DeclaredTestSearcher.class));
assertThat(searchers.getComponent(StringSearcher.class.getName()), nullValue());
@@ -326,7 +342,7 @@ public class SearchChainConfigurerTestCase {
if (append) {
Pattern p = Pattern.compile("^[a-z]+" + "\\[\\d+\\]\\.id (.+)");
BufferedReader reader = new BufferedReader(new InputStreamReader(
- new FileInputStream(new File(componentsFile)), "UTF-8"));
+ new FileInputStream(new File(componentsFile)), StandardCharsets.UTF_8));
while ((line = reader.readLine()) != null) {
Matcher m = p.matcher(line);
if (m.matches() && !m.group(1).equals(HandlersConfigurerDi.RegistriesHack.class.getName())) {
@@ -337,7 +353,7 @@ public class SearchChainConfigurerTestCase {
reader.close();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(
- new FileInputStream(new File(configFile)), "UTF-8"));
+ new FileInputStream(new File(configFile)), StandardCharsets.UTF_8));
Pattern component = Pattern.compile("^" + componentType + "\\[\\d+\\]\\.id (.+)");
while ((line = reader.readLine()) != null) {
Matcher m = component.matcher(line);
@@ -353,7 +369,7 @@ public class SearchChainConfigurerTestCase {
buf.append("components[").append(i++).append("].id ").append(ExecutionFactory.class.getName()).append("\n");
buf.insert(0, "components["+i+"]\n");
- Writer writer = new OutputStreamWriter(new FileOutputStream(new File(componentsFile)), "UTF-8");
+ Writer writer = new OutputStreamWriter(new FileOutputStream(new File(componentsFile)), StandardCharsets.UTF_8);
writer.write(buf.toString());
writer.flush();
writer.close();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java
index a8dcaa5740c..245747a882f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RotationStatusUpdater.java
@@ -1,7 +1,6 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.maintenance;
-import com.yahoo.concurrent.maintenance.JobControl;
import com.yahoo.vespa.hosted.controller.ApplicationController;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.Instance;
@@ -11,6 +10,7 @@ import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import com.yahoo.vespa.hosted.controller.rotation.RotationState;
import com.yahoo.vespa.hosted.controller.rotation.RotationStatus;
+import com.yahoo.yolean.Exceptions;
import java.time.Duration;
import java.util.LinkedHashMap;
@@ -69,11 +69,11 @@ public class RotationStatusUpdater extends ControllerMaintainer {
try {
pool.awaitTermination(30, TimeUnit.SECONDS);
if (lastException.get() != null) {
- log.log(Level.WARNING, String.format("Failed to get global routing status of %d/%d applications. Retrying in %s. Last error: ",
+ log.log(Level.WARNING, String.format("Failed to get global routing status of %d/%d applications. Retrying in %s. Last error: %s",
failures.get(),
attempts.get(),
- interval()),
- lastException.get());
+ interval(),
+ Exceptions.toMessageString(lastException.get())));
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
diff --git a/dist/vespa.spec b/dist/vespa.spec
index 1e1658d3d55..9353acf5c68 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -441,6 +441,7 @@ fi
%{_prefix}/lib/jars/application-model-jar-with-dependencies.jar
%{_prefix}/lib/jars/application-preprocessor-jar-with-dependencies.jar
%{_prefix}/lib/jars/athenz-identity-provider-service-jar-with-dependencies.jar
+%{_prefix}/lib/jars/cloud-tenant-cd-jar-with-dependencies.jar
%{_prefix}/lib/jars/clustercontroller-apps-jar-with-dependencies.jar
%{_prefix}/lib/jars/clustercontroller-apputil-jar-with-dependencies.jar
%{_prefix}/lib/jars/clustercontroller-core-jar-with-dependencies.jar
@@ -460,7 +461,9 @@ fi
%{_prefix}/lib/jars/searchlib.jar
%{_prefix}/lib/jars/searchlib-jar-with-dependencies.jar
%{_prefix}/lib/jars/service-monitor-jar-with-dependencies.jar
+%{_prefix}/lib/jars/tenant-cd-api-jar-with-dependencies.jar
%{_prefix}/lib/jars/vespa_feed_perf-jar-with-dependencies.jar
+%{_prefix}/lib/jars/vespa-osgi-testrunner-jar-with-dependencies.jar
%{_prefix}/lib/jars/vespa-testrunner-components.jar
%{_prefix}/lib/jars/vespa-testrunner-components-jar-with-dependencies.jar
%{_prefix}/lib/jars/zookeeper-command-line-client-jar-with-dependencies.jar
diff --git a/document/src/tests/CMakeLists.txt b/document/src/tests/CMakeLists.txt
index 6458adccfd8..e203fc4b464 100644
--- a/document/src/tests/CMakeLists.txt
+++ b/document/src/tests/CMakeLists.txt
@@ -1,5 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
+
# Runner for unit tests written in gtest.
# NOTE: All new test classes should be added here.
vespa_add_executable(document_gtest_runner_app TEST
@@ -32,7 +34,7 @@ vespa_add_executable(document_gtest_runner_app TEST
weightedsetfieldvaluetest.cpp
DEPENDS
document
- gtest
+ GTest::GTest
AFTER
document_documentconfig
)
diff --git a/documentapi-dependencies/.gitignore b/documentapi-dependencies/.gitignore
new file mode 100644
index 00000000000..35f8acd14ce
--- /dev/null
+++ b/documentapi-dependencies/.gitignore
@@ -0,0 +1,3 @@
+documentapi-dependencies.iml
+target
+/pom.xml.build
diff --git a/documentapi-dependencies/OWNERS b/documentapi-dependencies/OWNERS
new file mode 100644
index 00000000000..3b2ba1ede81
--- /dev/null
+++ b/documentapi-dependencies/OWNERS
@@ -0,0 +1 @@
+gjoranv
diff --git a/documentapi-dependencies/README.md b/documentapi-dependencies/README.md
new file mode 100644
index 00000000000..35654dc24b2
--- /dev/null
+++ b/documentapi-dependencies/README.md
@@ -0,0 +1,6 @@
+<!-- Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+# Documentapi-dependencies
+
+Pom artifact that lists dependencies that are common between `documentapi` and
+`container-documentapi`. These dependencies are provided by the Jdisc container,
+but are needed in scope 'compile' for building standalone document clients.
diff --git a/documentapi-dependencies/pom.xml b/documentapi-dependencies/pom.xml
new file mode 100644
index 00000000000..6a48c7e9c71
--- /dev/null
+++ b/documentapi-dependencies/pom.xml
@@ -0,0 +1,75 @@
+<?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>parent</artifactId>
+ <version>7-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>documentapi-dependencies</artifactId>
+ <packaging>pom</packaging>
+ <version>7-SNAPSHOT</version>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>annotations</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>component</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-lib</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>configdefinitions</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>document</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>jrt</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>messagebus</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vdslib</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespajlib</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>yolean</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+</project>
diff --git a/documentapi/OWNERS b/documentapi/OWNERS
index e030acdbc5b..8e9a0415de5 100644
--- a/documentapi/OWNERS
+++ b/documentapi/OWNERS
@@ -1 +1,2 @@
-freva
+vekterli
+baldersheim
diff --git a/documentapi/pom.xml b/documentapi/pom.xml
index 245c20b3a46..5fb82e06d1b 100644
--- a/documentapi/pom.xml
+++ b/documentapi/pom.xml
@@ -10,79 +10,39 @@
<relativePath>../parent/pom.xml</relativePath>
</parent>
<artifactId>documentapi</artifactId>
- <packaging>container-plugin</packaging>
+ <packaging>jar</packaging>
<version>7-SNAPSHOT</version>
<dependencies>
+
+ <!-- WARNING: dependencies (apart from test scoped) must be added to documentapi-dependencies, not here! -->
+
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>documentapi-dependencies</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ </dependency>
+
+ <dependency>
+ <!-- Needed because 'document' uses guava collections, and has guava only in provided scope -->
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <scope>provided</scope>
</dependency>
+
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>component</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>messagebus</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>vdslib</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>vespajlib</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config</artifactId>
- <version>${project.version}</version>
- <exclusions>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>document</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>configdefinitions</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>annotations</artifactId>
- <version>${project.version}</version>
- </dependency>
</dependencies>
<build>
<plugins>
<plugin>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>bundle-plugin</artifactId>
- <extensions>true</extensions>
- </plugin>
- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
@@ -107,6 +67,19 @@
</plugin>
<plugin>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-class-plugin</artifactId>
+ <version>${project.version}</version>
+ <executions>
+ <execution>
+ <id>config-gen</id>
+ <goals>
+ <goal>config-gen</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>abi-check-plugin</artifactId>
</plugin>
</plugins>
diff --git a/documentapi/src/tests/loadtypes/CMakeLists.txt b/documentapi/src/tests/loadtypes/CMakeLists.txt
index 495c05c0e43..3e3e64310f8 100644
--- a/documentapi/src/tests/loadtypes/CMakeLists.txt
+++ b/documentapi/src/tests/loadtypes/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(documentapi_loadtype_test_app TEST
SOURCES
loadtypetest.cpp
DEPENDS
documentapi
vdstestlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME documentapi_loadtype_test_app COMMAND documentapi_loadtype_test_app)
diff --git a/eval/src/tests/eval/inline_operation/CMakeLists.txt b/eval/src/tests/eval/inline_operation/CMakeLists.txt
index 04cdbca3abf..050e9287c21 100644
--- a/eval/src/tests/eval/inline_operation/CMakeLists.txt
+++ b/eval/src/tests/eval/inline_operation/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_inline_operation_test_app TEST
SOURCES
inline_operation_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_inline_operation_test_app COMMAND eval_inline_operation_test_app)
diff --git a/eval/src/tests/eval/multiply_add/CMakeLists.txt b/eval/src/tests/eval/multiply_add/CMakeLists.txt
index c50aa4f50a2..687c9b3b576 100644
--- a/eval/src/tests/eval/multiply_add/CMakeLists.txt
+++ b/eval/src/tests/eval/multiply_add/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_multiply_add_test_app TEST
SOURCES
multiply_add_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_multiply_add_test_app COMMAND eval_multiply_add_test_app)
diff --git a/eval/src/tests/tensor/dense_pow_as_map_optimizer/CMakeLists.txt b/eval/src/tests/tensor/dense_pow_as_map_optimizer/CMakeLists.txt
index cf5103f8b4f..60cb72a8ed7 100644
--- a/eval/src/tests/tensor/dense_pow_as_map_optimizer/CMakeLists.txt
+++ b/eval/src/tests/tensor/dense_pow_as_map_optimizer/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_dense_pow_as_map_optimizer_test_app TEST
SOURCES
dense_pow_as_map_optimizer_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_dense_pow_as_map_optimizer_test_app COMMAND eval_dense_pow_as_map_optimizer_test_app)
diff --git a/eval/src/tests/tensor/dense_simple_expand_function/CMakeLists.txt b/eval/src/tests/tensor/dense_simple_expand_function/CMakeLists.txt
index 4e7409a6139..42be6f4c613 100644
--- a/eval/src/tests/tensor/dense_simple_expand_function/CMakeLists.txt
+++ b/eval/src/tests/tensor/dense_simple_expand_function/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_dense_simple_expand_function_test_app TEST
SOURCES
dense_simple_expand_function_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_dense_simple_expand_function_test_app COMMAND eval_dense_simple_expand_function_test_app)
diff --git a/eval/src/tests/tensor/dense_simple_map_function/CMakeLists.txt b/eval/src/tests/tensor/dense_simple_map_function/CMakeLists.txt
index 766986bd628..daa68665d2e 100644
--- a/eval/src/tests/tensor/dense_simple_map_function/CMakeLists.txt
+++ b/eval/src/tests/tensor/dense_simple_map_function/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_dense_simple_map_function_test_app TEST
SOURCES
dense_simple_map_function_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_dense_simple_map_function_test_app COMMAND eval_dense_simple_map_function_test_app)
diff --git a/eval/src/tests/tensor/index_lookup_table/CMakeLists.txt b/eval/src/tests/tensor/index_lookup_table/CMakeLists.txt
index f2dc5d31045..3669c663896 100644
--- a/eval/src/tests/tensor/index_lookup_table/CMakeLists.txt
+++ b/eval/src/tests/tensor/index_lookup_table/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_index_lookup_table_test_app TEST
SOURCES
index_lookup_table_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_index_lookup_table_test_app COMMAND eval_index_lookup_table_test_app)
diff --git a/eval/src/tests/tensor/tensor_add_operation/CMakeLists.txt b/eval/src/tests/tensor/tensor_add_operation/CMakeLists.txt
index e511b23c5ec..3dcd14a2189 100644
--- a/eval/src/tests/tensor/tensor_add_operation/CMakeLists.txt
+++ b/eval/src/tests/tensor/tensor_add_operation/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_tensor_add_operation_test_app TEST
SOURCES
tensor_add_operation_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_tensor_add_operation_test_app COMMAND eval_tensor_add_operation_test_app)
diff --git a/eval/src/tests/tensor/tensor_modify_operation/CMakeLists.txt b/eval/src/tests/tensor/tensor_modify_operation/CMakeLists.txt
index 481a0aac7fb..9673c6d0c80 100644
--- a/eval/src/tests/tensor/tensor_modify_operation/CMakeLists.txt
+++ b/eval/src/tests/tensor/tensor_modify_operation/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_tensor_modify_operation_test_app TEST
SOURCES
tensor_modify_operation_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_tensor_modify_operation_test_app COMMAND eval_tensor_modify_operation_test_app)
diff --git a/eval/src/tests/tensor/tensor_remove_operation/CMakeLists.txt b/eval/src/tests/tensor/tensor_remove_operation/CMakeLists.txt
index ffc71563107..0d0c9b17473 100644
--- a/eval/src/tests/tensor/tensor_remove_operation/CMakeLists.txt
+++ b/eval/src/tests/tensor/tensor_remove_operation/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(eval_tensor_remove_operation_test_app TEST
SOURCES
tensor_remove_operation_test.cpp
DEPENDS
vespaeval
- gtest
+ GTest::GTest
)
vespa_add_test(NAME eval_tensor_remove_operation_test_app COMMAND eval_tensor_remove_operation_test_app)
diff --git a/fat-model-dependencies/pom.xml b/fat-model-dependencies/pom.xml
index 381035a75b0..6bf676dcba1 100644
--- a/fat-model-dependencies/pom.xml
+++ b/fat-model-dependencies/pom.xml
@@ -96,7 +96,7 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>documentapi</artifactId>
+ <artifactId>container-documentapi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
diff --git a/fbench/src/test/authority/CMakeLists.txt b/fbench/src/test/authority/CMakeLists.txt
index 00f804f43f6..09c1152383e 100644
--- a/fbench/src/test/authority/CMakeLists.txt
+++ b/fbench/src/test/authority/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(fbench_authority_test_app TEST
SOURCES
authority_test.cpp
DEPENDS
fbench_util
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME fbench_authority_test_app COMMAND fbench_authority_test_app)
diff --git a/tenant-auth/src/main/java/ai/vespa/hosted/auth/ApiAuthenticator.java b/hosted-api/src/main/java/ai/vespa/hosted/api/DefaultApiAuthenticator.java
index 55b3af93050..cdd9a9a56dc 100644
--- a/tenant-auth/src/main/java/ai/vespa/hosted/auth/ApiAuthenticator.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/DefaultApiAuthenticator.java
@@ -1,10 +1,7 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.auth;
+package ai.vespa.hosted.api;
-import ai.vespa.hosted.api.ControllerHttpClient;
-import ai.vespa.hosted.api.Properties;
-
-public class ApiAuthenticator implements ai.vespa.hosted.api.ApiAuthenticator {
+public class DefaultApiAuthenticator implements ai.vespa.hosted.api.ApiAuthenticator {
/** Returns a controller client using mTLS if a key and certificate pair is provided, or signed requests otherwise. */
@Override
diff --git a/logd/src/logd/proto_converter.cpp b/logd/src/logd/proto_converter.cpp
index b3facd4ef4a..e4331e00480 100644
--- a/logd/src/logd/proto_converter.cpp
+++ b/logd/src/logd/proto_converter.cpp
@@ -1,6 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "proto_converter.h"
+#include <vespa/vespalib/text/utf8.h>
using ns_log::LogMessage;
using ns_log::Logger;
@@ -59,7 +60,7 @@ ProtoConverter::log_message_to_proto(const LogMessage& message, ProtoLogMessage&
proto.set_service(message.service());
proto.set_component(message.component());
proto.set_level(convert_level(message.level()));
- proto.set_payload(message.payload());
+ proto.set_payload(vespalib::Utf8::filter_invalid_sequences(message.payload()));
}
}
diff --git a/logd/src/tests/empty_forwarder/CMakeLists.txt b/logd/src/tests/empty_forwarder/CMakeLists.txt
index 4c4ab8efa40..978a425a738 100644
--- a/logd/src/tests/empty_forwarder/CMakeLists.txt
+++ b/logd/src/tests/empty_forwarder/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(logd_empty_forwarder_test_app TEST
SOURCES
empty_forwarder_test.cpp
DEPENDS
logd
- gtest
+ GTest::GTest
)
vespa_add_test(NAME logd_empty_forwarder_test_app COMMAND logd_empty_forwarder_test_app)
diff --git a/logd/src/tests/proto_converter/CMakeLists.txt b/logd/src/tests/proto_converter/CMakeLists.txt
index 5ca048ecd4e..ba4c7c2b26d 100644
--- a/logd/src/tests/proto_converter/CMakeLists.txt
+++ b/logd/src/tests/proto_converter/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(logd_proto_converter_test_app TEST
SOURCES
proto_converter_test.cpp
DEPENDS
logd
- gtest
+ GTest::GTest
)
vespa_add_test(NAME logd_proto_converter_test_app COMMAND logd_proto_converter_test_app)
diff --git a/logd/src/tests/proto_converter/proto_converter_test.cpp b/logd/src/tests/proto_converter/proto_converter_test.cpp
index aa0b00e34d6..702752e8482 100644
--- a/logd/src/tests/proto_converter/proto_converter_test.cpp
+++ b/logd/src/tests/proto_converter/proto_converter_test.cpp
@@ -84,5 +84,21 @@ TEST_F(LogRequestTest, log_messages_are_converted_to_request)
ProtoLogLevel::LogMessage_Level_EVENT, "bar_payload", proto.log_messages(1));
}
+// UTF-8 encoding of \U+FFFD
+#define FFFD "\xEF\xBF\xBD"
+
+TEST_F(LogRequestTest, invalid_utf8_is_filtered)
+{
+ messages.emplace_back(12345, "foo_host", 3, 5, "foo_service", "foo_component", Logger::info,
+ "valid: \xE2\x82\xAC and \xEF\xBF\xBA; semi-valid: \xED\xA0\xBD\xED\xB8\x80; invalid: \xCC surrogate \xED\xBF\xBF overlong \xC1\x81 end"
+ );
+ convert();
+ EXPECT_EQ(1, proto.log_messages_size());
+ expect_proto_log_message_equal(12345, "foo_host", 3, 5, "foo_service", "foo_component",
+ ProtoLogLevel::LogMessage_Level_INFO,
+ "valid: \xE2\x82\xAC and \xEF\xBF\xBA; semi-valid: " FFFD FFFD "; invalid: " FFFD " surrogate " FFFD " overlong " FFFD FFFD " end",
+ proto.log_messages(0));
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/logd/src/tests/rpc_forwarder/CMakeLists.txt b/logd/src/tests/rpc_forwarder/CMakeLists.txt
index 66a30777b41..430647c9b82 100644
--- a/logd/src/tests/rpc_forwarder/CMakeLists.txt
+++ b/logd/src/tests/rpc_forwarder/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(logd_rpc_forwarder_test_app TEST
SOURCES
rpc_forwarder_test.cpp
DEPENDS
logd
- gtest
+ GTest::GTest
)
vespa_add_test(NAME logd_rpc_forwarder_test_app COMMAND logd_rpc_forwarder_test_app)
diff --git a/logd/src/tests/watcher/CMakeLists.txt b/logd/src/tests/watcher/CMakeLists.txt
index 0bf0a574fa9..42fddae48b3 100644
--- a/logd/src/tests/watcher/CMakeLists.txt
+++ b/logd/src/tests/watcher/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(logd_watcher_test_app TEST
SOURCES
watcher_test.cpp
DEPENDS
logd
- gtest
+ GTest::GTest
)
vespa_add_test(NAME logd_watcher_test_app COMMAND logd_watcher_test_app)
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java
index 6b1376452fa..c910ac26833 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java
@@ -40,9 +40,7 @@ public class ApplicationMetricsHandler extends HttpHandlerBase {
public static final String METRICS_V1_PATH = "/applicationmetrics/v1";
public static final String METRICS_VALUES_PATH = METRICS_V1_PATH + "/values";
-
- public static final String PROMETHEUS_V1_PATH = "/applicationprometheus/v1";
- public static final String PROMETHEUS_VALUES_PATH = PROMETHEUS_V1_PATH + "/values";
+ public static final String PROMETHEUS_VALUES_PATH = METRICS_V1_PATH + "/prometheus";
private final ApplicationMetricsRetriever metricsRetriever;
private final MetricsConsumers metricsConsumers;
@@ -60,8 +58,6 @@ public class ApplicationMetricsHandler extends HttpHandlerBase {
public Optional<HttpResponse> doHandle(URI requestUri, Path apiPath, String consumer) {
if (apiPath.matches(METRICS_V1_PATH)) return Optional.of(resourceListResponse(requestUri, List.of(METRICS_VALUES_PATH)));
if (apiPath.matches(METRICS_VALUES_PATH)) return Optional.of(applicationMetricsResponse(consumer));
-
- if (apiPath.matches(PROMETHEUS_V1_PATH)) return Optional.of(resourceListResponse(requestUri, List.of(PROMETHEUS_VALUES_PATH)));
if (apiPath.matches(PROMETHEUS_VALUES_PATH)) return Optional.of(applicationPrometheusResponse(consumer));
return Optional.empty();
diff --git a/metrics/src/tests/CMakeLists.txt b/metrics/src/tests/CMakeLists.txt
index c7ca296e7b5..435be3fd1dd 100644
--- a/metrics/src/tests/CMakeLists.txt
+++ b/metrics/src/tests/CMakeLists.txt
@@ -1,5 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
+
# Runner for unit tests written in gtest.
vespa_add_executable(metrics_gtest_runner_app TEST
SOURCES
@@ -17,7 +19,7 @@ vespa_add_executable(metrics_gtest_runner_app TEST
DEPENDS
metrics
vdstestlib
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java
index 4e55e464fa7..6c1c1a48a9c 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/UnixPath.java
@@ -20,10 +20,8 @@ import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.time.Instant;
-import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.yahoo.vespa.hosted.node.admin.task.util.file.IOExceptionUtil.ifExists;
@@ -231,8 +229,8 @@ public class UnixPath {
*/
public boolean deleteRecursively() {
if (!isSymbolicLink() && isDirectory()) {
- for (UnixPath path : listContentsOfDirectory()) {
- path.deleteRecursively();
+ try (Stream<UnixPath> paths = listContentsOfDirectory()) {
+ paths.forEach(UnixPath::deleteRecursively);
}
}
return uncheck(() -> Files.deleteIfExists(path));
@@ -243,13 +241,14 @@ public class UnixPath {
return this;
}
- public List<UnixPath> listContentsOfDirectory() {
- try (Stream<Path> stream = Files.list(path)){
- return stream
- .map(UnixPath::new)
- .collect(Collectors.toList());
+ /** Lists the contents of this as a stream. Callers should use try-with to ensure that the stream is closed */
+ public Stream<UnixPath> listContentsOfDirectory() {
+ try {
+ // Avoid the temptation to collect the stream here as collecting a directory with a high number of entries
+ // can quickly lead to out of memory conditions
+ return Files.list(path).map(UnixPath::new);
} catch (NoSuchFileException ignored) {
- return List.of();
+ return Stream.empty();
} catch (IOException e) {
throw new RuntimeException("Failed to list contents of directory " + path.toAbsolutePath(), e);
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
index b420b6aac23..e5c2e35f2b2 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
@@ -27,6 +27,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static com.yahoo.yolean.Exceptions.uncheck;
import static org.junit.Assert.assertEquals;
@@ -247,10 +248,11 @@ public class CoredumpHandlerTest {
private static void assertFolderContents(Path pathToFolder, String... filenames) {
Set<String> expectedContentsOfFolder = Set.of(filenames);
- Set<String> actualContentsOfFolder = new UnixPath(pathToFolder)
- .listContentsOfDirectory().stream()
- .map(unixPath -> unixPath.toPath().getFileName().toString())
- .collect(Collectors.toSet());
+ Set<String> actualContentsOfFolder;
+ try (Stream<UnixPath> paths = new UnixPath(pathToFolder).listContentsOfDirectory()) {
+ actualContentsOfFolder = paths.map(unixPath -> unixPath.toPath().getFileName().toString())
+ .collect(Collectors.toSet());
+ }
assertEquals(expectedContentsOfFolder, actualContentsOfFolder);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
index 06ecb1f4a01..3cb0f935888 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainer.java
@@ -71,6 +71,7 @@ public class PeriodicApplicationMaintainer extends ApplicationMaintainer {
private boolean shouldMaintain(ApplicationId id) {
if (id.tenant().value().equals("stream") && id.application().value().equals("stream-ranking")) return false;
if (id.tenant().value().equals("stream") && id.application().value().equals("stream-ranking-canary")) return false;
+ if (id.tenant().value().equals("stream") && id.application().value().equals("stream-ranking-rhel7")) return false;
return true;
}
diff --git a/persistence/CMakeLists.txt b/persistence/CMakeLists.txt
index e9fd742af27..3883929265c 100644
--- a/persistence/CMakeLists.txt
+++ b/persistence/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_define_module(
DEPENDS
fastos
@@ -20,7 +21,7 @@ vespa_define_module(
src/vespa/persistence/spi
TEST_DEPENDS
- gtest
+ GTest::GTest
TESTS
src/tests
diff --git a/persistence/src/vespa/persistence/CMakeLists.txt b/persistence/src/vespa/persistence/CMakeLists.txt
index 77f3f865b6f..456c3f8f87f 100644
--- a/persistence/src/vespa/persistence/CMakeLists.txt
+++ b/persistence/src/vespa/persistence/CMakeLists.txt
@@ -11,5 +11,5 @@ vespa_add_library(persistence_persistence_conformancetest
$<TARGET_OBJECTS:persistence_conformancetest_lib>
DEPENDS
persistence
- gtest
+ GTest::GTest
)
diff --git a/pom.xml b/pom.xml
index b150e33208a..bf9a958b274 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,6 +60,7 @@
<module>container-dev</module>
<module>container-di</module>
<module>container-disc</module>
+ <module>container-documentapi</module>
<module>container-integration-test</module>
<module>container-jersey2</module>
<module>container-messagebus</module>
@@ -74,8 +75,9 @@
<module>docker-api</module>
<module>docproc</module>
<module>docprocs</module>
- <module>documentapi</module>
<module>document</module>
+ <module>documentapi</module>
+ <module>documentapi-dependencies</module>
<module>documentgen-test</module>
<module>fat-model-dependencies</module>
<module>fileacquirer</module>
@@ -126,9 +128,9 @@
<module>standalone-container</module>
<module>statistics</module>
<module>storage</module>
- <module>tenant-auth</module>
<module>tenant-base</module>
<module>tenant-cd-api</module>
+ <module>tenant-cd-commons</module>
<module>testutil</module>
<module>vdslib</module>
<module>vespaclient-core</module>
diff --git a/processing/src/main/java/com/yahoo/processing/request/CompoundName.java b/processing/src/main/java/com/yahoo/processing/request/CompoundName.java
index 976fb3e2796..09c0879fdbf 100644
--- a/processing/src/main/java/com/yahoo/processing/request/CompoundName.java
+++ b/processing/src/main/java/com/yahoo/processing/request/CompoundName.java
@@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import static com.yahoo.text.Lowercase.toLowerCase;
@@ -141,9 +140,8 @@ public final class CompoundName {
if (nameParts.length == 0) return this;
if (isEmpty()) return fromComponents(nameParts);
- List<String> newCompounds = new ArrayList<>();
- for (String namePart : nameParts)
- newCompounds.add(namePart);
+ List<String> newCompounds = new ArrayList<>(nameParts.length+compounds.size());
+ newCompounds.addAll(Arrays.asList(nameParts));
newCompounds.addAll(this.compounds);
return new CompoundName(newCompounds);
}
diff --git a/searchcommon/src/tests/schema/CMakeLists.txt b/searchcommon/src/tests/schema/CMakeLists.txt
index aafe015d9a1..76c8d3f8483 100644
--- a/searchcommon/src/tests/schema/CMakeLists.txt
+++ b/searchcommon/src/tests/schema/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcommon_schema_test_app TEST
SOURCES
schema_test.cpp
DEPENDS
searchcommon
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcommon_schema_test_app NO_VALGRIND COMMAND searchcommon_schema_test_app)
diff --git a/searchcore/src/tests/proton/attribute/CMakeLists.txt b/searchcore/src/tests/proton/attribute/CMakeLists.txt
index c23d97c6e88..95f55c28f18 100644
--- a/searchcore/src/tests/proton/attribute/CMakeLists.txt
+++ b/searchcore/src/tests/proton/attribute/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_attribute_test_app TEST
SOURCES
attribute_test.cpp
@@ -8,7 +9,7 @@ vespa_add_executable(searchcore_attribute_test_app TEST
searchcore_flushengine
searchcore_pcommon
searchlib_test
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_attribute_test_app COMMAND searchcore_attribute_test_app)
diff --git a/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/CMakeLists.txt b/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/CMakeLists.txt
index de1ab5851f4..54c028fbfe8 100644
--- a/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/CMakeLists.txt
+++ b/searchcore/src/tests/proton/attribute/attribute_aspect_delayer/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_attribute_aspect_delayer_test_app TEST
SOURCES
attribute_aspect_delayer_test.cpp
DEPENDS
searchcore_attribute
searchcore_pcommon
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_attribute_aspect_delayer_test_app COMMAND searchcore_attribute_aspect_delayer_test_app)
diff --git a/searchcore/src/tests/proton/attribute/attribute_usage_sampler_functor/CMakeLists.txt b/searchcore/src/tests/proton/attribute/attribute_usage_sampler_functor/CMakeLists.txt
index 2cb6bc65df3..a48d7f19abe 100644
--- a/searchcore/src/tests/proton/attribute/attribute_usage_sampler_functor/CMakeLists.txt
+++ b/searchcore/src/tests/proton/attribute/attribute_usage_sampler_functor/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_attribute_usage_sampler_functor_test_app TEST
SOURCES
attribute_usage_sampler_functor_test.cpp
DEPENDS
searchcore_attribute
searchcore_pcommon
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_attribute_usage_sampler_functor_test_app COMMAND searchcore_attribute_usage_sampler_functor_test_app)
diff --git a/searchcore/src/tests/proton/common/operation_rate_tracker/CMakeLists.txt b/searchcore/src/tests/proton/common/operation_rate_tracker/CMakeLists.txt
index f5e6a791124..3c5152935d7 100644
--- a/searchcore/src/tests/proton/common/operation_rate_tracker/CMakeLists.txt
+++ b/searchcore/src/tests/proton/common/operation_rate_tracker/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_operation_rate_tracker_test_app TEST
SOURCES
operation_rate_tracker_test.cpp
DEPENDS
searchcore_pcommon
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_operation_rate_tracker_test_app COMMAND searchcore_operation_rate_tracker_test_app)
diff --git a/searchcore/src/tests/proton/documentdb/lid_space_compaction/CMakeLists.txt b/searchcore/src/tests/proton/documentdb/lid_space_compaction/CMakeLists.txt
index c4e69ace22c..3e008e3f5d0 100644
--- a/searchcore/src/tests/proton/documentdb/lid_space_compaction/CMakeLists.txt
+++ b/searchcore/src/tests/proton/documentdb/lid_space_compaction/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_lid_space_compaction_test_app TEST
SOURCES
lid_space_compaction_test.cpp
@@ -10,6 +11,6 @@ vespa_add_executable(searchcore_lid_space_compaction_test_app TEST
searchcore_documentmetastore
searchcore_bucketdb
searchcore_pcommon
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_lid_space_compaction_test_app COMMAND searchcore_lid_space_compaction_test_app)
diff --git a/searchcore/src/tests/proton/documentmetastore/CMakeLists.txt b/searchcore/src/tests/proton/documentmetastore/CMakeLists.txt
index f31d82240e0..c35d736acd1 100644
--- a/searchcore/src/tests/proton/documentmetastore/CMakeLists.txt
+++ b/searchcore/src/tests/proton/documentmetastore/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_documentmetastore_test_app TEST
SOURCES
documentmetastore_test.cpp
@@ -9,7 +10,7 @@ vespa_add_executable(searchcore_documentmetastore_test_app TEST
searchcore_attribute
searchcore_feedoperation
searchcore_fconfig
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_documentmetastore_test_app COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/documentmetastore_test.sh
DEPENDS searchcore_documentmetastore_test_app)
diff --git a/searchcore/src/tests/proton/index/CMakeLists.txt b/searchcore/src/tests/proton/index/CMakeLists.txt
index 1ffad6cdbf8..b967bf304c3 100644
--- a/searchcore/src/tests/proton/index/CMakeLists.txt
+++ b/searchcore/src/tests/proton/index/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_indexmanager_test_app TEST
SOURCES
indexmanager_test.cpp
@@ -7,7 +8,7 @@ vespa_add_executable(searchcore_indexmanager_test_app TEST
searchcore_index
searchcore_flushengine
searchcore_pcommon
- gtest
+ GTest::GTest
)
vespa_add_executable(searchcore_fusionrunner_test_app TEST
SOURCES
@@ -28,7 +29,7 @@ vespa_add_executable(searchcore_indexcollection_test_app TEST
indexcollection_test.cpp
DEPENDS
searchcore_index
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_index_test COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/index_test.sh
DEPENDS searchcore_indexmanager_test_app searchcore_fusionrunner_test_app searchcore_diskindexcleaner_test_app searchcore_indexcollection_test_app)
diff --git a/searchcore/src/tests/proton/matching/handle_recorder/CMakeLists.txt b/searchcore/src/tests/proton/matching/handle_recorder/CMakeLists.txt
index a56d22ab154..3f4fd071e5c 100644
--- a/searchcore/src/tests/proton/matching/handle_recorder/CMakeLists.txt
+++ b/searchcore/src/tests/proton/matching/handle_recorder/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_matching_handle_recorder_test_app TEST
SOURCES
handle_recorder_test.cpp
DEPENDS
searchcore_matching
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_matching_handle_recorder_test_app COMMAND searchcore_matching_handle_recorder_test_app)
diff --git a/searchcore/src/tests/proton/matching/request_context/CMakeLists.txt b/searchcore/src/tests/proton/matching/request_context/CMakeLists.txt
index 54696112302..c044a9d4869 100644
--- a/searchcore/src/tests/proton/matching/request_context/CMakeLists.txt
+++ b/searchcore/src/tests/proton/matching/request_context/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_matching_request_context_test_app TEST
SOURCES
request_context_test.cpp
DEPENDS
searchcore_matching
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_matching_request_context_test_app COMMAND searchcore_matching_request_context_test_app)
diff --git a/searchcore/src/tests/proton/matching/unpacking_iterators_optimizer/CMakeLists.txt b/searchcore/src/tests/proton/matching/unpacking_iterators_optimizer/CMakeLists.txt
index 6fce7151664..764208c0eba 100644
--- a/searchcore/src/tests/proton/matching/unpacking_iterators_optimizer/CMakeLists.txt
+++ b/searchcore/src/tests/proton/matching/unpacking_iterators_optimizer/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_unpacking_iterators_optimizer_test_app TEST
SOURCES
unpacking_iterators_optimizer_test.cpp
DEPENDS
searchcore_matching
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_unpacking_iterators_optimizer_test_app COMMAND searchcore_unpacking_iterators_optimizer_test_app)
diff --git a/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/CMakeLists.txt b/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/CMakeLists.txt
index 048ed1938e5..f96a31b7765 100644
--- a/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/CMakeLists.txt
+++ b/searchcore/src/tests/proton/reprocessing/attribute_reprocessing_initializer/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchcore_attribute_reprocessing_initializer_test_app TEST
SOURCES
attribute_reprocessing_initializer_test.cpp
@@ -6,6 +7,6 @@ vespa_add_executable(searchcore_attribute_reprocessing_initializer_test_app TEST
searchcore_reprocessing
searchcore_attribute
searchcore_pcommon
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchcore_attribute_reprocessing_initializer_test_app COMMAND searchcore_attribute_reprocessing_initializer_test_app)
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp b/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp
index fadea4b7962..19bece5ae9c 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_tools.cpp
@@ -194,7 +194,8 @@ MatchToolsFactory(QueryLimiter & queryLimiter,
trace.addEvent(4, "MTF: Fetch Postings");
_query.fetchPostings();
trace.addEvent(5, "MTF: Handle Global Filters");
- _query.handle_global_filters(searchContext.getDocIdLimit());
+ double global_filter_limit = GlobalFilterLimit::lookup(rankProperties, rankSetup.get_global_filter_limit());
+ _query.handle_global_filters(searchContext.getDocIdLimit(), global_filter_limit);
_query.freeze();
trace.addEvent(5, "MTF: prepareSharedState");
_rankSetup.prepareSharedState(_queryEnv, _queryEnv.getObjectStore());
diff --git a/searchcore/src/vespa/searchcore/proton/matching/query.cpp b/searchcore/src/vespa/searchcore/proton/matching/query.cpp
index 5213a2b9230..62a59ab7680 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/query.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/query.cpp
@@ -199,10 +199,11 @@ Query::fetchPostings()
}
void
-Query::handle_global_filters(uint32_t docid_limit)
+Query::handle_global_filters(uint32_t docid_limit, double global_filter_limit)
{
using search::queryeval::GlobalFilter;
- if (_blueprint->getState().want_global_filter()) {
+ double estimated_hit_ratio = _blueprint->getState().hit_ratio(docid_limit);
+ if (_blueprint->getState().want_global_filter() && estimated_hit_ratio >= global_filter_limit) {
auto constraint = Blueprint::FilterConstraint::UPPER_BOUND;
bool strict = true;
auto filter_iterator = _blueprint->createFilterSearch(strict, constraint);
diff --git a/searchcore/src/vespa/searchcore/proton/matching/query.h b/searchcore/src/vespa/searchcore/proton/matching/query.h
index 3ed6229830d..60f40e24d1e 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/query.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/query.h
@@ -89,7 +89,7 @@ public:
**/
void optimize();
void fetchPostings();
- void handle_global_filters(uint32_t docidLimit);
+ void handle_global_filters(uint32_t docidLimit, double global_filter_limit);
void freeze();
/**
diff --git a/searchlib/CMakeLists.txt b/searchlib/CMakeLists.txt
index 9b9f1a704cc..1db9018bd21 100644
--- a/searchlib/CMakeLists.txt
+++ b/searchlib/CMakeLists.txt
@@ -187,11 +187,9 @@ vespa_define_module(
src/tests/query
src/tests/queryeval
src/tests/queryeval/blueprint
- src/tests/queryeval/booleanmatchiteratorwrapper
src/tests/queryeval/dot_product
src/tests/queryeval/equiv
src/tests/queryeval/fake_searchable
- src/tests/queryeval/filter_wrapper
src/tests/queryeval/getnodeweight
src/tests/queryeval/monitoring_search_iterator
src/tests/queryeval/multibitvectoriterator
@@ -207,6 +205,7 @@ vespa_define_module(
src/tests/queryeval/weak_and_heap
src/tests/queryeval/weak_and_scorers
src/tests/queryeval/weighted_set_term
+ src/tests/queryeval/wrappers
src/tests/rankingexpression/feature_name_extractor
src/tests/rankingexpression/intrinsic_blueprint_adapter
src/tests/ranksetup
diff --git a/searchlib/abi-spec.json b/searchlib/abi-spec.json
index c22d906e2b2..0768999c52f 100644
--- a/searchlib/abi-spec.json
+++ b/searchlib/abi-spec.json
@@ -1614,6 +1614,7 @@
"public com.yahoo.tensor.functions.TensorFunction withArguments(java.util.List)",
"public com.yahoo.tensor.functions.PrimitiveTensorFunction toPrimitive()",
"public com.yahoo.tensor.TensorType type(com.yahoo.tensor.evaluation.TypeContext)",
+ "public java.util.Optional asScalarFunction()",
"public com.yahoo.tensor.Tensor evaluate(com.yahoo.tensor.evaluation.EvaluationContext)",
"public java.lang.String toString()",
"public java.lang.String toString(com.yahoo.tensor.functions.ToStringContext)"
diff --git a/searchlib/src/tests/attribute/attribute_header/CMakeLists.txt b/searchlib/src/tests/attribute/attribute_header/CMakeLists.txt
index e72c0c6a528..2327cf5116f 100644
--- a/searchlib/src/tests/attribute/attribute_header/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/attribute_header/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_attribute_header_test_app TEST
SOURCES
attribute_header_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_attribute_header_test_app COMMAND searchlib_attribute_header_test_app)
diff --git a/searchlib/src/tests/attribute/document_weight_or_filter_search/CMakeLists.txt b/searchlib/src/tests/attribute/document_weight_or_filter_search/CMakeLists.txt
index 660cfbaefff..8f25f6b66b8 100644
--- a/searchlib/src/tests/attribute/document_weight_or_filter_search/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/document_weight_or_filter_search/CMakeLists.txt
@@ -5,6 +5,7 @@ vespa_add_executable(searchlib_document_weight_or_filter_search_test_app TEST
document_weight_or_filter_search_test.cpp
DEPENDS
searchlib
+ searchlib_test
GTest::GTest
)
vespa_add_test(NAME searchlib_document_weight_or_filter_search_test_app COMMAND searchlib_document_weight_or_filter_search_test_app)
diff --git a/searchlib/src/tests/attribute/document_weight_or_filter_search/document_weight_or_filter_search_test.cpp b/searchlib/src/tests/attribute/document_weight_or_filter_search/document_weight_or_filter_search_test.cpp
index c8e799a8f10..22f928ddf45 100644
--- a/searchlib/src/tests/attribute/document_weight_or_filter_search/document_weight_or_filter_search_test.cpp
+++ b/searchlib/src/tests/attribute/document_weight_or_filter_search/document_weight_or_filter_search_test.cpp
@@ -5,6 +5,8 @@
#include <vespa/searchlib/attribute/document_weight_or_filter_search.h>
#include <vespa/searchlib/queryeval/searchiterator.h>
#include <vespa/searchlib/common/bitvector.h>
+#define ENABLE_GTEST_MIGRATION
+#include <vespa/searchlib/test/searchiteratorverifier.h>
using PostingList = search::attribute::PostingListTraits<int32_t>::PostingStoreBase;
using Iterator = search::attribute::PostingListTraits<int32_t>::const_iterator;
@@ -20,7 +22,7 @@ class DocumentWeightOrFilterSearchTest : public ::testing::Test {
std::vector<EntryRef> _trees;
uint32_t _range_start;
uint32_t _range_end;
-protected:
+public:
DocumentWeightOrFilterSearchTest();
~DocumentWeightOrFilterSearchTest();
void inc_generation();
@@ -48,6 +50,13 @@ protected:
_postings.apply(_trees[idx], &*adds.begin(), &*adds.end(), &*removes.begin(), &*removes.end());
}
+ void clear_tree(size_t idx) {
+ if (idx < _trees.size()) {
+ _postings.clear(_trees[idx]);
+ _trees[idx] = EntryRef();
+ }
+ }
+
std::unique_ptr<SearchIterator> make_iterator() const {
std::vector<Iterator> iterators;
for (size_t i = 0; i < num_trees(); ++i) {
@@ -200,4 +209,53 @@ TEST_F(DocumentWeightOrFilterSearchTest, taat_and_hits_into_ranged)
expect_result(frombv(*bv), { 14 });
}
+namespace {
+
+class Verifier : public search::test::SearchIteratorVerifier {
+ DocumentWeightOrFilterSearchTest &_test;
+public:
+ Verifier(DocumentWeightOrFilterSearchTest &test, int num_trees)
+ : _test(test)
+ {
+ std::vector<std::vector<uint32_t>> trees(num_trees);
+ uint32_t tree_id = 0;
+ for (const auto doc_id : getExpectedDocIds()) {
+ trees[tree_id++ % trees.size()].emplace_back(doc_id);
+ }
+ tree_id = 0;
+ for (const auto &tree : trees) {
+ _test.add_tree(tree_id++, tree);
+ }
+ _test.inc_generation();
+ }
+ ~Verifier() {
+ for (uint32_t tree_id = 0; tree_id < _test.num_trees(); ++tree_id) {
+ _test.clear_tree(tree_id);
+ }
+ _test.inc_generation();
+ }
+ std::unique_ptr<SearchIterator> create(bool) const override {
+ return _test.make_iterator();
+ }
+
+};
+
+TEST_F(DocumentWeightOrFilterSearchTest, iterator_conformance)
+{
+ {
+ Verifier verifier(*this, 1);
+ verifier.verify();
+ }
+ {
+ Verifier verifier(*this, 2);
+ verifier.verify();
+ }
+ {
+ Verifier verifier(*this, 3);
+ verifier.verify();
+ }
+}
+
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/attribute/enumstore/CMakeLists.txt b/searchlib/src/tests/attribute/enumstore/CMakeLists.txt
index e8f6068cebe..b9a361d05fc 100644
--- a/searchlib/src/tests/attribute/enumstore/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/enumstore/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_enumstore_test_app TEST
SOURCES
enumstore_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_enumstore_test_app COMMAND searchlib_enumstore_test_app)
diff --git a/searchlib/src/tests/attribute/multi_value_mapping/CMakeLists.txt b/searchlib/src/tests/attribute/multi_value_mapping/CMakeLists.txt
index 6b1f5f3d656..53856bf7b88 100644
--- a/searchlib/src/tests/attribute/multi_value_mapping/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/multi_value_mapping/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_multi_value_mapping_test_app TEST
SOURCES
multi_value_mapping_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_multi_value_mapping_test_app COMMAND searchlib_multi_value_mapping_test_app)
diff --git a/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt b/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt
index 6638bf886b7..9473f9f7fbf 100644
--- a/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/reference_attribute/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_reference_attribute_test_app TEST
SOURCES
reference_attribute_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_reference_attribute_test_app COMMAND searchlib_reference_attribute_test_app)
diff --git a/searchlib/src/tests/attribute/save_target/CMakeLists.txt b/searchlib/src/tests/attribute/save_target/CMakeLists.txt
index e127f66579e..dac326207da 100644
--- a/searchlib/src/tests/attribute/save_target/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/save_target/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_attribute_save_target_test_app TEST
SOURCES
attribute_save_target_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_attribute_save_target_test_app COMMAND searchlib_attribute_save_target_test_app)
diff --git a/searchlib/src/tests/attribute/searchable/CMakeLists.txt b/searchlib/src/tests/attribute/searchable/CMakeLists.txt
index e754253c34a..80c23e299f4 100644
--- a/searchlib/src/tests/attribute/searchable/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/searchable/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_attribute_searchable_adapter_test_app TEST
SOURCES
attribute_searchable_adapter_test.cpp
@@ -19,6 +20,6 @@ vespa_add_executable(searchlib_attribute_blueprint_test_app TEST
attributeblueprint_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_attribute_blueprint_test_app COMMAND searchlib_attribute_blueprint_test_app)
diff --git a/searchlib/src/tests/attribute/searchcontextelementiterator/CMakeLists.txt b/searchlib/src/tests/attribute/searchcontextelementiterator/CMakeLists.txt
index 1a27b446d30..6810d145124 100644
--- a/searchlib/src/tests/attribute/searchcontextelementiterator/CMakeLists.txt
+++ b/searchlib/src/tests/attribute/searchcontextelementiterator/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_attribute_searchcontextelementiterator_test_app TEST
SOURCES
searchcontextelementiterator_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_attribute_searchcontextelementiterator_test_app COMMAND searchlib_attribute_searchcontextelementiterator_test_app)
diff --git a/searchlib/src/tests/common/matching_elements/CMakeLists.txt b/searchlib/src/tests/common/matching_elements/CMakeLists.txt
index cd1d3560c15..ef7aa316dfd 100644
--- a/searchlib/src/tests/common/matching_elements/CMakeLists.txt
+++ b/searchlib/src/tests/common/matching_elements/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_common_matching_elements_test_app TEST
SOURCES
matching_elements_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_common_matching_elements_test_app COMMAND searchlib_common_matching_elements_test_app)
diff --git a/searchlib/src/tests/common/matching_elements_fields/CMakeLists.txt b/searchlib/src/tests/common/matching_elements_fields/CMakeLists.txt
index 3d6f338315a..f5a61787328 100644
--- a/searchlib/src/tests/common/matching_elements_fields/CMakeLists.txt
+++ b/searchlib/src/tests/common/matching_elements_fields/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_common_matching_elements_fields_test_app TEST
SOURCES
matching_elements_fields_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_common_matching_elements_fields_test_app COMMAND searchlib_common_matching_elements_fields_test_app)
diff --git a/searchlib/src/tests/engine/proto_converter/CMakeLists.txt b/searchlib/src/tests/engine/proto_converter/CMakeLists.txt
index 718e8120c2c..82e8e1b8be7 100644
--- a/searchlib/src/tests/engine/proto_converter/CMakeLists.txt
+++ b/searchlib/src/tests/engine/proto_converter/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_engine_proto_converter_test_app TEST
SOURCES
proto_converter_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_engine_proto_converter_test_app COMMAND searchlib_engine_proto_converter_test_app)
diff --git a/searchlib/src/tests/engine/proto_rpc_adapter/CMakeLists.txt b/searchlib/src/tests/engine/proto_rpc_adapter/CMakeLists.txt
index 3fa638f16e5..7d7259c40d8 100644
--- a/searchlib/src/tests/engine/proto_rpc_adapter/CMakeLists.txt
+++ b/searchlib/src/tests/engine/proto_rpc_adapter/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_engine_proto_rpc_adapter_test_app TEST
SOURCES
proto_rpc_adapter_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_engine_proto_rpc_adapter_test_app COMMAND searchlib_engine_proto_rpc_adapter_test_app)
diff --git a/searchlib/src/tests/features/bm25/CMakeLists.txt b/searchlib/src/tests/features/bm25/CMakeLists.txt
index 3f9b92684f8..35ad629169f 100644
--- a/searchlib/src/tests/features/bm25/CMakeLists.txt
+++ b/searchlib/src/tests/features/bm25/CMakeLists.txt
@@ -1,11 +1,12 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_features_bm25_test_app TEST
SOURCES
bm25_test.cpp
DEPENDS
searchlib
searchlib_test
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_features_bm25_test_app COMMAND searchlib_features_bm25_test_app)
diff --git a/searchlib/src/tests/features/prod_features.cpp b/searchlib/src/tests/features/prod_features.cpp
index c50c7a12698..f886ba59c1c 100644
--- a/searchlib/src/tests/features/prod_features.cpp
+++ b/searchlib/src/tests/features/prod_features.cpp
@@ -34,7 +34,7 @@
#include <vespa/searchlib/features/setup.h>
#include <vespa/searchlib/features/termfeature.h>
#include <vespa/searchlib/features/utils.h>
-#include <vespa/searchlib/features/uniquefeature.h>
+#include <vespa/searchlib/features/global_sequence_feature.h>
#include <vespa/searchlib/features/weighted_set_parser.hpp>
#include <vespa/searchlib/fef/featurenamebuilder.h>
#include <vespa/searchlib/fef/indexproperties.h>
@@ -1565,22 +1565,33 @@ Test::testMatchCount()
}
}
+void verifySequence(uint64_t first, uint64_t second) {
+ ASSERT_GREATER(first, second);
+ ASSERT_GREATER(double(first), double(second));
+}
+
void
Test::testUnique()
{
{
- UniqueBlueprint bp;
- EXPECT_TRUE(assertCreateInstance(bp, "unique"));
+ GlobalSequenceBlueprint bp;
+ EXPECT_TRUE(assertCreateInstance(bp, "globalSequence"));
FtFeatureTest ft(_factory, "");
StringList params, in, out;
FT_SETUP_OK(bp, ft.getIndexEnv(), params, in, out.add("out"));
- FT_DUMP_EMPTY(_factory, "unique");
+ FT_DUMP_EMPTY(_factory, "globalSequence");
}
- FtFeatureTest ft(_factory, "unique");
+ FtFeatureTest ft(_factory, "globalSequence");
ASSERT_TRUE(ft.setup());
- EXPECT_TRUE(ft.execute(0x10003,0, 1));
- EXPECT_TRUE(ft.execute(0x70003,0, 7));
-
+ TEST_DO(verifySequence(GlobalSequenceBlueprint::globalSequence(1, 0), GlobalSequenceBlueprint::globalSequence(1,1)));
+ TEST_DO(verifySequence(GlobalSequenceBlueprint::globalSequence(1, 1), GlobalSequenceBlueprint::globalSequence(1,2)));
+ TEST_DO(verifySequence(GlobalSequenceBlueprint::globalSequence(1, 1), GlobalSequenceBlueprint::globalSequence(2,1)));
+ TEST_DO(verifySequence(GlobalSequenceBlueprint::globalSequence(2, 1), GlobalSequenceBlueprint::globalSequence(2,2)));
+ TEST_DO(verifySequence(GlobalSequenceBlueprint::globalSequence(2, 2), GlobalSequenceBlueprint::globalSequence(2,3)));
+ TEST_DO(verifySequence(GlobalSequenceBlueprint::globalSequence(2, 2), GlobalSequenceBlueprint::globalSequence(3,0)));
+ ASSERT_EQUAL(0xfffffffefffdul, (1ul << 48) - 0x10003l);
+ EXPECT_TRUE(ft.execute(0xfffffffefffdul, 0, 1));
+ EXPECT_TRUE(ft.execute(0xfffffff8fffdul, 0, 7));
}
void
diff --git a/searchlib/src/tests/index/field_length_calculator/CMakeLists.txt b/searchlib/src/tests/index/field_length_calculator/CMakeLists.txt
index df09d0abaa7..e35927662e6 100644
--- a/searchlib/src/tests/index/field_length_calculator/CMakeLists.txt
+++ b/searchlib/src/tests/index/field_length_calculator/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_field_length_calculator_test_app TEST
SOURCES
field_length_calculator_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_field_length_calculator_test_app COMMAND searchlib_field_length_calculator_test_app)
diff --git a/searchlib/src/tests/memoryindex/compact_words_store/CMakeLists.txt b/searchlib/src/tests/memoryindex/compact_words_store/CMakeLists.txt
index 754ff796690..71e13db15fa 100644
--- a/searchlib/src/tests/memoryindex/compact_words_store/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/compact_words_store/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_compact_words_store_test_app TEST
SOURCES
compact_words_store_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_compact_words_store_test_app COMMAND searchlib_compact_words_store_test_app)
diff --git a/searchlib/src/tests/memoryindex/datastore/CMakeLists.txt b/searchlib/src/tests/memoryindex/datastore/CMakeLists.txt
index be1a193cd3c..770118a65b4 100644
--- a/searchlib/src/tests/memoryindex/datastore/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/datastore/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_feature_store_test_app TEST
SOURCES
feature_store_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_feature_store_test_app COMMAND searchlib_feature_store_test_app)
vespa_add_executable(searchlib_word_store_test_app TEST
@@ -12,6 +13,6 @@ vespa_add_executable(searchlib_word_store_test_app TEST
word_store_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_word_store_test_app COMMAND searchlib_word_store_test_app)
diff --git a/searchlib/src/tests/memoryindex/document_inverter/CMakeLists.txt b/searchlib/src/tests/memoryindex/document_inverter/CMakeLists.txt
index ecf33ee48fd..0831e87252f 100644
--- a/searchlib/src/tests/memoryindex/document_inverter/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/document_inverter/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_document_inverter_test_app TEST
SOURCES
document_inverter_test.cpp
DEPENDS
searchlib_test
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_document_inverter_test_app COMMAND searchlib_document_inverter_test_app)
diff --git a/searchlib/src/tests/memoryindex/field_index/CMakeLists.txt b/searchlib/src/tests/memoryindex/field_index/CMakeLists.txt
index a09d6baf1a5..2568249c2df 100644
--- a/searchlib/src/tests/memoryindex/field_index/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/field_index/CMakeLists.txt
@@ -1,11 +1,12 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_field_index_test_app TEST
SOURCES
field_index_test.cpp
DEPENDS
searchlib
searchlib_test
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_field_index_test_app COMMAND searchlib_field_index_test_app)
diff --git a/searchlib/src/tests/memoryindex/field_index_remover/CMakeLists.txt b/searchlib/src/tests/memoryindex/field_index_remover/CMakeLists.txt
index f18b4ba29cd..bc3a62c4f26 100644
--- a/searchlib/src/tests/memoryindex/field_index_remover/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/field_index_remover/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_field_index_remover_test_app TEST
SOURCES
field_index_remover_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_field_index_remover_test_app COMMAND searchlib_field_index_remover_test_app)
diff --git a/searchlib/src/tests/memoryindex/field_inverter/CMakeLists.txt b/searchlib/src/tests/memoryindex/field_inverter/CMakeLists.txt
index 6fefada6570..b670e0ccf57 100644
--- a/searchlib/src/tests/memoryindex/field_inverter/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/field_inverter/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_field_inverter_test_app TEST
SOURCES
field_inverter_test.cpp
DEPENDS
searchlib_test
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_field_inverter_test_app COMMAND searchlib_field_inverter_test_app)
diff --git a/searchlib/src/tests/memoryindex/memory_index/CMakeLists.txt b/searchlib/src/tests/memoryindex/memory_index/CMakeLists.txt
index 0dd42eacf30..611aa6e58c6 100644
--- a/searchlib/src/tests/memoryindex/memory_index/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/memory_index/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_memory_index_test_app TEST
SOURCES
memory_index_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_memory_index_test_app COMMAND searchlib_memory_index_test_app)
diff --git a/searchlib/src/tests/memoryindex/url_field_inverter/CMakeLists.txt b/searchlib/src/tests/memoryindex/url_field_inverter/CMakeLists.txt
index db9418b7190..0378d209d5e 100644
--- a/searchlib/src/tests/memoryindex/url_field_inverter/CMakeLists.txt
+++ b/searchlib/src/tests/memoryindex/url_field_inverter/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_url_field_inverter_test_app TEST
SOURCES
url_field_inverter_test.cpp
DEPENDS
searchlib_test
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_url_field_inverter_test_app COMMAND searchlib_url_field_inverter_test_app)
diff --git a/searchlib/src/tests/postinglistbm/CMakeLists.txt b/searchlib/src/tests/postinglistbm/CMakeLists.txt
index 6e90f44726a..004cff76efd 100644
--- a/searchlib/src/tests/postinglistbm/CMakeLists.txt
+++ b/searchlib/src/tests/postinglistbm/CMakeLists.txt
@@ -1,11 +1,12 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_posting_list_test_app TEST
SOURCES
posting_list_test.cpp
DEPENDS
searchlib_test
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_posting_list_test_app NO_VALGRIND COMMAND searchlib_posting_list_test_app)
diff --git a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.cvsignore b/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.cvsignore
deleted file mode 100644
index 9e6565f9d16..00000000000
--- a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.cvsignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.depend
-Makefile
-booleanmatchiteratorwrapper_test
diff --git a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.gitignore b/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.gitignore
deleted file mode 100644
index b568b87514a..00000000000
--- a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.depend
-Makefile
-booleanmatchiteratorwrapper_test
-searchlib_booleanmatchiteratorwrapper_test_app
diff --git a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/CMakeLists.txt b/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/CMakeLists.txt
deleted file mode 100644
index d97fe5b12d7..00000000000
--- a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_executable(searchlib_booleanmatchiteratorwrapper_test_app TEST
- SOURCES
- booleanmatchiteratorwrapper_test.cpp
- DEPENDS
- searchlib
- searchlib_test
-)
-vespa_add_test(NAME searchlib_booleanmatchiteratorwrapper_test_app COMMAND searchlib_booleanmatchiteratorwrapper_test_app)
diff --git a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/booleanmatchiteratorwrapper_test.cpp b/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/booleanmatchiteratorwrapper_test.cpp
deleted file mode 100644
index 7d4551a5c7d..00000000000
--- a/searchlib/src/tests/queryeval/booleanmatchiteratorwrapper/booleanmatchiteratorwrapper_test.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-// 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/searchlib/queryeval/booleanmatchiteratorwrapper.h>
-#include <vespa/searchlib/fef/termfieldmatchdata.h>
-#include <vespa/searchlib/common/bitvectoriterator.h>
-#include <vespa/searchlib/test/searchiteratorverifier.h>
-
-using namespace search::fef;
-using namespace search::queryeval;
-using search::BitVector;
-using search::BitVectorIterator;
-
-struct DummyItr : public SearchIterator {
- static uint32_t seekCnt;
- static uint32_t unpackCnt;
- static uint32_t dtorCnt;
- static uint32_t _unpackedDocId;
- TermFieldMatchData *match;
-
- DummyItr(TermFieldMatchData *m) {
- match = m;
- }
-
- ~DummyItr() {
- ++dtorCnt;
- }
-
- void doSeek(uint32_t docid) override {
- ++seekCnt;
- if (docid <= 10) {
- setDocId(10);
- } else if (docid <= 20) {
- setDocId(20);
- } else {
- setAtEnd();
- }
- }
-
- void doUnpack(uint32_t docid) override {
- ++unpackCnt;
- if (match != 0) {
- _unpackedDocId = docid;
- }
- }
-};
-uint32_t DummyItr::seekCnt = 0;
-uint32_t DummyItr::unpackCnt = 0;
-uint32_t DummyItr::dtorCnt = 0;
-uint32_t DummyItr::_unpackedDocId = 0;
-
-
-TEST("mostly everything") {
- EXPECT_EQUAL(DummyItr::seekCnt, 0u);
- EXPECT_EQUAL(DummyItr::unpackCnt, 0u);
- EXPECT_EQUAL(DummyItr::dtorCnt, 0u);
- { // without wrapper
- TermFieldMatchData match;
- DummyItr::_unpackedDocId = 0;
- SearchIterator::UP search(new DummyItr(&match));
- search->initFullRange();
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(1u));
- EXPECT_EQUAL(search->getDocId(), 10u);
- EXPECT_TRUE(search->seek(10));
- search->unpack(10);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 10u);
- EXPECT_TRUE(!search->seek(15));
- EXPECT_EQUAL(search->getDocId(), 20u);
- EXPECT_TRUE(search->seek(20));
- search->unpack(20);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 20u);
- EXPECT_TRUE(!search->seek(25));
- EXPECT_TRUE(search->isAtEnd());
- }
- EXPECT_EQUAL(DummyItr::seekCnt, 3u);
- EXPECT_EQUAL(DummyItr::unpackCnt, 2u);
- EXPECT_EQUAL(DummyItr::dtorCnt, 1u);
- { // with wrapper
- TermFieldMatchData match;
- TermFieldMatchDataArray tfmda;
- tfmda.add(&match);
- DummyItr::_unpackedDocId = 0;
- SearchIterator::UP search(new BooleanMatchIteratorWrapper(SearchIterator::UP(new DummyItr(&match)), tfmda));
- search->initFullRange();
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(1u));
- EXPECT_EQUAL(search->getDocId(), 10u);
- EXPECT_TRUE(search->seek(10));
- search->unpack(10);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(15));
- EXPECT_EQUAL(search->getDocId(), 20u);
- EXPECT_TRUE(search->seek(20));
- search->unpack(20);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(25));
- EXPECT_TRUE(search->isAtEnd());
- }
- EXPECT_EQUAL(DummyItr::seekCnt, 6u);
- EXPECT_EQUAL(DummyItr::unpackCnt, 2u);
- EXPECT_EQUAL(DummyItr::dtorCnt, 2u);
- { // with wrapper, without match data
- SearchIterator::UP search(new BooleanMatchIteratorWrapper(SearchIterator::UP(new DummyItr(0)), TermFieldMatchDataArray()));
- search->initFullRange();
- EXPECT_TRUE(!search->seek(1u));
- EXPECT_EQUAL(search->getDocId(), 10u);
- EXPECT_TRUE(search->seek(10));
- search->unpack(10);
- EXPECT_TRUE(!search->seek(15));
- EXPECT_EQUAL(search->getDocId(), 20u);
- EXPECT_TRUE(search->seek(20));
- search->unpack(20);
- EXPECT_TRUE(!search->seek(25));
- EXPECT_TRUE(search->isAtEnd());
- }
- EXPECT_EQUAL(DummyItr::seekCnt, 9u);
- EXPECT_EQUAL(DummyItr::unpackCnt, 2u);
- EXPECT_EQUAL(DummyItr::dtorCnt, 3u);
-}
-
-class Verifier : public search::test::SearchIteratorVerifier {
-public:
- ~Verifier();
- SearchIterator::UP create(bool strict) const override {
- return std::make_unique<BooleanMatchIteratorWrapper>(createIterator(getExpectedDocIds(), strict), _tfmda);;
- }
-private:
- mutable TermFieldMatchDataArray _tfmda;
-};
-
-Verifier::~Verifier() {}
-
-TEST("Test that boolean wrapper iterators adheres to SearchIterator requirements") {
- Verifier searchIteratorVerifier;
- searchIteratorVerifier.verify();
-}
-
-TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt b/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt
index c98306aa179..d21524d158a 100644
--- a/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt
+++ b/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_fake_searchable_test_app TEST
SOURCES
fake_searchable_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_fake_searchable_test_app COMMAND searchlib_fake_searchable_test_app)
diff --git a/searchlib/src/tests/queryeval/filter_wrapper/CMakeLists.txt b/searchlib/src/tests/queryeval/filter_wrapper/CMakeLists.txt
deleted file mode 100644
index bab956e73bb..00000000000
--- a/searchlib/src/tests/queryeval/filter_wrapper/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-vespa_add_executable(searchlib_filter_wrapper_test_app TEST
- SOURCES
- filter_wrapper_test.cpp
- DEPENDS
- searchlib
- searchlib_test
-)
-vespa_add_test(NAME searchlib_filter_wrapper_test_app COMMAND searchlib_filter_wrapper_test_app)
diff --git a/searchlib/src/tests/queryeval/filter_wrapper/filter_wrapper_test.cpp b/searchlib/src/tests/queryeval/filter_wrapper/filter_wrapper_test.cpp
deleted file mode 100644
index 707f51c551a..00000000000
--- a/searchlib/src/tests/queryeval/filter_wrapper/filter_wrapper_test.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include <vespa/vespalib/testkit/testapp.h>
-#include <vespa/searchlib/queryeval/filter_wrapper.h>
-#include <vespa/searchlib/fef/termfieldmatchdata.h>
-#include <vespa/searchlib/common/bitvectoriterator.h>
-#include <vespa/searchlib/test/searchiteratorverifier.h>
-
-using namespace search::fef;
-using namespace search::queryeval;
-using search::BitVector;
-using search::BitVectorIterator;
-
-struct DummyItr : public SearchIterator {
- static uint32_t seekCnt;
- static uint32_t unpackCnt;
- static uint32_t dtorCnt;
- static uint32_t _unpackedDocId;
- TermFieldMatchData *match;
-
- DummyItr(TermFieldMatchData *m) {
- match = m;
- }
-
- ~DummyItr() {
- ++dtorCnt;
- }
-
- void doSeek(uint32_t docid) override {
- ++seekCnt;
- if (docid <= 10) {
- setDocId(10);
- } else if (docid <= 20) {
- setDocId(20);
- } else {
- setAtEnd();
- }
- }
-
- void doUnpack(uint32_t docid) override {
- ++unpackCnt;
- if (match != 0) {
- _unpackedDocId = docid;
- }
- }
-};
-uint32_t DummyItr::seekCnt = 0;
-uint32_t DummyItr::unpackCnt = 0;
-uint32_t DummyItr::dtorCnt = 0;
-uint32_t DummyItr::_unpackedDocId = 0;
-
-
-TEST("filter wrapper forwards as expected") {
- EXPECT_EQUAL(DummyItr::seekCnt, 0u);
- EXPECT_EQUAL(DummyItr::unpackCnt, 0u);
- EXPECT_EQUAL(DummyItr::dtorCnt, 0u);
- { // without wrapper
- TermFieldMatchData match;
- DummyItr::_unpackedDocId = 0;
- SearchIterator::UP search(new DummyItr(&match));
- search->initFullRange();
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(1u));
- EXPECT_EQUAL(search->getDocId(), 10u);
- EXPECT_TRUE(search->seek(10));
- search->unpack(10);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 10u);
- EXPECT_TRUE(!search->seek(15));
- EXPECT_EQUAL(search->getDocId(), 20u);
- EXPECT_TRUE(search->seek(20));
- search->unpack(20);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 20u);
- EXPECT_TRUE(!search->seek(25));
- EXPECT_TRUE(search->isAtEnd());
- }
- EXPECT_EQUAL(DummyItr::seekCnt, 3u);
- EXPECT_EQUAL(DummyItr::unpackCnt, 2u);
- EXPECT_EQUAL(DummyItr::dtorCnt, 1u);
- { // with wrapper
- TermFieldMatchData match;
- TermFieldMatchDataArray tfmda;
- tfmda.add(&match);
- DummyItr::_unpackedDocId = 0;
- auto search = std::make_unique<FilterWrapper>(1);
- auto to_wrap = std::make_unique<DummyItr>(search->tfmda()[0]);
- search->wrap(std::move(to_wrap));
- search->initFullRange();
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(1u));
- EXPECT_EQUAL(search->getDocId(), 10u);
- EXPECT_TRUE(search->seek(10));
- search->unpack(10);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(15));
- EXPECT_EQUAL(search->getDocId(), 20u);
- EXPECT_TRUE(search->seek(20));
- search->unpack(20);
- EXPECT_EQUAL(DummyItr::_unpackedDocId, 0u);
- EXPECT_TRUE(!search->seek(25));
- EXPECT_TRUE(search->isAtEnd());
- }
- EXPECT_EQUAL(DummyItr::seekCnt, 6u);
- EXPECT_EQUAL(DummyItr::unpackCnt, 2u);
- EXPECT_EQUAL(DummyItr::dtorCnt, 2u);
-}
-
-class Verifier : public search::test::SearchIteratorVerifier {
-public:
- ~Verifier();
- SearchIterator::UP create(bool strict) const override {
- auto search = std::make_unique<FilterWrapper>(1);
- search->wrap(createIterator(getExpectedDocIds(), strict));
- return search;
- }
-};
-
-Verifier::~Verifier() {}
-
-TEST("Test that filter wrapper iterators adheres to SearchIterator requirements") {
- Verifier searchIteratorVerifier;
- searchIteratorVerifier.verify();
-}
-
-TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/tests/queryeval/wrappers/CMakeLists.txt b/searchlib/src/tests/queryeval/wrappers/CMakeLists.txt
new file mode 100644
index 00000000000..d1d566b1cab
--- /dev/null
+++ b/searchlib/src/tests/queryeval/wrappers/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+vespa_add_executable(searchlib_wrappers_test_app TEST
+ SOURCES
+ wrappers_test.cpp
+ DEPENDS
+ searchlib
+ searchlib_test
+ gtest
+)
+vespa_add_test(NAME searchlib_wrappers_test_app COMMAND searchlib_wrappers_test_app)
diff --git a/searchlib/src/tests/queryeval/wrappers/wrappers_test.cpp b/searchlib/src/tests/queryeval/wrappers/wrappers_test.cpp
new file mode 100644
index 00000000000..62bff93ab0e
--- /dev/null
+++ b/searchlib/src/tests/queryeval/wrappers/wrappers_test.cpp
@@ -0,0 +1,197 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchlib/queryeval/filter_wrapper.h>
+#include <vespa/searchlib/queryeval/booleanmatchiteratorwrapper.h>
+#include <vespa/searchlib/fef/termfieldmatchdata.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+#define ENABLE_GTEST_MIGRATION
+#include <vespa/searchlib/test/searchiteratorverifier.h>
+
+using namespace search::fef;
+using namespace search::queryeval;
+
+struct ObservedData {
+ uint32_t seekCnt;
+ uint32_t unpackCnt;
+ uint32_t dtorCnt;
+ uint32_t unpackedDocId;
+};
+
+class WrapperTest : public ::testing::Test {
+public:
+ class DummyItr : public SearchIterator {
+ private:
+ ObservedData &_data;
+ TermFieldMatchData *_match;
+ public:
+ DummyItr(ObservedData &data, TermFieldMatchData *m) : _data(data), _match(m) {}
+ ~DummyItr() {
+ ++_data.dtorCnt;
+ }
+ void doSeek(uint32_t docid) override {
+ ++_data.seekCnt;
+ if (docid <= 10) {
+ setDocId(10);
+ } else if (docid <= 20) {
+ setDocId(20);
+ } else {
+ setAtEnd();
+ }
+ }
+ void doUnpack(uint32_t docid) override {
+ ++_data.unpackCnt;
+ if (_match != 0) {
+ _data.unpackedDocId = docid;
+ }
+ }
+ };
+ WrapperTest() : _data{0,0,0,0} {}
+protected:
+ ObservedData _data;
+
+ void verify_unwrapped() {
+ EXPECT_EQ(_data.seekCnt, 0u);
+ EXPECT_EQ(_data.unpackCnt, 0u);
+ EXPECT_EQ(_data.dtorCnt, 0u);
+
+ // without wrapper
+ TermFieldMatchData match;
+ _data.unpackedDocId = 0;
+ auto search = std::make_unique<DummyItr>(_data, &match);
+ search->initFullRange();
+ EXPECT_EQ(_data.unpackedDocId, 0u);
+ EXPECT_TRUE(!search->seek(1u));
+ EXPECT_EQ(search->getDocId(), 10u);
+ EXPECT_TRUE(search->seek(10));
+ search->unpack(10);
+ EXPECT_EQ(_data.unpackedDocId, 10u);
+ EXPECT_TRUE(!search->seek(15));
+ EXPECT_EQ(search->getDocId(), 20u);
+ EXPECT_TRUE(search->seek(20));
+ search->unpack(20);
+ EXPECT_EQ(_data.unpackedDocId, 20u);
+ EXPECT_TRUE(!search->seek(25));
+ EXPECT_TRUE(search->isAtEnd());
+
+ search.reset(nullptr);
+ EXPECT_EQ(_data.seekCnt, 3u);
+ EXPECT_EQ(_data.unpackCnt, 2u);
+ EXPECT_EQ(_data.dtorCnt, 1u);
+ }
+};
+
+TEST_F(WrapperTest, filter_wrapper)
+{
+ verify_unwrapped();
+
+ // with FilterWrapper
+ TermFieldMatchData match;
+ TermFieldMatchDataArray tfmda;
+ tfmda.add(&match);
+ _data.unpackedDocId = 0;
+ auto search = std::make_unique<FilterWrapper>(1);
+search->wrap(std::make_unique<DummyItr>(_data, search->tfmda()[0]));
+ search->initFullRange();
+ EXPECT_EQ(_data.unpackedDocId, 0u);
+ EXPECT_TRUE(!search->seek(1u));
+ EXPECT_EQ(search->getDocId(), 10u);
+ EXPECT_TRUE(search->seek(10));
+ search->unpack(10);
+ EXPECT_EQ(_data.unpackedDocId, 0u);
+ EXPECT_TRUE(!search->seek(15));
+ EXPECT_EQ(search->getDocId(), 20u);
+ EXPECT_TRUE(search->seek(20));
+ search->unpack(20);
+ EXPECT_EQ(_data.unpackedDocId, 0u);
+ EXPECT_TRUE(!search->seek(25));
+ EXPECT_TRUE(search->isAtEnd());
+
+ search.reset(nullptr);
+ EXPECT_EQ(_data.seekCnt, 6u);
+ EXPECT_EQ(_data.unpackCnt, 2u);
+ EXPECT_EQ(_data.dtorCnt, 2u);
+}
+
+TEST_F(WrapperTest, boolean_match_iterator_wrapper)
+{
+ verify_unwrapped();
+ { // with wrapper
+ TermFieldMatchData match;
+ TermFieldMatchDataArray tfmda;
+ tfmda.add(&match);
+ _data.unpackedDocId = 0;
+ auto to_wrap = std::make_unique<DummyItr>(_data, &match);
+ auto search = std::make_unique<BooleanMatchIteratorWrapper>(std::move(to_wrap), tfmda);
+ search->initFullRange();
+ EXPECT_EQ(_data.unpackedDocId, 0u);
+ EXPECT_TRUE(!search->seek(1u));
+ EXPECT_EQ(search->getDocId(), 10u);
+ EXPECT_TRUE(search->seek(10));
+ search->unpack(10);
+ EXPECT_EQ(_data.unpackedDocId, 0u);
+ EXPECT_TRUE(!search->seek(15));
+ EXPECT_EQ(search->getDocId(), 20u);
+ EXPECT_TRUE(search->seek(20));
+ search->unpack(20);
+ EXPECT_EQ(_data.unpackedDocId, 0u);
+ EXPECT_TRUE(!search->seek(25));
+ EXPECT_TRUE(search->isAtEnd());
+ }
+ EXPECT_EQ(_data.seekCnt, 6u);
+ EXPECT_EQ(_data.unpackCnt, 2u);
+ EXPECT_EQ(_data.dtorCnt, 2u);
+ { // with wrapper, without match data
+
+ auto to_wrap = std::make_unique<DummyItr>(_data, nullptr);
+ auto search = std::make_unique<BooleanMatchIteratorWrapper>(std::move(to_wrap), TermFieldMatchDataArray());
+ search->initFullRange();
+ EXPECT_TRUE(!search->seek(1u));
+ EXPECT_EQ(search->getDocId(), 10u);
+ EXPECT_TRUE(search->seek(10));
+ search->unpack(10);
+ EXPECT_TRUE(!search->seek(15));
+ EXPECT_EQ(search->getDocId(), 20u);
+ EXPECT_TRUE(search->seek(20));
+ search->unpack(20);
+ EXPECT_TRUE(!search->seek(25));
+ EXPECT_TRUE(search->isAtEnd());
+ }
+ EXPECT_EQ(_data.seekCnt, 9u);
+ EXPECT_EQ(_data.unpackCnt, 2u);
+ EXPECT_EQ(_data.dtorCnt, 3u);
+}
+
+class FilterWrapperVerifier : public search::test::SearchIteratorVerifier {
+public:
+ ~FilterWrapperVerifier() {}
+ SearchIterator::UP create(bool strict) const override {
+ auto search = std::make_unique<FilterWrapper>(1);
+ search->wrap(createIterator(getExpectedDocIds(), strict));
+ return search;
+ }
+};
+
+TEST(FilterWrapperTest, adheres_to_search_iterator_requirements)
+{
+ FilterWrapperVerifier verifier;
+ verifier.verify();
+}
+
+class BooleanMatchIteratorWrapperVerifier : public search::test::SearchIteratorVerifier {
+public:
+ SearchIterator::UP create(bool strict) const override {
+ return std::make_unique<BooleanMatchIteratorWrapper>(createIterator(getExpectedDocIds(), strict), _tfmda);
+ }
+ ~BooleanMatchIteratorWrapperVerifier() {}
+private:
+ mutable TermFieldMatchDataArray _tfmda;
+};
+
+TEST(BooleanMatchIteratorWrapperWrapperTest, adheres_to_search_iterator_requirements)
+{
+ BooleanMatchIteratorWrapperVerifier verifier;
+ verifier.verify();
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt b/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt
index 9356658f90e..6f7c71d4d54 100644
--- a/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt
+++ b/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_distance_functions_test_app TEST
SOURCES
distance_functions_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_distance_functions_test_app COMMAND searchlib_distance_functions_test_app)
diff --git a/searchlib/src/tests/tensor/hnsw_index/CMakeLists.txt b/searchlib/src/tests/tensor/hnsw_index/CMakeLists.txt
index b6a87502fdf..0fd2f9a205a 100644
--- a/searchlib/src/tests/tensor/hnsw_index/CMakeLists.txt
+++ b/searchlib/src/tests/tensor/hnsw_index/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_hnsw_index_test_app TEST
SOURCES
hnsw_index_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_hnsw_index_test_app COMMAND searchlib_hnsw_index_test_app)
@@ -13,5 +14,5 @@ vespa_add_executable(mt_stress_hnsw_app TEST
stress_hnsw_mt.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
diff --git a/searchlib/src/tests/tensor/hnsw_saver/CMakeLists.txt b/searchlib/src/tests/tensor/hnsw_saver/CMakeLists.txt
index 90202e222a7..0dbd80c68d0 100644
--- a/searchlib/src/tests/tensor/hnsw_saver/CMakeLists.txt
+++ b/searchlib/src/tests/tensor/hnsw_saver/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(searchlib_hnsw_save_load_test_app TEST
SOURCES
hnsw_save_load_test.cpp
DEPENDS
searchlib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME searchlib_hnsw_save_load_test_app COMMAND searchlib_hnsw_save_load_test_app)
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
index c4467df5a1c..4a34a07a773 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
@@ -416,9 +416,25 @@ public:
.setDocIdLimit(get_docid_limit()),
_weights, _terms, _attr, strict);
}
+ std::unique_ptr<SearchIterator> createFilterSearch(bool strict, FilterConstraint constraint) const override;
bool always_needs_unpack() const override { return true; }
};
+std::unique_ptr<SearchIterator>
+DirectWandBlueprint::createFilterSearch(bool, FilterConstraint constraint) const
+{
+ if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
+ std::vector<DocumentWeightIterator> iterators;
+ iterators.reserve(_terms.size());
+ for (const IDocumentWeightAttribute::LookupResult &r : _terms) {
+ _attr.create(r.posting_idx, iterators);
+ }
+ return attribute::DocumentWeightOrFilterSearch::create(std::move(iterators));
+ } else {
+ return std::make_unique<queryeval::EmptySearch>();
+ }
+}
+
//-----------------------------------------------------------------------------
class DirectAttributeBlueprint : public queryeval::SimpleLeafBlueprint
diff --git a/searchlib/src/vespa/searchlib/attribute/document_weight_or_filter_search.cpp b/searchlib/src/vespa/searchlib/attribute/document_weight_or_filter_search.cpp
index 35c5ce75f33..d9134417d27 100644
--- a/searchlib/src/vespa/searchlib/attribute/document_weight_or_filter_search.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/document_weight_or_filter_search.cpp
@@ -53,7 +53,7 @@ DocumentWeightOrFilterSearchImpl::doSeek(uint32_t docId)
_children.seek(0, docId);
}
uint32_t min_doc_id = _children.get_docid(0);
- for (uint16_t i = 1; min_doc_id > docId && i < _children.size(); ++i) {
+ for (uint16_t i = 1; i < _children.size(); ++i) {
if (_children.get_docid(i) < docId) {
_children.seek(i, docId);
}
diff --git a/searchlib/src/vespa/searchlib/features/CMakeLists.txt b/searchlib/src/vespa/searchlib/features/CMakeLists.txt
index a3ce67c4bf6..215b6ade9fd 100644
--- a/searchlib/src/vespa/searchlib/features/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/features/CMakeLists.txt
@@ -26,6 +26,7 @@ vespa_add_library(searchlib_features OBJECT
flow_completeness_feature.cpp
foreachfeature.cpp
freshnessfeature.cpp
+ global_sequence_feature.cpp
internal_max_reduce_prod_join_feature.cpp
item_raw_score_feature.cpp
jarowinklerdistancefeature.cpp
@@ -63,7 +64,6 @@ vespa_add_library(searchlib_features OBJECT
termfeature.cpp
terminfofeature.cpp
text_similarity_feature.cpp
- uniquefeature.cpp
utils.cpp
valuefeature.cpp
weighted_set_parser.cpp
diff --git a/searchlib/src/vespa/searchlib/features/global_sequence_feature.cpp b/searchlib/src/vespa/searchlib/features/global_sequence_feature.cpp
new file mode 100644
index 00000000000..255b033a592
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/features/global_sequence_feature.cpp
@@ -0,0 +1,64 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "global_sequence_feature.h"
+#include <vespa/vespalib/util/stash.h>
+
+using namespace search::fef;
+
+namespace search::features {
+
+namespace {
+
+/**
+ * Implements the executor for combining lid and distribution key to form a globally unique value.
+ */
+class GlobalSequenceExecutor : public fef::FeatureExecutor {
+private:
+ uint32_t _distributionKey;
+
+public:
+ GlobalSequenceExecutor(uint32_t distributionKey)
+ : _distributionKey(distributionKey)
+ {
+ }
+
+ void execute(uint32_t docId) override {
+ outputs().set_number(0, GlobalSequenceBlueprint::globalSequence(docId, _distributionKey));
+ }
+};
+
+}
+
+GlobalSequenceBlueprint::GlobalSequenceBlueprint() :
+ Blueprint("globalSequence"),
+ _distributionKey(0)
+{
+}
+
+void
+GlobalSequenceBlueprint::visitDumpFeatures(const IIndexEnvironment &, IDumpFeatureVisitor &) const
+{
+}
+
+bool
+GlobalSequenceBlueprint::setup(const IIndexEnvironment & env, const ParameterList & )
+{
+ _distributionKey = env.getDistributionKey();
+ assert( _distributionKey < 0x10000);
+ describeOutput("out", "Returns (1 << 48) - ((lid << 16) | distributionKey)");
+ return true;
+}
+
+Blueprint::UP
+GlobalSequenceBlueprint::createInstance() const
+{
+ return std::make_unique<GlobalSequenceBlueprint>();
+}
+
+FeatureExecutor &
+GlobalSequenceBlueprint::createExecutor(const IQueryEnvironment &, vespalib::Stash &stash) const
+{
+ return stash.create<GlobalSequenceExecutor>(_distributionKey);
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/features/uniquefeature.h b/searchlib/src/vespa/searchlib/features/global_sequence_feature.h
index f21a427762a..3678a260b00 100644
--- a/searchlib/src/vespa/searchlib/features/uniquefeature.h
+++ b/searchlib/src/vespa/searchlib/features/global_sequence_feature.h
@@ -14,12 +14,12 @@ namespace search::features {
* It will change if documents change lid.
*/
-class UniqueBlueprint : public fef::Blueprint
+class GlobalSequenceBlueprint : public fef::Blueprint
{
private:
uint32_t _distributionKey;
public:
- UniqueBlueprint();
+ GlobalSequenceBlueprint();
void visitDumpFeatures(const fef::IIndexEnvironment & env, fef::IDumpFeatureVisitor & visitor) const override;
fef::Blueprint::UP createInstance() const override;
fef::ParameterDescriptions getDescriptions() const override {
@@ -27,6 +27,10 @@ public:
}
bool setup(const fef::IIndexEnvironment & env, const fef::ParameterList & params) override;
fef::FeatureExecutor &createExecutor(const fef::IQueryEnvironment &env, vespalib::Stash &stash) const override;
+
+ static uint64_t globalSequence(uint32_t docId, uint32_t distrKey) {
+ return (1ul << 48) - ((uint64_t(docId) << 16)| distrKey);
+ }
};
}
diff --git a/searchlib/src/vespa/searchlib/features/setup.cpp b/searchlib/src/vespa/searchlib/features/setup.cpp
index ea6ec842a00..bd79f1d4fb5 100644
--- a/searchlib/src/vespa/searchlib/features/setup.cpp
+++ b/searchlib/src/vespa/searchlib/features/setup.cpp
@@ -53,7 +53,7 @@
#include "termfeature.h"
#include "terminfofeature.h"
#include "text_similarity_feature.h"
-#include "uniquefeature.h"
+#include "global_sequence_feature.h"
#include "valuefeature.h"
#include "max_reduce_prod_join_replacer.h"
@@ -122,7 +122,7 @@ void setup_search_features(fef::IBlueprintRegistry & registry)
registry.addPrototype(std::make_shared<TermEditDistanceBlueprint>());
registry.addPrototype(std::make_shared<TermFieldMdBlueprint>());
registry.addPrototype(std::make_shared<ConstantBlueprint>());
- registry.addPrototype(std::make_shared<UniqueBlueprint>());
+ registry.addPrototype(std::make_shared<GlobalSequenceBlueprint>());
// Ranking Expression
diff --git a/searchlib/src/vespa/searchlib/features/uniquefeature.cpp b/searchlib/src/vespa/searchlib/features/uniquefeature.cpp
deleted file mode 100644
index 73ac4a1178e..00000000000
--- a/searchlib/src/vespa/searchlib/features/uniquefeature.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "uniquefeature.h"
-#include <vespa/vespalib/util/stash.h>
-
-using namespace search::fef;
-
-namespace search::features {
-
-namespace {
-
-/**
- * Implements the executor for combining lid and distribution key to form a globally unique value.
- */
-class UniqueLidAndDistributionKeyExecutor : public fef::FeatureExecutor {
-private:
- uint32_t _distributionKey;
-
-public:
- UniqueLidAndDistributionKeyExecutor(uint32_t distributionKey)
- : _distributionKey(distributionKey)
- {
- assert( _distributionKey < 0x10000);
- }
-
- void execute(uint32_t docId) override {
- outputs().set_number(0, (uint64_t(docId) << 16u) | _distributionKey);
- }
-};
-
-}
-
-UniqueBlueprint::UniqueBlueprint() :
- Blueprint("unique"),
- _distributionKey(0)
-{
-}
-
-void
-UniqueBlueprint::visitDumpFeatures(const IIndexEnvironment &, IDumpFeatureVisitor &) const
-{
-}
-
-bool
-UniqueBlueprint::setup(const IIndexEnvironment & env, const ParameterList & )
-{
- _distributionKey = env.getDistributionKey();
- describeOutput("out", "Returns (lid << 16) | distributionKey");
- return true;
-}
-
-Blueprint::UP
-UniqueBlueprint::createInstance() const
-{
- return std::make_unique<UniqueBlueprint>();
-}
-
-FeatureExecutor &
-UniqueBlueprint::createExecutor(const IQueryEnvironment &, vespalib::Stash &stash) const
-{
- return stash.create<UniqueLidAndDistributionKeyExecutor>(_distributionKey);
-}
-
-}
diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
index fb44b986301..622e437692a 100644
--- a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
+++ b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
@@ -290,6 +290,22 @@ NearestNeighborBruteForceLimit::lookup(const Properties &props, double defaultVa
return lookupDouble(props, NAME, defaultValue);
}
+const vespalib::string GlobalFilterLimit::NAME("vespa.matching.global_filter_limit");
+
+const double GlobalFilterLimit::DEFAULT_VALUE(0.0);
+
+double
+GlobalFilterLimit::lookup(const Properties &props)
+{
+ return lookup(props, DEFAULT_VALUE);
+}
+
+double
+GlobalFilterLimit::lookup(const Properties &props, double defaultValue)
+{
+ return lookupDouble(props, NAME, defaultValue);
+}
+
} // namespace matching
namespace softtimeout {
diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.h b/searchlib/src/vespa/searchlib/fef/indexproperties.h
index 30c726caeba..1b4c2e92d8d 100644
--- a/searchlib/src/vespa/searchlib/fef/indexproperties.h
+++ b/searchlib/src/vespa/searchlib/fef/indexproperties.h
@@ -218,6 +218,19 @@ namespace matching {
static double lookup(const Properties &props);
static double lookup(const Properties &props, double defaultValue);
};
+
+ /**
+ * Property to control fallback to not building a global filter
+ * for a query with a blueprint that wants a global filter. If the
+ * estimated ratio of matching documents is less than this limit
+ * then don't build a global filter.
+ **/
+ struct GlobalFilterLimit {
+ static const vespalib::string NAME;
+ static const double DEFAULT_VALUE;
+ static double lookup(const Properties &props);
+ static double lookup(const Properties &props, double defaultValue);
+ };
}
namespace softtimeout {
diff --git a/searchlib/src/vespa/searchlib/fef/ranksetup.cpp b/searchlib/src/vespa/searchlib/fef/ranksetup.cpp
index e197f095852..249351a4fe5 100644
--- a/searchlib/src/vespa/searchlib/fef/ranksetup.cpp
+++ b/searchlib/src/vespa/searchlib/fef/ranksetup.cpp
@@ -62,7 +62,8 @@ RankSetup::RankSetup(const BlueprintFactory &factory, const IIndexEnvironment &i
_softTimeoutEnabled(false),
_softTimeoutTailCost(0.1),
_softTimeoutFactor(0.5),
- _nearest_neighbor_brute_force_limit(0.05)
+ _nearest_neighbor_brute_force_limit(0.05),
+ _global_filter_limit(0.0)
{ }
RankSetup::~RankSetup() = default;
@@ -106,6 +107,7 @@ RankSetup::configure()
setSoftTimeoutTailCost(softtimeout::TailCost::lookup(_indexEnv.getProperties()));
setSoftTimeoutFactor(softtimeout::Factor::lookup(_indexEnv.getProperties()));
set_nearest_neighbor_brute_force_limit(matching::NearestNeighborBruteForceLimit::lookup(_indexEnv.getProperties()));
+ set_global_filter_limit(matching::GlobalFilterLimit::lookup(_indexEnv.getProperties()));
}
void
diff --git a/searchlib/src/vespa/searchlib/fef/ranksetup.h b/searchlib/src/vespa/searchlib/fef/ranksetup.h
index ad793eeaceb..3e127a1e8b5 100644
--- a/searchlib/src/vespa/searchlib/fef/ranksetup.h
+++ b/searchlib/src/vespa/searchlib/fef/ranksetup.h
@@ -60,6 +60,7 @@ private:
double _softTimeoutTailCost;
double _softTimeoutFactor;
double _nearest_neighbor_brute_force_limit;
+ double _global_filter_limit;
public:
@@ -369,6 +370,9 @@ public:
void set_nearest_neighbor_brute_force_limit(double v) { _nearest_neighbor_brute_force_limit = v; }
double get_nearest_neighbor_brute_force_limit() const { return _nearest_neighbor_brute_force_limit; }
+ void set_global_filter_limit(double v) { _global_filter_limit = v; }
+ double get_global_filter_limit() const { return _global_filter_limit; }
+
/**
* This method may be used to indicate that certain features
* should be dumped during a full feature dump.
diff --git a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp
index 454db4e820a..799755042a6 100644
--- a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp
@@ -65,6 +65,12 @@ DotProductBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArray
return DotProductSearch::create(children, *tfmda[0], childMatch, _weights, std::move(md));
}
+SearchIterator::UP
+DotProductBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
+{
+ return create_or_filter(_terms, strict, constraint);
+}
+
void
DotProductBlueprint::fetchPostings(const ExecuteInfo &execInfo)
{
diff --git a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h
index 86c8e90300a..a6d33731e9c 100644
--- a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h
@@ -32,6 +32,7 @@ public:
SearchIteratorUP
createLeafSearch(const search::fef::TermFieldMatchDataArray &tfmda,
bool strict) const override;
+ SearchIteratorUP createFilterSearch(bool strict, FilterConstraint constraint) const override;
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
void fetchPostings(const ExecuteInfo &execInfo) override;
diff --git a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
index aa65342c114..3f632cfc377 100644
--- a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
@@ -434,6 +434,16 @@ WeakAndBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
return WeakAndSearch::create(terms, _n, strict);
}
+SearchIterator::UP
+WeakAndBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
+{
+ if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
+ return create_or_filter(get_children(), strict, constraint);
+ } else {
+ return std::make_unique<EmptySearch>();
+ }
+}
+
//-----------------------------------------------------------------------------
Blueprint::HitEstimate
diff --git a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
index 6bbe4562641..bc635952d55 100644
--- a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
+++ b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
@@ -92,6 +92,7 @@ public:
SearchIterator::UP
createIntermediateSearch(MultiSearch::Children subSearches,
bool strict, fef::MatchData &md) const override;
+ SearchIterator::UP createFilterSearch(bool strict, FilterConstraint constraint) const override;
WeakAndBlueprint(uint32_t n) : _n(n) {}
~WeakAndBlueprint();
diff --git a/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp
index ac4f164b09f..4de52af102b 100644
--- a/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp
@@ -3,6 +3,8 @@
#include "same_element_blueprint.h"
#include "same_element_search.h"
#include "field_spec.hpp"
+#include "andsearch.h"
+#include "emptysearch.h"
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/searchlib/attribute/searchcontextelementiterator.h>
#include <vespa/vespalib/objects/visit.hpp>
@@ -88,6 +90,24 @@ SameElementBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArra
return create_same_element_search(strict);
}
+SearchIterator::UP
+SameElementBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
+{
+ if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
+ MultiSearch::Children sub_searches;
+ sub_searches.reserve(_terms.size());
+ for (size_t i = 0; i < _terms.size(); ++i) {
+ auto search = _terms[i]->createFilterSearch(strict && (i == 0), constraint);
+ sub_searches.push_back(std::move(search));
+ }
+ UnpackInfo unpack_info;
+ auto search = AndSearch::create(std::move(sub_searches), strict, unpack_info);
+ return search;
+ } else {
+ return std::make_unique<EmptySearch>();
+ }
+}
+
void
SameElementBlueprint::visitMembers(vespalib::ObjectVisitor &visitor) const
{
diff --git a/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.h
index 8d647ac3a32..be04203fc6d 100644
--- a/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.h
@@ -40,6 +40,7 @@ public:
std::unique_ptr<SameElementSearch> create_same_element_search(bool strict) const;
SearchIteratorUP createLeafSearch(const search::fef::TermFieldMatchDataArray &tfmda,
bool strict) const override;
+ SearchIteratorUP createFilterSearch(bool strict, FilterConstraint constraint) const override;
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
const std::vector<Blueprint::UP> &terms() const { return _terms; }
const vespalib::string &field_name() const { return _field_name; }
diff --git a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp
index d4f16e2f91c..4e83c45cbc0 100644
--- a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp
@@ -3,6 +3,7 @@
#include "parallel_weak_and_blueprint.h"
#include "parallel_weak_and_search.h"
#include <vespa/searchlib/queryeval/field_spec.hpp>
+#include <vespa/searchlib/queryeval/emptysearch.h>
#include <vespa/searchlib/queryeval/searchiterator.h>
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/vespalib/objects/visit.hpp>
@@ -102,6 +103,16 @@ ParallelWeakAndBlueprint::createLeafSearch(const search::fef::TermFieldMatchData
std::move(childrenMatchData)), strict));
}
+std::unique_ptr<SearchIterator>
+ParallelWeakAndBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
+{
+ if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
+ return create_or_filter(_terms, strict, constraint);
+ } else {
+ return std::make_unique<EmptySearch>();
+ }
+}
+
void
ParallelWeakAndBlueprint::fetchPostings(const ExecuteInfo & execInfo)
{
diff --git a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h
index 842067f9849..5b034b6de0a 100644
--- a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h
@@ -59,6 +59,7 @@ public:
void addTerm(Blueprint::UP term, int32_t weight);
SearchIterator::UP createLeafSearch(const fef::TermFieldMatchDataArray &tfmda, bool strict) const override;
+ std::unique_ptr<SearchIterator> createFilterSearch(bool strict, FilterConstraint constraint) const override;
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
void fetchPostings(const ExecuteInfo &execInfo) override;
bool always_needs_unpack() const override;
diff --git a/searchlib/src/vespa/searchlib/test/CMakeLists.txt b/searchlib/src/vespa/searchlib/test/CMakeLists.txt
index 41084148c87..4d722e687ef 100644
--- a/searchlib/src/vespa/searchlib/test/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/test/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(searchlib_test
SOURCES
document_weight_attribute_helper.cpp
@@ -12,7 +13,17 @@ vespa_add_library(searchlib_test
statestring.cpp
$<TARGET_OBJECTS:searchlib_test_fakedata>
$<TARGET_OBJECTS:searchlib_searchlib_test_diskindex>
+ $<TARGET_OBJECTS:searchlib_test_gtest_migration>
DEPENDS
searchlib
searchlib_searchlib_test_memoryindex
+ GTest::GTest
)
+
+vespa_add_library(searchlib_test_gtest_migration OBJECT
+ SOURCES
+ initrange.cpp
+ searchiteratorverifier.cpp
+)
+
+target_compile_definitions(searchlib_test_gtest_migration PRIVATE ENABLE_GTEST_MIGRATION)
diff --git a/searchlib/src/vespa/searchlib/test/initrange.cpp b/searchlib/src/vespa/searchlib/test/initrange.cpp
index 2292a8e775e..1e23f7c5b8c 100644
--- a/searchlib/src/vespa/searchlib/test/initrange.cpp
+++ b/searchlib/src/vespa/searchlib/test/initrange.cpp
@@ -1,6 +1,12 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "initrange.h"
+#ifdef ENABLE_GTEST_MIGRATION
+#include <vespa/vespalib/gtest/gtest.h>
+#define ASSERT_EQUAL ASSERT_EQ
+#define EXPECT_EQUAL EXPECT_EQ
+#else
#include <vespa/vespalib/testkit/test_kit.h>
+#endif
#include <vespa/searchlib/queryeval/emptysearch.h>
#include <vespa/searchlib/queryeval/truesearch.h>
#include <algorithm>
diff --git a/searchlib/src/vespa/searchlib/test/initrange.h b/searchlib/src/vespa/searchlib/test/initrange.h
index a143dfdb119..4fbb851779a 100644
--- a/searchlib/src/vespa/searchlib/test/initrange.h
+++ b/searchlib/src/vespa/searchlib/test/initrange.h
@@ -7,6 +7,10 @@
namespace search::test {
+#ifdef ENABLE_GTEST_MIGRATION
+#define InitRangeVerifier InitRangeVerifierForGTest
+#endif
+
class InitRangeVerifier {
public:
typedef queryeval::SearchIterator SearchIterator;
diff --git a/searchlib/src/vespa/searchlib/test/searchiteratorverifier.cpp b/searchlib/src/vespa/searchlib/test/searchiteratorverifier.cpp
index ec53d6d9d00..276f8fbc08d 100644
--- a/searchlib/src/vespa/searchlib/test/searchiteratorverifier.cpp
+++ b/searchlib/src/vespa/searchlib/test/searchiteratorverifier.cpp
@@ -2,7 +2,14 @@
#include "searchiteratorverifier.h"
#include "initrange.h"
+#ifdef ENABLE_GTEST_MIGRATION
+#include <vespa/vespalib/gtest/gtest.h>
+#define TEST_DO(x) x
+#define EXPECT_EQUAL EXPECT_EQ
+#define ASSERT_EQUAL ASSERT_EQ
+#else
#include <vespa/vespalib/testkit/test_kit.h>
+#endif
#include <vespa/searchlib/queryeval/emptysearch.h>
#include <vespa/searchlib/queryeval/truesearch.h>
#include <vespa/searchlib/queryeval/termwise_search.h>
diff --git a/searchlib/src/vespa/searchlib/test/searchiteratorverifier.h b/searchlib/src/vespa/searchlib/test/searchiteratorverifier.h
index 3d35731dab1..afeb46f0c16 100644
--- a/searchlib/src/vespa/searchlib/test/searchiteratorverifier.h
+++ b/searchlib/src/vespa/searchlib/test/searchiteratorverifier.h
@@ -7,6 +7,10 @@
namespace search::test {
+#ifdef ENABLE_GTEST_MIGRATION
+#define SearchIteratorVerifier SearchIteratorVerifierForGTest
+#endif
+
class SearchIteratorVerifier {
public:
typedef queryeval::SearchIterator SearchIterator;
diff --git a/searchlib/src/vespa/searchlib/uca/CMakeLists.txt b/searchlib/src/vespa/searchlib/uca/CMakeLists.txt
index a04add4bfee..c3f469e59ac 100644
--- a/searchlib/src/vespa/searchlib/uca/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/uca/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(ICU 60.0 REQUIRED COMPONENTS uc i18n)
vespa_add_library(searchlib_searchlib_uca
SOURCES
ucaconverter.cpp
@@ -6,6 +7,6 @@ vespa_add_library(searchlib_searchlib_uca
INSTALL lib64
DEPENDS
searchlib
- icui18n
- icuuc
+ ICU::i18n
+ ICU::uc
)
diff --git a/simplemetrics/pom.xml b/simplemetrics/pom.xml
index f65aeaabef1..6ca02febefd 100644
--- a/simplemetrics/pom.xml
+++ b/simplemetrics/pom.xml
@@ -25,6 +25,12 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>component</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>vespajlib</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
diff --git a/storage/src/tests/bucketdb/CMakeLists.txt b/storage/src/tests/bucketdb/CMakeLists.txt
index 2468e587aff..5dc269e54f9 100644
--- a/storage/src/tests/bucketdb/CMakeLists.txt
+++ b/storage/src/tests/bucketdb/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storage_bucketdb_gtest_runner_app TEST
SOURCES
bucketinfotest.cpp
@@ -12,7 +13,7 @@ vespa_add_executable(storage_bucketdb_gtest_runner_app TEST
DEPENDS
storage
storage_testcommon
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/bucketdb/initializertest.cpp b/storage/src/tests/bucketdb/initializertest.cpp
index 63f990f7cc1..7d3d2c185da 100644
--- a/storage/src/tests/bucketdb/initializertest.cpp
+++ b/storage/src/tests/bucketdb/initializertest.cpp
@@ -139,11 +139,11 @@ typedef std::map<document::BucketId, BucketData> DiskData;
struct BucketInfoLogger {
std::map<PartitionId, DiskData>& map;
- BucketInfoLogger(std::map<PartitionId, DiskData>& m)
+ explicit BucketInfoLogger(std::map<PartitionId, DiskData>& m)
: map(m) {}
StorBucketDatabase::Decision operator()(
- uint64_t revBucket, StorBucketDatabase::Entry& entry)
+ uint64_t revBucket, const StorBucketDatabase::Entry& entry)
{
document::BucketId bucket(
document::BucketId::keyToBucketId(revBucket));
@@ -152,14 +152,14 @@ struct BucketInfoLogger {
DiskData& ddata(map[entry.disk]);
BucketData& bdata(ddata[bucket]);
bdata.info = entry.getBucketInfo();
- return StorBucketDatabase::CONTINUE;
+ return StorBucketDatabase::Decision::CONTINUE;
}
};
std::map<PartitionId, DiskData>
createMapFromBucketDatabase(StorBucketDatabase& db) {
std::map<PartitionId, DiskData> result;
BucketInfoLogger infoLogger(result);
- db.all(infoLogger, "createmap");
+ db.for_each(std::ref(infoLogger), "createmap");
return result;
}
// Create data we want to have in this test
@@ -551,8 +551,8 @@ struct DatabaseInsertCallback : MessageCallback
BucketData d;
StorBucketDatabase::WrappedEntry entry(
_database.get(bid, "DatabaseInsertCallback::onMessage",
- StorBucketDatabase::LOCK_IF_NONEXISTING_AND_NOT_CREATING));
- if (entry.exist()) {
+ StorBucketDatabase::CREATE_IF_NONEXISTING));
+ if (entry.preExisted()) {
_errors << "db entry for " << bid << " already existed";
}
if (i < 5) {
diff --git a/storage/src/tests/bucketdb/lockablemaptest.cpp b/storage/src/tests/bucketdb/lockablemaptest.cpp
index 101b9d014fa..c5d22b6e6b5 100644
--- a/storage/src/tests/bucketdb/lockablemaptest.cpp
+++ b/storage/src/tests/bucketdb/lockablemaptest.cpp
@@ -4,8 +4,8 @@
#include <vespa/storage/bucketdb/judymultimap.h>
#include <vespa/storage/bucketdb/judymultimap.hpp>
#include <vespa/storage/bucketdb/lockablemap.hpp>
+#include <vespa/storage/bucketdb/btree_lockable_map.hpp>
#include <vespa/vespalib/gtest/gtest.h>
-#include <boost/operators.hpp>
#include <vespa/log/log.h>
LOG_SETUP(".lockable_map_test");
@@ -13,41 +13,62 @@ LOG_SETUP(".lockable_map_test");
// FIXME these old tests may have the least obvious semantics and worst naming in the entire storage module
using namespace ::testing;
+using document::BucketId;
namespace storage {
namespace {
- struct A : public boost::operators<A> {
- int _val1;
- int _val2;
- int _val3;
- A() : _val1(0), _val2(0), _val3(0) {}
- A(int val1, int val2, int val3)
- : _val1(val1), _val2(val2), _val3(val3) {}
+struct A {
+ int _val1;
+ int _val2;
+ int _val3;
- static bool mayContain(const A&) { return true; }
+ A() : _val1(0), _val2(0), _val3(0) {}
+ A(int val1, int val2, int val3)
+ : _val1(val1), _val2(val2), _val3(val3) {}
- bool operator==(const A& a) const {
- return (_val1 == a._val1 && _val2 == a._val2 && _val3 == a._val3);
- }
- bool operator<(const A& a) const {
- if (_val1 != a._val1) return (_val1 < a._val1);
- if (_val2 != a._val2) return (_val2 < a._val2);
- return (_val3 < a._val3);
- }
- };
+ static bool mayContain(const A&) { return true; }
+ // Make this type smell more like a proper bucket DB value type.
+ constexpr bool verifyLegal() const noexcept { return true; }
+ constexpr bool valid() const noexcept { return true; }
- std::ostream& operator<<(std::ostream& out, const A& a) {
- return out << "A(" << a._val1 << ", " << a._val2 << ", " << a._val3 << ")";
+ bool operator==(const A& a) const noexcept {
+ return (_val1 == a._val1 && _val2 == a._val2 && _val3 == a._val3);
+ }
+ bool operator!=(const A& a) const noexcept {
+ return !(*this == a);
}
+ bool operator<(const A& a) const noexcept {
+ if (_val1 != a._val1) return (_val1 < a._val1);
+ if (_val2 != a._val2) return (_val2 < a._val2);
+ return (_val3 < a._val3);
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const A& a) {
+ return out << "A(" << a._val1 << ", " << a._val2 << ", " << a._val3 << ")";
+}
- typedef LockableMap<JudyMultiMap<A> > Map;
}
-TEST(LockableMapTest, simple_usage) {
+template <typename MapT>
+struct LockableMapTest : ::testing::Test {
+ using Map = MapT;
+};
+
+using MapTypes = ::testing::Types<LockableMap<JudyMultiMap<A>>, bucketdb::BTreeLockableMap<A>>;
+VESPA_GTEST_TYPED_TEST_SUITE(LockableMapTest, MapTypes);
+
+// Disable warnings emitted by gtest generated files when using typed tests
+#pragma GCC diagnostic push
+#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wsuggest-override"
+#endif
+
+TYPED_TEST(LockableMapTest, simple_usage) {
// Tests insert, erase, size, empty, operator[]
- Map map;
+ TypeParam map;
// Do some insertions
EXPECT_TRUE(map.empty());
bool preExisted;
@@ -57,11 +78,11 @@ TEST(LockableMapTest, simple_usage) {
EXPECT_EQ(false, preExisted);
map.insert(14, A(42, 0, 0), "foo", preExisted);
EXPECT_EQ(false, preExisted);
- EXPECT_EQ((Map::size_type) 3, map.size()) << map.toString();
+ EXPECT_EQ(map.size(), 3);
map.insert(11, A(4, 7, 0), "foo", preExisted);
EXPECT_EQ(true, preExisted);
- EXPECT_EQ((Map::size_type) 3, map.size());
+ EXPECT_EQ(map.size(), 3);
EXPECT_FALSE(map.empty());
// Access some elements
@@ -71,20 +92,20 @@ TEST(LockableMapTest, simple_usage) {
// Do removes
EXPECT_EQ(map.erase(12, "foo"), 0);
- EXPECT_EQ((Map::size_type) 3, map.size());
+ EXPECT_EQ(map.size(), 3);
EXPECT_EQ(map.erase(14, "foo"), 1);
- EXPECT_EQ((Map::size_type) 2, map.size());
+ EXPECT_EQ(map.size(), 2);
EXPECT_EQ(map.erase(11, "foo"), 1);
EXPECT_EQ(map.erase(16, "foo"), 1);
- EXPECT_EQ((Map::size_type) 0, map.size());
+ EXPECT_EQ(map.size(), 0);
EXPECT_TRUE(map.empty());
}
-TEST(LockableMapTest, comparison) {
- Map map1;
- Map map2;
+TYPED_TEST(LockableMapTest, comparison) {
+ TypeParam map1;
+ TypeParam map2;
bool preExisted;
// Check empty state is correct
@@ -123,154 +144,194 @@ TEST(LockableMapTest, comparison) {
}
namespace {
- struct NonConstProcessor {
- Map::Decision operator()(int key, A& a) {
- (void) key;
- ++a._val2;
- return Map::UPDATE;
+
+template <typename Map>
+struct NonConstProcessor {
+ typename Map::Decision operator()(int key, A& a) {
+ (void) key;
+ ++a._val2;
+ return Map::UPDATE;
+ }
+};
+
+template <typename Map>
+struct EntryProcessor {
+ mutable uint32_t count;
+ mutable std::vector<std::string> log;
+ mutable std::vector<typename Map::Decision> behaviour;
+
+ EntryProcessor();
+ explicit EntryProcessor(const std::vector<typename Map::Decision>& decisions);
+ ~EntryProcessor();
+
+ typename Map::Decision operator()(uint64_t key, A& a) const {
+ std::ostringstream ost;
+ ost << key << " - " << a;
+ log.push_back(ost.str());
+ typename Map::Decision d = Map::CONTINUE;
+ if (behaviour.size() > count) {
+ d = behaviour[count++];
}
- };
- struct EntryProcessor {
- mutable uint32_t count;
- mutable std::vector<std::string> log;
- mutable std::vector<Map::Decision> behaviour;
-
- EntryProcessor();
- EntryProcessor(const std::vector<Map::Decision>& decisions);
- ~EntryProcessor();
-
- Map::Decision operator()(uint64_t key, A& a) const {
- std::ostringstream ost;
- ost << key << " - " << a;
- log.push_back(ost.str());
- Map::Decision d = Map::CONTINUE;
- if (behaviour.size() > count) {
- d = behaviour[count++];
- }
- if (d == Map::UPDATE) {
- ++a._val3;
- }
- return d;
+ if (d == Map::UPDATE) {
+ ++a._val3;
}
+ return d;
+ }
- std::string toString() {
- std::ostringstream ost;
- for (uint32_t i=0; i<log.size(); ++i) {
- ost << log[i] << "\n";
- }
- return ost.str();
+ std::string toString() {
+ std::ostringstream ost;
+ for (uint32_t i=0; i<log.size(); ++i) {
+ ost << log[i] << "\n";
}
- };
-}
+ return ost.str();
+ }
+};
+
+template <typename Map>
+EntryProcessor<Map>::EntryProcessor()
+ : count(0), log(), behaviour() {}
+
+template <typename Map>
+EntryProcessor<Map>::EntryProcessor(const std::vector<typename Map::Decision>& decisions)
+ : count(0), log(), behaviour(decisions) {}
+
+template <typename Map>
+EntryProcessor<Map>::~EntryProcessor() = default;
+
+template <typename Map>
+struct ConstProcessor {
+ mutable std::vector<std::string> log;
+
+ typename Map::Decision operator()(uint64_t key, const A& a) const {
+ std::ostringstream ost;
+ ost << key << " - " << a;
+ log.push_back(ost.str());
+ return Map::CONTINUE;
+ }
-EntryProcessor::EntryProcessor() : count(0), log(), behaviour() {}
-EntryProcessor::EntryProcessor(const std::vector<Map::Decision>& decisions)
- : count(0), log(), behaviour(decisions) {}
-EntryProcessor::~EntryProcessor() = default;
+ std::string toString() {
+ std::ostringstream ost;
+ for (const auto& entry : log) {
+ ost << entry << "\n";
+ }
+ return ost.str();
+ }
+};
+
+}
-TEST(LockableMapTest, iterating) {
- Map map;
+TYPED_TEST(LockableMapTest, iterating) {
+ TypeParam map;
bool preExisted;
map.insert(16, A(1, 2, 3), "foo", preExisted);
map.insert(11, A(4, 6, 0), "foo", preExisted);
map.insert(14, A(42, 0, 0), "foo", preExisted);
- // Test that we can use functor with non-const function
+ // Test that we can use functor with non-const function
{
- NonConstProcessor ncproc;
- map.each(ncproc, "foo"); // Locking both for each element
+ NonConstProcessor<TypeParam> ncproc;
+ map.for_each_mutable(std::ref(ncproc), "foo"); // First round of mutating functor for `all`
EXPECT_EQ(A(4, 7, 0), *map.get(11, "foo"));
EXPECT_EQ(A(42,1, 0), *map.get(14, "foo"));
EXPECT_EQ(A(1, 3, 3), *map.get(16, "foo"));
- map.all(ncproc, "foo"); // And for all
+ map.for_each_mutable(std::ref(ncproc), "foo"); // Once more, with feeling.
EXPECT_EQ(A(4, 8, 0), *map.get(11, "foo"));
EXPECT_EQ(A(42,2, 0), *map.get(14, "foo"));
EXPECT_EQ(A(1, 4, 3), *map.get(16, "foo"));
}
- // Test that we can use const functors directly..
- map.each(EntryProcessor(), "foo");
- // Test iterator bounds
{
- EntryProcessor proc;
- map.each(proc, "foo", 11, 16);
+ ConstProcessor<TypeParam> cproc;
+ map.for_each(std::ref(cproc), "foo");
+ std::string expected("11 - A(4, 8, 0)\n"
+ "14 - A(42, 2, 0)\n"
+ "16 - A(1, 4, 3)\n");
+ EXPECT_EQ(expected, cproc.toString());
+ }
+ // Test that we can use const functors directly..
+ map.for_each(ConstProcessor<TypeParam>(), "foo");
+
+ // Test iterator bounds
+ {
+ EntryProcessor<TypeParam> proc;
+ map.for_each_mutable(std::ref(proc), "foo", 11, 16);
std::string expected("11 - A(4, 8, 0)\n"
"14 - A(42, 2, 0)\n"
"16 - A(1, 4, 3)\n");
EXPECT_EQ(expected, proc.toString());
- EntryProcessor proc2;
- map.each(proc2, "foo", 12, 15);
+ EntryProcessor<TypeParam> proc2;
+ map.for_each_mutable(std::ref(proc2), "foo", 12, 15);
expected = "14 - A(42, 2, 0)\n";
EXPECT_EQ(expected, proc2.toString());
}
// Test that we can abort iterating
{
- std::vector<Map::Decision> decisions;
- decisions.push_back(Map::CONTINUE);
- decisions.push_back(Map::ABORT);
- EntryProcessor proc(decisions);
- map.each(proc, "foo");
+ std::vector<typename TypeParam::Decision> decisions;
+ decisions.push_back(TypeParam::CONTINUE);
+ decisions.push_back(TypeParam::ABORT);
+ EntryProcessor<TypeParam> proc(decisions);
+ map.for_each_mutable(std::ref(proc), "foo");
std::string expected("11 - A(4, 8, 0)\n"
"14 - A(42, 2, 0)\n");
EXPECT_EQ(expected, proc.toString());
}
- // Test that we can remove during iteration
+ // Test that we can remove during iteration
{
- std::vector<Map::Decision> decisions;
- decisions.push_back(Map::CONTINUE);
- decisions.push_back(Map::REMOVE);
- EntryProcessor proc(decisions);
- map.each(proc, "foo");
+ std::vector<typename TypeParam::Decision> decisions;
+ decisions.push_back(TypeParam::CONTINUE);
+ decisions.push_back(TypeParam::REMOVE); // TODO consider removing; not used
+ EntryProcessor<TypeParam> proc(decisions);
+ map.for_each_mutable(std::ref(proc), "foo");
std::string expected("11 - A(4, 8, 0)\n"
"14 - A(42, 2, 0)\n"
"16 - A(1, 4, 3)\n");
EXPECT_EQ(expected, proc.toString());
- EXPECT_EQ((Map::size_type) 2, map.size()) << map.toString();
+ EXPECT_EQ(2u, map.size());
EXPECT_EQ(A(4, 8, 0), *map.get(11, "foo"));
EXPECT_EQ(A(1, 4, 3), *map.get(16, "foo"));
- Map::WrappedEntry entry = map.get(14, "foo");
+ auto entry = map.get(14, "foo");
EXPECT_FALSE(entry.exist());
}
}
-TEST(LockableMapTest, chunked_iteration_is_transparent_across_chunk_sizes) {
- Map map;
+TYPED_TEST(LockableMapTest, chunked_iteration_is_transparent_across_chunk_sizes) {
+ TypeParam map;
bool preExisted;
map.insert(16, A(1, 2, 3), "foo", preExisted);
map.insert(11, A(4, 6, 0), "foo", preExisted);
map.insert(14, A(42, 0, 0), "foo", preExisted);
- NonConstProcessor ncproc; // Increments 2nd value in all entries.
- // chunkedAll with chunk size of 1
- map.chunkedAll(ncproc, "foo", 1us, 1);
+ NonConstProcessor<TypeParam> ncproc; // Increments 2nd value in all entries.
+ // for_each_chunked with chunk size of 1
+ map.for_each_chunked(std::ref(ncproc), "foo", 1us, 1);
EXPECT_EQ(A(4, 7, 0), *map.get(11, "foo"));
EXPECT_EQ(A(42, 1, 0), *map.get(14, "foo"));
EXPECT_EQ(A(1, 3, 3), *map.get(16, "foo"));
- // chunkedAll with chunk size larger than db size
- map.chunkedAll(ncproc, "foo", 1us, 100);
+ // for_each_chunked with chunk size larger than db size
+ map.for_each_chunked(std::ref(ncproc), "foo", 1us, 100);
EXPECT_EQ(A(4, 8, 0), *map.get(11, "foo"));
EXPECT_EQ(A(42, 2, 0), *map.get(14, "foo"));
EXPECT_EQ(A(1, 4, 3), *map.get(16, "foo"));
}
-TEST(LockableMapTest, can_abort_during_chunked_iteration) {
- Map map;
+TYPED_TEST(LockableMapTest, can_abort_during_chunked_iteration) {
+ TypeParam map;
bool preExisted;
map.insert(16, A(1, 2, 3), "foo", preExisted);
map.insert(11, A(4, 6, 0), "foo", preExisted);
map.insert(14, A(42, 0, 0), "foo", preExisted);
- std::vector<Map::Decision> decisions;
- decisions.push_back(Map::CONTINUE);
- decisions.push_back(Map::ABORT);
- EntryProcessor proc(decisions);
- map.chunkedAll(proc, "foo", 1us, 100);
+ std::vector<typename TypeParam::Decision> decisions;
+ decisions.push_back(TypeParam::CONTINUE);
+ decisions.push_back(TypeParam::ABORT);
+ EntryProcessor<TypeParam> proc(decisions);
+ map.for_each_chunked(std::ref(proc), "foo", 1us, 100);
std::string expected("11 - A(4, 6, 0)\n"
"14 - A(42, 0, 0)\n");
EXPECT_EQ(expected, proc.toString());
}
-TEST(LockableMapTest, find_buckets_simple) {
- Map map;
+TYPED_TEST(LockableMapTest, find_buckets_simple) {
+ TypeParam map;
document::BucketId id1(17, 0x0ffff);
id1 = id1.stripUnused();
@@ -293,8 +354,8 @@ TEST(LockableMapTest, find_buckets_simple) {
EXPECT_EQ(A(3,4,5), *results[id3]);
}
-TEST(LockableMapTest, find_buckets) {
- Map map;
+TYPED_TEST(LockableMapTest, find_buckets) {
+ TypeParam map;
document::BucketId id1(16, 0x0ffff);
document::BucketId id2(17, 0x0ffff);
@@ -317,8 +378,8 @@ TEST(LockableMapTest, find_buckets) {
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]);
}
-TEST(LockableMapTest, find_buckets_2) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_buckets_2) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(16, 0x0ffff);
document::BucketId id2(17, 0x0ffff);
@@ -341,8 +402,8 @@ TEST(LockableMapTest, find_buckets_2) { // ticket 3121525
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]);
}
-TEST(LockableMapTest, find_buckets_3) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_buckets_3) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(16, 0x0ffff);
document::BucketId id2(17, 0x0ffff);
@@ -359,8 +420,8 @@ TEST(LockableMapTest, find_buckets_3) { // ticket 3121525
EXPECT_EQ(A(1,2,3), *results[id1.stripUnused()]);
}
-TEST(LockableMapTest, find_buckets_4) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_buckets_4) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(16, 0x0ffff);
document::BucketId id2(17, 0x0ffff);
@@ -379,8 +440,8 @@ TEST(LockableMapTest, find_buckets_4) { // ticket 3121525
EXPECT_EQ(A(1,2,3), *results[id1.stripUnused()]);
}
-TEST(LockableMapTest, find_buckets_5) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_buckets_5) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(16, 0x0ffff);
document::BucketId id2(17, 0x0ffff);
@@ -399,8 +460,8 @@ TEST(LockableMapTest, find_buckets_5) { // ticket 3121525
EXPECT_EQ(A(1,2,3), *results[id1.stripUnused()]);
}
-TEST(LockableMapTest, find_no_buckets) {
- Map map;
+TYPED_TEST(LockableMapTest, find_no_buckets) {
+ TypeParam map;
document::BucketId id(16, 0x0ffff);
auto results = map.getAll(id, "foo");
@@ -408,8 +469,8 @@ TEST(LockableMapTest, find_no_buckets) {
EXPECT_EQ(0, results.size());
}
-TEST(LockableMapTest, find_all) {
- Map map;
+TYPED_TEST(LockableMapTest, find_all) {
+ TypeParam map;
document::BucketId id1(16, 0x0aaaa); // contains id2-id7
document::BucketId id2(17, 0x0aaaa); // contains id3-id4
@@ -450,8 +511,8 @@ TEST(LockableMapTest, find_all) {
EXPECT_EQ(A(9,10,11), *results[id9.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, find_all_2) { // Ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_all_2) { // Ticket 3121525
+ TypeParam map;
document::BucketId id1(17, 0x00001);
document::BucketId id2(17, 0x10001);
@@ -469,8 +530,8 @@ TEST(LockableMapTest, find_all_2) { // Ticket 3121525
EXPECT_EQ(A(2,3,4), *results[id2.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, find_all_unused_bit_is_set) { // ticket 2938896
- Map map;
+TYPED_TEST(LockableMapTest, find_all_unused_bit_is_set) { // ticket 2938896
+ TypeParam map;
document::BucketId id1(24, 0x000dc7089);
document::BucketId id2(33, 0x0053c7089);
@@ -493,8 +554,8 @@ TEST(LockableMapTest, find_all_unused_bit_is_set) { // ticket 2938896
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, find_all_inconsistently_split) { // Ticket 2938896
- Map map;
+TYPED_TEST(LockableMapTest, find_all_inconsistently_split) { // Ticket 2938896
+ TypeParam map;
document::BucketId id1(16, 0x00001); // contains id2-id3
document::BucketId id2(17, 0x00001);
@@ -515,8 +576,8 @@ TEST(LockableMapTest, find_all_inconsistently_split) { // Ticket 2938896
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, find_all_inconsistently_split_2) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_all_inconsistently_split_2) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(17, 0x10000);
document::BucketId id2(27, 0x007228034); // contains id3
@@ -538,8 +599,8 @@ TEST(LockableMapTest, find_all_inconsistently_split_2) { // ticket 3121525
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]); // most specific match (super bucket)
}
-TEST(LockableMapTest, find_all_inconsistently_split_3) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_all_inconsistently_split_3) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(16, 0x0ffff); // contains id2
document::BucketId id2(17, 0x0ffff);
@@ -556,8 +617,8 @@ TEST(LockableMapTest, find_all_inconsistently_split_3) { // ticket 3121525
EXPECT_EQ(A(1,2,3), *results[id1.stripUnused()]); // super bucket
}
-TEST(LockableMapTest, find_all_inconsistently_split_4) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_all_inconsistently_split_4) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(16, 0x0ffff); // contains id2-id3
document::BucketId id2(17, 0x0ffff);
@@ -577,8 +638,8 @@ TEST(LockableMapTest, find_all_inconsistently_split_4) { // ticket 3121525
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, find_all_inconsistently_split_5) { // ticket 3121525
- Map map;
+TYPED_TEST(LockableMapTest, find_all_inconsistently_split_5) { // ticket 3121525
+ TypeParam map;
document::BucketId id1(16, 0x0ffff); // contains id2-id3
document::BucketId id2(17, 0x0ffff);
@@ -598,8 +659,8 @@ TEST(LockableMapTest, find_all_inconsistently_split_5) { // ticket 3121525
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, find_all_inconsistently_split_6) {
- Map map;
+TYPED_TEST(LockableMapTest, find_all_inconsistently_split_6) {
+ TypeParam map;
document::BucketId id1(16, 0x0ffff); // contains id2-id3
document::BucketId id2(18, 0x1ffff);
@@ -619,8 +680,8 @@ TEST(LockableMapTest, find_all_inconsistently_split_6) {
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, find_all_inconsistent_below_16_bits) {
- Map map;
+TYPED_TEST(LockableMapTest, find_all_inconsistent_below_16_bits) {
+ TypeParam map;
document::BucketId id1(1, 0x1); // contains id2-id3
document::BucketId id2(3, 0x1);
@@ -641,24 +702,64 @@ TEST(LockableMapTest, find_all_inconsistent_below_16_bits) {
EXPECT_EQ(A(3,4,5), *results[id3.stripUnused()]); // sub bucket
}
-TEST(LockableMapTest, is_consistent) {
- Map map;
+TYPED_TEST(LockableMapTest, is_consistent) {
+ TypeParam map;
document::BucketId id1(16, 0x00001); // contains id2-id3
document::BucketId id2(17, 0x00001);
bool preExisted;
map.insert(id1.stripUnused().toKey(), A(1,2,3), "foo", preExisted);
{
- Map::WrappedEntry entry(
- map.get(id1.stripUnused().toKey(), "foo", true));
+ auto entry = map.get(id1.stripUnused().toKey(), "foo", true);
EXPECT_TRUE(map.isConsistent(entry));
}
map.insert(id2.stripUnused().toKey(), A(1,2,3), "foo", preExisted);
{
- Map::WrappedEntry entry(
- map.get(id1.stripUnused().toKey(), "foo", true));
+ auto entry = map.get(id1.stripUnused().toKey(), "foo", true);
EXPECT_FALSE(map.isConsistent(entry));
}
}
+TYPED_TEST(LockableMapTest, get_without_auto_create_does_not_implicitly_lock_bucket) {
+ TypeParam map;
+ BucketId id(16, 0x00001);
+
+ auto entry = map.get(id.toKey(), "foo", false);
+ EXPECT_FALSE(entry.exist());
+ EXPECT_FALSE(entry.preExisted());
+ EXPECT_FALSE(entry.locked());
+}
+
+TYPED_TEST(LockableMapTest, get_with_auto_create_returns_default_constructed_entry_if_missing) {
+ TypeParam map;
+ BucketId id(16, 0x00001);
+
+ auto entry = map.get(id.toKey(), "foo", true);
+ EXPECT_TRUE(entry.exist());
+ EXPECT_FALSE(entry.preExisted());
+ EXPECT_TRUE(entry.locked());
+ EXPECT_EQ(*entry, A());
+ *entry = A(1, 2, 3);
+ entry.write(); // Implicit unlock (!)
+
+ // Should now exist
+ entry = map.get(id.toKey(), "foo", true);
+ EXPECT_TRUE(entry.exist());
+ EXPECT_TRUE(entry.preExisted());
+ EXPECT_TRUE(entry.locked());
+ EXPECT_EQ(*entry, A(1, 2, 3));
+}
+
+TYPED_TEST(LockableMapTest, entry_changes_not_visible_if_write_not_invoked_on_guard) {
+ TypeParam map;
+ BucketId id(16, 0x00001);
+ auto entry = map.get(id.toKey(), "foo", true);
+ *entry = A(1, 2, 3);
+ // No write() call on guard
+ entry.unlock();
+
+ entry = map.get(id.toKey(), "foo", true);
+ EXPECT_EQ(*entry, A());
+}
+
} // storage
diff --git a/storage/src/tests/common/CMakeLists.txt b/storage/src/tests/common/CMakeLists.txt
index 1922d13ca61..5d5b04d8095 100644
--- a/storage/src/tests/common/CMakeLists.txt
+++ b/storage/src/tests/common/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storage_testcommon TEST
SOURCES
dummystoragelink.cpp
@@ -19,7 +20,7 @@ vespa_add_executable(storage_common_gtest_runner_app TEST
DEPENDS
storage_testcommon
storage
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/common/hostreporter/CMakeLists.txt b/storage/src/tests/common/hostreporter/CMakeLists.txt
index 2fcb159cb08..4328b8156e6 100644
--- a/storage/src/tests/common/hostreporter/CMakeLists.txt
+++ b/storage/src/tests/common/hostreporter/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storage_testhostreporter TEST
SOURCES
util.cpp
@@ -14,7 +15,7 @@ vespa_add_executable(storage_hostreporter_gtest_runner_app TEST
DEPENDS
storage
storage_testhostreporter
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/common/teststorageapp.cpp b/storage/src/tests/common/teststorageapp.cpp
index 082af954871..9fcf1049e1b 100644
--- a/storage/src/tests/common/teststorageapp.cpp
+++ b/storage/src/tests/common/teststorageapp.cpp
@@ -140,10 +140,8 @@ namespace {
}
TestServiceLayerApp::TestServiceLayerApp(vespalib::stringref configId)
- : TestStorageApp(
- StorageComponentRegisterImpl::UP(
- new ServiceLayerComponentRegisterImpl),
- lib::NodeType::STORAGE, getIndexFromConfig(configId), configId),
+ : TestStorageApp(std::make_unique<ServiceLayerComponentRegisterImpl>(true), // TODO remove B-tree flag once default
+ lib::NodeType::STORAGE, getIndexFromConfig(configId), configId),
_compReg(dynamic_cast<ServiceLayerComponentRegisterImpl&>(
TestStorageApp::getComponentRegister())),
_persistenceProvider(),
@@ -157,10 +155,8 @@ TestServiceLayerApp::TestServiceLayerApp(vespalib::stringref configId)
TestServiceLayerApp::TestServiceLayerApp(DiskCount dc, NodeIndex index,
vespalib::stringref configId)
- : TestStorageApp(
- StorageComponentRegisterImpl::UP(
- new ServiceLayerComponentRegisterImpl),
- lib::NodeType::STORAGE, index, configId),
+ : TestStorageApp(std::make_unique<ServiceLayerComponentRegisterImpl>(true), // TODO remove B-tree flag once default
+ lib::NodeType::STORAGE, index, configId),
_compReg(dynamic_cast<ServiceLayerComponentRegisterImpl&>(
TestStorageApp::getComponentRegister())),
_persistenceProvider(),
diff --git a/storage/src/tests/distributor/CMakeLists.txt b/storage/src/tests/distributor/CMakeLists.txt
index 3148540d86d..1403021a9c3 100644
--- a/storage/src/tests/distributor/CMakeLists.txt
+++ b/storage/src/tests/distributor/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storage_distributor_gtest_runner_app TEST
SOURCES
blockingoperationstartertest.cpp
@@ -49,7 +50,7 @@ vespa_add_executable(storage_distributor_gtest_runner_app TEST
storage_testcommon
storage_testhostreporter
storage_distributor
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/frameworkimpl/status/CMakeLists.txt b/storage/src/tests/frameworkimpl/status/CMakeLists.txt
index 1b49b1bac45..cf2ee5fd51b 100644
--- a/storage/src/tests/frameworkimpl/status/CMakeLists.txt
+++ b/storage/src/tests/frameworkimpl/status/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storage_status_gtest_runner_app TEST
SOURCES
gtest_runner.cpp
@@ -8,7 +9,7 @@ vespa_add_executable(storage_status_gtest_runner_app TEST
DEPENDS
storage
storage_testcommon
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/persistence/CMakeLists.txt b/storage/src/tests/persistence/CMakeLists.txt
index e8095109806..ae96748ff9d 100644
--- a/storage/src/tests/persistence/CMakeLists.txt
+++ b/storage/src/tests/persistence/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storage_persistence_gtest_runner_app TEST
SOURCES
bucketownershipnotifiertest.cpp
@@ -15,7 +16,7 @@ vespa_add_executable(storage_persistence_gtest_runner_app TEST
DEPENDS
storage
storage_testpersistence_common
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/persistence/common/CMakeLists.txt b/storage/src/tests/persistence/common/CMakeLists.txt
index 53ec3fd7c0c..29b826c0c9b 100644
--- a/storage/src/tests/persistence/common/CMakeLists.txt
+++ b/storage/src/tests/persistence/common/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storage_testpersistence_common TEST
SOURCES
filestortestfixture.cpp
persistenceproviderwrapper.cpp
DEPENDS
- gtest
+ GTest::GTest
persistence
storage_testcommon
)
diff --git a/storage/src/tests/persistence/filestorage/CMakeLists.txt b/storage/src/tests/persistence/filestorage/CMakeLists.txt
index 5209bcce73d..8cabfb865cd 100644
--- a/storage/src/tests/persistence/filestorage/CMakeLists.txt
+++ b/storage/src/tests/persistence/filestorage/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storage_filestorage_gtest_runner_app TEST
SOURCES
deactivatebucketstest.cpp
@@ -16,7 +17,7 @@ vespa_add_executable(storage_filestorage_gtest_runner_app TEST
storage
storageapi
storage_testpersistence_common
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/storageserver/CMakeLists.txt b/storage/src/tests/storageserver/CMakeLists.txt
index 1d759a534f6..95ce08265ad 100644
--- a/storage/src/tests/storageserver/CMakeLists.txt
+++ b/storage/src/tests/storageserver/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storage_teststorageserver TEST
SOURCES
testvisitormessagesession.cpp
@@ -25,7 +26,7 @@ vespa_add_executable(storage_storageserver_gtest_runner_app TEST
storage_storageserver
storage_testcommon
storage_teststorageserver
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/tests/visiting/CMakeLists.txt b/storage/src/tests/visiting/CMakeLists.txt
index c1b19960cea..8bfc28e7eb9 100644
--- a/storage/src/tests/visiting/CMakeLists.txt
+++ b/storage/src/tests/visiting/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storage_visiting_gtest_runner_app TEST
SOURCES
commandqueuetest.cpp
@@ -10,7 +11,7 @@ vespa_add_executable(storage_visiting_gtest_runner_app TEST
DEPENDS
storage
storage_teststorageserver
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storage/src/vespa/storage/bucketdb/CMakeLists.txt b/storage/src/vespa/storage/bucketdb/CMakeLists.txt
index a99d16d9f0f..5bd966ae0e1 100644
--- a/storage/src/vespa/storage/bucketdb/CMakeLists.txt
+++ b/storage/src/vespa/storage/bucketdb/CMakeLists.txt
@@ -2,11 +2,13 @@
vespa_add_library(storage_bucketdb OBJECT
SOURCES
btree_bucket_database.cpp
+ btree_lockable_map.cpp
bucketcopy.cpp
bucketdatabase.cpp
bucketinfo.cpp
bucketmanager.cpp
bucketmanagermetrics.cpp
+ generic_btree_bucket_database.cpp
judyarray.cpp
lockablemap.cpp
mapbucketdatabase.cpp
diff --git a/storage/src/vespa/storage/bucketdb/abstract_bucket_map.h b/storage/src/vespa/storage/bucketdb/abstract_bucket_map.h
new file mode 100644
index 00000000000..6c669beab1c
--- /dev/null
+++ b/storage/src/vespa/storage/bucketdb/abstract_bucket_map.h
@@ -0,0 +1,244 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/document/bucket/bucketid.h>
+#include <vespa/vespalib/stllike/hash_map.h>
+#include <vespa/vespalib/stllike/hash_set.h>
+#include <vespa/vespalib/util/time.h>
+#include <cassert>
+#include <functional>
+#include <iosfwd>
+#include <map>
+
+namespace storage::bucketdb {
+
+/*
+ * Interface for content node bucket database implementations.
+ *
+ * Allows for multiple divergent implementations to exist of the
+ * bucket database in a transition period.
+ */
+template <typename ValueT>
+class AbstractBucketMap {
+public:
+ using key_type = uint64_t; // Always a raw u64 bucket key.
+ using mapped_type = ValueT;
+ using size_type = size_t;
+ using BucketId = document::BucketId;
+ struct WrappedEntry;
+
+ // Responsible for releasing lock in map when out of scope.
+ class LockKeeper {
+ friend struct WrappedEntry;
+ AbstractBucketMap& _map;
+ key_type _key;
+ bool _locked;
+
+ LockKeeper(AbstractBucketMap& map, key_type key)
+ : _map(map), _key(key), _locked(true) {}
+ void unlock() { _map.unlock(_key); _locked = false; }
+ public:
+ ~LockKeeper() { if (_locked) unlock(); }
+ };
+
+ struct WrappedEntry {
+ WrappedEntry()
+ : _exists(false),
+ _preExisted(false),
+ _lockKeeper(),
+ _value(),
+ _clientId(nullptr)
+ {}
+ WrappedEntry(AbstractBucketMap& map,
+ const key_type& key, const mapped_type& val,
+ const char* clientId, bool preExisted_)
+ : _exists(true),
+ _preExisted(preExisted_),
+ _lockKeeper(new LockKeeper(map, key)),
+ _value(val),
+ _clientId(clientId) {}
+ WrappedEntry(AbstractBucketMap& map, const key_type& key,
+ const char* clientId)
+ : _exists(false),
+ _preExisted(false),
+ _lockKeeper(new LockKeeper(map, key)),
+ _value(),
+ _clientId(clientId) {}
+ // TODO noexcept on these:
+ WrappedEntry(WrappedEntry&&) = default;
+ WrappedEntry& operator=(WrappedEntry&&) = default;
+ ~WrappedEntry();
+
+ mapped_type* operator->() { return &_value; }
+ const mapped_type* operator->() const { return &_value; }
+ mapped_type& operator*() { return _value; }
+ const mapped_type& operator*() const { return _value; }
+
+ const mapped_type *get() const { return &_value; }
+ mapped_type *get() { return &_value; }
+
+ void write();
+ void remove();
+ void unlock();
+ [[nodiscard]] bool exist() const { return _exists; } // TODO rename to exists()
+ [[nodiscard]] bool preExisted() const { return _preExisted; }
+ [[nodiscard]] bool locked() const { return _lockKeeper.get(); }
+ const key_type& getKey() const { return _lockKeeper->_key; };
+
+ BucketId getBucketId() const {
+ return BucketId(BucketId::keyToBucketId(getKey()));
+ }
+ protected:
+ bool _exists;
+ bool _preExisted;
+ std::unique_ptr<LockKeeper> _lockKeeper;
+ mapped_type _value;
+ const char* _clientId;
+ friend class AbstractLockableMap;
+ };
+
+ struct LockId {
+ key_type _key;
+ const char* _owner;
+
+ LockId() : _key(0), _owner("none - empty token") {}
+ LockId(key_type key, const char* owner)
+ : _key(key), _owner(owner)
+ {
+ assert(_owner);
+ }
+
+ size_t hash() const { return _key; }
+ size_t operator%(size_t val) const { return _key % val; }
+ bool operator==(const LockId& id) const { return (_key == id._key); }
+ operator key_type() const { return _key; }
+ };
+
+ using EntryMap = std::map<BucketId, WrappedEntry>; // TODO ordered std::vector instead? map interface needed?
+
+ enum Decision { ABORT, UPDATE, REMOVE, CONTINUE, DECISION_COUNT };
+
+ AbstractBucketMap() = default;
+ virtual ~AbstractBucketMap() = default;
+
+ virtual void insert(const key_type& key, const mapped_type& value,
+ const char* client_id, bool has_lock,
+ bool& pre_existed) = 0;
+ virtual bool erase(const key_type& key, const char* clientId, bool has_lock) = 0;
+
+ virtual WrappedEntry get(const key_type& key, const char* clientId, bool createIfNonExisting) = 0;
+ WrappedEntry get(const key_type& key, const char* clientId) {
+ return get(key, clientId, false);
+ }
+ /**
+ * Returns all buckets in the bucket database that can contain the given
+ * bucket, and all buckets that that bucket contains.
+ */
+ virtual EntryMap getAll(const BucketId& bucketId, const char* clientId) = 0;
+ /**
+ * Returns all buckets in the bucket database that can contain the given
+ * bucket. Usually, there should be only one such bucket, but in the case
+ * of inconsistent splitting, there may be more than one.
+ */
+ virtual EntryMap getContained(const BucketId& bucketId, const char* clientId) = 0;
+ /**
+ * Returns true iff bucket has no superbuckets or sub-buckets in the
+ * database. Usage assumption is that any operation that can cause the
+ * bucket to become inconsistent will require taking its lock, so by
+ * requiring the lock to be provided here we avoid race conditions.
+ */
+ virtual bool isConsistent(const WrappedEntry& entry) = 0; // TODO const
+
+ static constexpr uint32_t DEFAULT_CHUNK_SIZE = 1000;
+
+
+ /**
+ * Iterate over the entire database contents, holding the global database
+ * mutex for `chunkSize` processed entries at a time, yielding the current
+ * thread between each chunk to allow other threads to get a chance at
+ * acquiring a bucket lock.
+ *
+ * TODO deprecate in favor of snapshotting once fully on B-tree DB
+ *
+ * Type erasure of functor needed due to virtual indirection.
+ */
+ void for_each_chunked(std::function<Decision(uint64_t, ValueT&)> func,
+ const char* clientId,
+ vespalib::duration yieldTime = 10us,
+ uint32_t chunkSize = DEFAULT_CHUNK_SIZE)
+ {
+ do_for_each_chunked(std::move(func), clientId, yieldTime, chunkSize);
+ }
+
+ void for_each_mutable(std::function<Decision(uint64_t, ValueT&)> func,
+ const char* clientId,
+ const key_type& first = 0,
+ const key_type& last = UINT64_MAX)
+ {
+ do_for_each_mutable(std::move(func), clientId, first, last);
+ }
+
+ void for_each(std::function<Decision(uint64_t, const ValueT&)> func,
+ const char* clientId,
+ const key_type& first = 0,
+ const key_type& last = UINT64_MAX)
+ {
+ do_for_each(std::move(func), clientId, first, last);
+ }
+
+ [[nodiscard]] virtual size_type size() const noexcept = 0;
+ [[nodiscard]] virtual size_type getMemoryUsage() const noexcept = 0;
+ [[nodiscard]] virtual bool empty() const noexcept = 0;
+
+ virtual void showLockClients(vespalib::asciistream& out) const = 0;
+
+ virtual void print(std::ostream& out, bool verbose, const std::string& indent) const = 0;
+private:
+ virtual void unlock(const key_type& key) = 0; // Only for bucket lock guards
+ virtual void do_for_each_chunked(std::function<Decision(uint64_t, ValueT&)> func,
+ const char* clientId,
+ vespalib::duration yieldTime,
+ uint32_t chunkSize) = 0;
+ virtual void do_for_each_mutable(std::function<Decision(uint64_t, ValueT&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last) = 0;
+ virtual void do_for_each(std::function<Decision(uint64_t, const ValueT&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last) = 0;
+};
+
+template <typename ValueT>
+std::ostream& operator<<(std::ostream& os, const AbstractBucketMap<ValueT>& map) {
+ map.print(os, false, "");
+ return os;
+}
+
+template <typename ValueT>
+AbstractBucketMap<ValueT>::WrappedEntry::~WrappedEntry() = default;
+
+template <typename ValueT>
+void AbstractBucketMap<ValueT>::WrappedEntry::write() {
+ assert(_lockKeeper->_locked);
+ assert(_value.verifyLegal());
+ bool b;
+ _lockKeeper->_map.insert(_lockKeeper->_key, _value, _clientId, true, b);
+ _lockKeeper->unlock();
+}
+
+template <typename ValueT>
+void AbstractBucketMap<ValueT>::WrappedEntry::remove() {
+ assert(_lockKeeper->_locked);
+ assert(_exists);
+ _lockKeeper->_map.erase(_lockKeeper->_key, _clientId, true);
+ _lockKeeper->unlock();
+}
+
+template <typename ValueT>
+void AbstractBucketMap<ValueT>::WrappedEntry::unlock() {
+ assert(_lockKeeper->_locked);
+ _lockKeeper->unlock();
+}
+
+}
diff --git a/storage/src/vespa/storage/bucketdb/btree_bucket_database.cpp b/storage/src/vespa/storage/bucketdb/btree_bucket_database.cpp
index 30fa8bb7543..42bd3a247bb 100644
--- a/storage/src/vespa/storage/bucketdb/btree_bucket_database.cpp
+++ b/storage/src/vespa/storage/bucketdb/btree_bucket_database.cpp
@@ -13,6 +13,10 @@
#include <vespa/vespalib/datastore/array_store.hpp>
#include <iostream>
+// TODO remove once this impl uses the generic bucket B-tree code!
+#include "generic_btree_bucket_database.h"
+#include <vespa/vespalib/datastore/datastore.h>
+
/*
* Buckets in our tree are represented by their 64-bit numeric key, in what's known as
* "reversed bit order with appended used-bits" form. I.e. a bucket ID (16, 0xcafe), which
@@ -40,16 +44,8 @@ using vespalib::datastore::EntryRef;
using vespalib::ConstArrayRef;
using document::BucketId;
-BTreeBucketDatabase::BTreeBucketDatabase()
- : _tree(),
- _store(make_default_array_store_config()),
- _generation_handler()
-{
-}
-
-BTreeBucketDatabase::~BTreeBucketDatabase() = default;
-
-vespalib::datastore::ArrayStoreConfig BTreeBucketDatabase::make_default_array_store_config() {
+template <typename ReplicaStore>
+vespalib::datastore::ArrayStoreConfig make_default_array_store_config() {
return ReplicaStore::optimizedConfigForHugePage(1023, vespalib::alloc::MemoryAllocator::HUGEPAGE_SIZE,
4 * 1024, 8 * 1024, 0.2).enable_free_lists(true);
}
@@ -121,6 +117,15 @@ uint8_t next_parent_bit_seek_level(uint8_t minBits, const document::BucketId& a,
}
+BTreeBucketDatabase::BTreeBucketDatabase()
+ : _tree(),
+ _store(make_default_array_store_config<ReplicaStore>()),
+ _generation_handler()
+{
+}
+
+BTreeBucketDatabase::~BTreeBucketDatabase() = default;
+
void BTreeBucketDatabase::commit_tree_changes() {
// TODO break up and refactor
// TODO verify semantics and usage
@@ -574,4 +579,45 @@ uint64_t BTreeBucketDatabase::ReadGuardImpl::generation() const noexcept {
return _guard.getGeneration();
}
+// TODO replace existing distributor DB code with generic impl.
+// This is to ensure the generic implementation compiles with an ArrayStore backing in
+// the meantime.
+struct BTreeBucketDatabase2 {
+ struct ReplicaValueTraits {
+ using ValueType = Entry;
+ using ConstValueRef = ConstEntryRef;
+ using DataStoreType = vespalib::datastore::ArrayStore<BucketCopy>;
+
+ static ValueType make_invalid_value() {
+ return Entry::createInvalid();
+ }
+ static uint64_t wrap_and_store_value(DataStoreType& store, const Entry& entry) noexcept {
+ auto replicas_ref = store.add(entry.getBucketInfo().getRawNodes());
+ return value_from(entry.getBucketInfo().getLastGarbageCollectionTime(), replicas_ref);
+ }
+ static void remove_by_wrapped_value(DataStoreType& store, uint64_t value) noexcept {
+ store.remove(entry_ref_from_value(value));
+ }
+ static ValueType unwrap_from_key_value(const DataStoreType& store, uint64_t key, uint64_t value) {
+ const auto replicas_ref = store.get(entry_ref_from_value(value));
+ const auto bucket = BucketId(BucketId::keyToBucketId(key));
+ return entry_from_replica_array_ref(bucket, gc_timestamp_from_value(value), replicas_ref);
+ }
+ static ConstValueRef unwrap_const_ref_from_key_value(const DataStoreType& store, uint64_t key, uint64_t value) {
+ const auto replicas_ref = store.get(entry_ref_from_value(value));
+ const auto bucket = BucketId(BucketId::keyToBucketId(key));
+ return const_entry_ref_from_replica_array_ref(bucket, gc_timestamp_from_value(value), replicas_ref);
+ }
+ };
+
+ using BTreeImpl = bucketdb::GenericBTreeBucketDatabase<ReplicaValueTraits>;
+ BTreeImpl _impl;
+
+ BTreeBucketDatabase2()
+ : _impl(make_default_array_store_config<ReplicaValueTraits::DataStoreType>())
+ {}
+};
+
+template class bucketdb::GenericBTreeBucketDatabase<BTreeBucketDatabase2::ReplicaValueTraits>;
+
}
diff --git a/storage/src/vespa/storage/bucketdb/btree_bucket_database.h b/storage/src/vespa/storage/bucketdb/btree_bucket_database.h
index 35e248cfc76..0dfa2b07b8a 100644
--- a/storage/src/vespa/storage/bucketdb/btree_bucket_database.h
+++ b/storage/src/vespa/storage/bucketdb/btree_bucket_database.h
@@ -84,8 +84,6 @@ private:
const document::BucketId& bucket,
std::vector<Entry>& entries) const;
- static vespalib::datastore::ArrayStoreConfig make_default_array_store_config();
-
class ReadGuardImpl : public ReadGuard {
const BTreeBucketDatabase* _db;
GenerationHandler::Guard _guard;
diff --git a/storage/src/vespa/storage/bucketdb/btree_lockable_map.cpp b/storage/src/vespa/storage/bucketdb/btree_lockable_map.cpp
new file mode 100644
index 00000000000..a76f50d41ab
--- /dev/null
+++ b/storage/src/vespa/storage/bucketdb/btree_lockable_map.cpp
@@ -0,0 +1,8 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "btree_lockable_map.hpp"
+
+namespace storage::bucketdb {
+
+template class BTreeLockableMap<StorageBucketInfo>; // Forced instantiation.
+
+}
diff --git a/storage/src/vespa/storage/bucketdb/btree_lockable_map.h b/storage/src/vespa/storage/bucketdb/btree_lockable_map.h
new file mode 100644
index 00000000000..136baefb615
--- /dev/null
+++ b/storage/src/vespa/storage/bucketdb/btree_lockable_map.h
@@ -0,0 +1,163 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include "abstract_bucket_map.h"
+#include "storagebucketinfo.h"
+#include <vespa/document/bucket/bucketid.h>
+#include <vespa/vespalib/util/time.h>
+#include <vespa/vespalib/stllike/hash_map.h>
+#include <vespa/vespalib/stllike/hash_set.h>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <cassert>
+#include <iosfwd>
+
+namespace storage::bucketdb {
+
+template <typename DataStoreTraitsT> class GenericBTreeBucketDatabase;
+
+/*
+ * AbstractBucketMap implementation that uses a B-tree bucket database backing structure.
+ *
+ * Identical global and per-bucket locking semantics as LockableMap.
+ */
+template <typename T>
+class BTreeLockableMap : public AbstractBucketMap<T> {
+ struct ValueTraits;
+public:
+ using ParentType = AbstractBucketMap<T>;
+ using WrappedEntry = typename ParentType::WrappedEntry;
+ using key_type = typename ParentType::key_type;
+ using mapped_type = typename ParentType::mapped_type;
+ using LockId = typename ParentType::LockId;
+ using EntryMap = typename ParentType::EntryMap;
+ using Decision = typename ParentType::Decision;
+ using BucketId = document::BucketId;
+
+ BTreeLockableMap();
+ ~BTreeLockableMap();
+
+ bool operator==(const BTreeLockableMap& other) const;
+ bool operator!=(const BTreeLockableMap& other) const {
+ return ! (*this == other);
+ }
+ bool operator<(const BTreeLockableMap& other) const;
+ size_t size() const noexcept override;
+ size_t getMemoryUsage() const noexcept override;
+ bool empty() const noexcept override;
+ void swap(BTreeLockableMap&);
+
+ WrappedEntry get(const key_type& key, const char* clientId, bool createIfNonExisting) override;
+ WrappedEntry get(const key_type& key, const char* clientId) {
+ return get(key, clientId, false);
+ }
+ bool erase(const key_type& key, const char* clientId, bool has_lock) override;
+ void insert(const key_type& key, const mapped_type& value,
+ const char* client_id, bool has_lock, bool& pre_existed) override;
+
+ bool erase(const key_type& key, const char* client_id) {
+ return erase(key, client_id, false);
+ }
+ void insert(const key_type& key, const mapped_type& value,
+ const char* client_id, bool& pre_existed) {
+ return insert(key, value, client_id, false, pre_existed);
+ }
+ void clear();
+ void print(std::ostream& out, bool verbose, const std::string& indent) const override;
+ EntryMap getContained(const BucketId& bucketId, const char* clientId) override;
+ EntryMap getAll(const BucketId& bucketId, const char* clientId) override;
+ bool isConsistent(const WrappedEntry& entry) override;
+ void showLockClients(vespalib::asciistream & out) const override;
+
+private:
+ struct hasher {
+ size_t operator () (const LockId & lid) const { return lid.hash(); }
+ };
+ class LockIdSet : public vespalib::hash_set<LockId, hasher> {
+ typedef vespalib::hash_set<LockId, hasher> Hash;
+ public:
+ LockIdSet();
+ ~LockIdSet();
+ void print(std::ostream& out, bool verbose, const std::string& indent) const;
+ bool exists(const LockId & lid) const { return this->find(lid) != Hash::end(); }
+ size_t getMemoryUsage() const;
+ };
+
+ class LockWaiters {
+ typedef vespalib::hash_map<size_t, LockId> WaiterMap;
+ public:
+ typedef size_t Key;
+ typedef typename WaiterMap::const_iterator const_iterator;
+ LockWaiters();
+ ~LockWaiters();
+ Key insert(const LockId & lid);
+ void erase(Key id) { _map.erase(id); }
+ const_iterator begin() const { return _map.begin(); }
+ const_iterator end() const { return _map.end(); }
+ private:
+ Key _id;
+ WaiterMap _map;
+ };
+
+ mutable std::mutex _lock;
+ std::condition_variable _cond;
+ std::unique_ptr<GenericBTreeBucketDatabase<ValueTraits>> _impl;
+ LockIdSet _lockedKeys;
+ LockWaiters _lockWaiters;
+
+ void unlock(const key_type& key) override;
+ bool findNextKey(key_type& key, mapped_type& val, const char* clientId,
+ std::unique_lock<std::mutex> &guard);
+ bool handleDecision(key_type& key, mapped_type& val, Decision decision);
+ void acquireKey(const LockId & lid, std::unique_lock<std::mutex> &guard);
+
+ void do_for_each_mutable(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last) override;
+
+ void do_for_each(std::function<Decision(uint64_t, const mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last) override;
+
+ void do_for_each_chunked(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* client_id,
+ vespalib::duration yield_time,
+ uint32_t chunk_size) override;
+
+ /**
+ * Process up to `chunk_size` bucket database entries from--and possibly
+ * including--the bucket pointed to by `key`.
+ *
+ * Returns true if additional chunks may be processed after the call to
+ * this function has returned, false if iteration has completed or if
+ * `func` returned an abort-decision.
+ *
+ * Modifies `key` in-place to point to the next key to process for the next
+ * invocation of this function.
+ */
+ bool processNextChunk(std::function<Decision(uint64_t, mapped_type&)>& func,
+ key_type& key,
+ const char* client_id,
+ uint32_t chunk_size);
+
+ /**
+ * Returns the given bucket, its super buckets and its sub buckets.
+ */
+ void getAllWithoutLocking(const BucketId& bucket,
+ std::vector<BucketId::Type>& keys);
+
+ /**
+ * Find the given list of keys in the map and add them to the map of
+ * results, locking them in the process.
+ */
+ void addAndLockResults(const std::vector<BucketId::Type>& keys,
+ const char* clientId,
+ std::map<BucketId, WrappedEntry>& results,
+ std::unique_lock<std::mutex> &guard);
+};
+
+}
diff --git a/storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp b/storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp
new file mode 100644
index 00000000000..9c7228ae21d
--- /dev/null
+++ b/storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp
@@ -0,0 +1,507 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "btree_lockable_map.h"
+#include "generic_btree_bucket_database.hpp"
+#include <vespa/vespalib/btree/btreebuilder.h>
+#include <vespa/vespalib/btree/btreenodeallocator.hpp>
+#include <vespa/vespalib/btree/btreenode.hpp>
+#include <vespa/vespalib/btree/btreenodestore.hpp>
+#include <vespa/vespalib/btree/btreeiterator.hpp>
+#include <vespa/vespalib/btree/btreeroot.hpp>
+#include <vespa/vespalib/btree/btreebuilder.hpp>
+#include <vespa/vespalib/btree/btree.hpp>
+#include <vespa/vespalib/btree/btreestore.hpp>
+#include <vespa/vespalib/datastore/datastore.h>
+#include <vespa/vespalib/stllike/hash_map.hpp>
+#include <vespa/vespalib/stllike/hash_set.hpp>
+#include <thread>
+#include <sstream>
+
+// Major TODOs in the short term:
+// - Introduce snapshotting for readers
+// - Greatly improve performance for DB iteration for readers by avoiding
+// requirement to lock individual buckets and perform O(n) lbound seeks
+// just to do a sweep.
+
+namespace storage::bucketdb {
+
+using vespalib::datastore::EntryRef;
+using vespalib::ConstArrayRef;
+using document::BucketId;
+
+template <typename T>
+struct BTreeLockableMap<T>::ValueTraits {
+ using ValueType = T;
+ using ConstValueRef = const T&;
+ using DataStoreType = vespalib::datastore::DataStore<ValueType>;
+
+ static EntryRef entry_ref_from_value(uint64_t value) {
+ return EntryRef(value & 0xffffffffULL);
+ }
+ static ValueType make_invalid_value() {
+ return ValueType();
+ }
+ static uint64_t wrap_and_store_value(DataStoreType& store, const ValueType& value) noexcept {
+ return store.addEntry(value).ref();
+ }
+ static void remove_by_wrapped_value(DataStoreType& store, uint64_t value) noexcept {
+ store.holdElem(entry_ref_from_value(value), 1);
+ }
+ static ValueType unwrap_from_key_value(const DataStoreType& store, [[maybe_unused]] uint64_t key, uint64_t value) {
+ return store.getEntry(entry_ref_from_value(value));
+ }
+ static ConstValueRef unwrap_const_ref_from_key_value(const DataStoreType& store, [[maybe_unused]] uint64_t key, uint64_t value) {
+ return store.getEntry(entry_ref_from_value(value));
+ }
+};
+
+template <typename T>
+BTreeLockableMap<T>::BTreeLockableMap()
+ : _impl(std::make_unique<GenericBTreeBucketDatabase<ValueTraits>>())
+{}
+
+template <typename T>
+BTreeLockableMap<T>::~BTreeLockableMap() = default;
+
+template <typename T>
+BTreeLockableMap<T>::LockIdSet::LockIdSet() : Hash() {}
+
+template <typename T>
+BTreeLockableMap<T>::LockIdSet::~LockIdSet() = default;
+
+template <typename T>
+size_t BTreeLockableMap<T>::LockIdSet::getMemoryUsage() const {
+ return Hash::getMemoryConsumption();
+}
+
+template <typename T>
+BTreeLockableMap<T>::LockWaiters::LockWaiters() : _id(0), _map() {}
+
+template <typename T>
+BTreeLockableMap<T>::LockWaiters::~LockWaiters() = default;
+
+template <typename T>
+size_t BTreeLockableMap<T>::LockWaiters::insert(const LockId & lid) {
+ Key id(_id++);
+ _map.insert(typename WaiterMap::value_type(id, lid));
+ return id;
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::operator==(const BTreeLockableMap& other) const {
+ std::lock_guard guard(_lock);
+ std::lock_guard guard2(other._lock);
+ if (_impl->size() != other._impl->size()) {
+ return false;
+ }
+ auto lhs = _impl->begin();
+ auto rhs = other._impl->begin();
+ for (; lhs.valid(); ++lhs, ++rhs) {
+ assert(rhs.valid());
+ if (lhs.getKey() != rhs.getKey()) {
+ return false;
+ }
+ if (_impl->const_value_ref_from_valid_iterator(lhs)
+ != other._impl->const_value_ref_from_valid_iterator(rhs))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::operator<(const BTreeLockableMap& other) const {
+ std::lock_guard guard(_lock);
+ std::lock_guard guard2(other._lock);
+ auto lhs = _impl->begin();
+ auto rhs = other._impl->begin();
+ for (; lhs.valid() && rhs.valid(); ++lhs, ++rhs) {
+ if (lhs.getKey() != rhs.getKey()) {
+ return (lhs.getKey() < rhs.getKey());
+ }
+ if (_impl->const_value_ref_from_valid_iterator(lhs)
+ != other._impl->const_value_ref_from_valid_iterator(rhs))
+ {
+ return (_impl->const_value_ref_from_valid_iterator(lhs)
+ < other._impl->const_value_ref_from_valid_iterator(rhs));
+ }
+ }
+ if (lhs.valid() == rhs.valid()) {
+ return false; // All keys are equal in maps of equal size.
+ }
+ return rhs.valid(); // Rhs still valid, lhs is not; ergo lhs is "less".
+}
+
+template <typename T>
+size_t BTreeLockableMap<T>::size() const noexcept {
+ std::lock_guard guard(_lock);
+ return _impl->size();
+}
+
+template <typename T>
+size_t BTreeLockableMap<T>::getMemoryUsage() const noexcept {
+ std::lock_guard guard(_lock);
+ const auto impl_usage = _impl->memory_usage();
+ return (impl_usage.allocatedBytes() + _lockedKeys.getMemoryUsage() +
+ sizeof(std::mutex) + sizeof(std::condition_variable));
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::empty() const noexcept {
+ std::lock_guard guard(_lock);
+ return _impl->empty();
+}
+
+template <typename T>
+void BTreeLockableMap<T>::swap(BTreeLockableMap& other) {
+ std::lock_guard guard(_lock);
+ std::lock_guard guard2(other._lock);
+ _impl.swap(other._impl);
+}
+
+template <typename T>
+void BTreeLockableMap<T>::acquireKey(const LockId& lid, std::unique_lock<std::mutex>& guard) {
+ if (_lockedKeys.exists(lid)) {
+ auto waitId = _lockWaiters.insert(lid);
+ while (_lockedKeys.exists(lid)) {
+ _cond.wait(guard);
+ }
+ _lockWaiters.erase(waitId);
+ }
+}
+
+template <typename T>
+typename BTreeLockableMap<T>::WrappedEntry
+BTreeLockableMap<T>::get(const key_type& key, const char* clientId, bool createIfNonExisting) {
+ LockId lid(key, clientId);
+ std::unique_lock guard(_lock);
+ acquireKey(lid, guard);
+ auto iter = _impl->find(key);
+ bool preExisted = iter.valid();
+
+ if (!preExisted && createIfNonExisting) {
+ _impl->update_by_raw_key(key, mapped_type());
+ // TODO avoid double lookup, though this is in an unlikely branch so shouldn't matter much.
+ iter = _impl->find(key);
+ assert(iter.valid());
+ }
+ if (!iter.valid()) {
+ return WrappedEntry();
+ }
+ _lockedKeys.insert(lid);
+ return WrappedEntry(*this, key, _impl->entry_from_iterator(iter), clientId, preExisted);
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::erase(const key_type& key, const char* client_id, bool has_lock) {
+ LockId lid(key, client_id);
+ std::unique_lock guard(_lock);
+ if (!has_lock) {
+ acquireKey(lid, guard);
+ }
+ return _impl->remove_by_raw_key(key);
+}
+
+template <typename T>
+void BTreeLockableMap<T>::insert(const key_type& key, const mapped_type& value,
+ const char* clientId, bool has_lock, bool& pre_existed)
+{
+ LockId lid(key, clientId);
+ std::unique_lock guard(_lock);
+ if (!has_lock) {
+ acquireKey(lid, guard);
+ }
+ pre_existed = _impl->update_by_raw_key(key, value);
+}
+
+template <typename T>
+void BTreeLockableMap<T>::clear() {
+ std::lock_guard guard(_lock);
+ _impl->clear();
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::findNextKey(key_type& key, mapped_type& val,
+ const char* clientId,
+ std::unique_lock<std::mutex> &guard)
+{
+ // Wait for next value to unlock.
+ auto it = _impl->lower_bound(key);
+ while (it.valid() && _lockedKeys.exists(LockId(it.getKey(), ""))) {
+ auto wait_id = _lockWaiters.insert(LockId(it.getKey(), clientId));
+ _cond.wait(guard);
+ _lockWaiters.erase(wait_id);
+ it = _impl->lower_bound(key);
+ }
+ if (!it.valid()) {
+ return true;
+ }
+ key = it.getKey();
+ val = _impl->entry_from_iterator(it);
+ return false;
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::handleDecision(key_type& key, mapped_type& val,
+ Decision decision)
+{
+ switch (decision) {
+ case Decision::UPDATE:
+ _impl->update_by_raw_key(key, val);
+ break;
+ case Decision::REMOVE:
+ // Invalidating is fine, since the caller doesn't hold long-lived iterators.
+ _impl->remove_by_raw_key(key);
+ break;
+ case Decision::ABORT:
+ return true;
+ case Decision::CONTINUE:
+ break;
+ default:
+ HDR_ABORT("should not be reached");
+ }
+ return false;
+}
+
+template <typename T>
+void BTreeLockableMap<T>::do_for_each_mutable(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last)
+{
+ key_type key = first;
+ mapped_type val;
+ std::unique_lock guard(_lock);
+ while (true) {
+ if (findNextKey(key, val, clientId, guard) || key > last) {
+ return;
+ }
+ Decision d(func(key, val));
+ if (handleDecision(key, val, d)) {
+ return;
+ }
+ ++key;
+ }
+}
+
+template <typename T>
+void BTreeLockableMap<T>::do_for_each(std::function<Decision(uint64_t, const mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last)
+{
+ key_type key = first;
+ mapped_type val;
+ std::unique_lock guard(_lock);
+ while (true) {
+ if (findNextKey(key, val, clientId, guard) || key > last) {
+ return;
+ }
+ Decision d(func(key, val));
+ assert(d == Decision::ABORT || d == Decision::CONTINUE);
+ if (handleDecision(key, val, d)) {
+ return;
+ }
+ ++key;
+ }
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::processNextChunk(std::function<Decision(uint64_t, mapped_type&)>& func,
+ key_type& key,
+ const char* client_id,
+ const uint32_t chunk_size)
+{
+ mapped_type val;
+ std::unique_lock guard(_lock);
+ for (uint32_t processed = 0; processed < chunk_size; ++processed) {
+ if (findNextKey(key, val, client_id, guard)) {
+ return false;
+ }
+ Decision d(func(const_cast<const key_type&>(key), val));
+ if (handleDecision(key, val, d)) {
+ return false;
+ }
+ ++key;
+ }
+ return true;
+}
+
+template <typename T>
+void BTreeLockableMap<T>::do_for_each_chunked(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* client_id,
+ vespalib::duration yield_time,
+ uint32_t chunk_size)
+{
+ key_type key{};
+ while (processNextChunk(func, key, client_id, chunk_size)) {
+ // Rationale: delay iteration for as short a time as possible while
+ // allowing another thread blocked on the main DB mutex to acquire it
+ // in the meantime. Simply yielding the thread does not have the
+ // intended effect with the Linux scheduler.
+ // This is a pragmatic stop-gap solution; a more robust change requires
+ // the redesign of bucket DB locking and signalling semantics in the
+ // face of blocked point lookups.
+ std::this_thread::sleep_for(yield_time);
+ }
+}
+
+template <typename T>
+void BTreeLockableMap<T>::print(std::ostream& out, bool verbose,
+ const std::string& indent) const
+{
+ std::lock_guard guard(_lock);
+ out << "BTreeLockableMap {\n" << indent << " ";
+
+ if (verbose) {
+ for (auto it = _impl->begin(); it.valid(); ++it) {
+ out << "Key: " << BucketId(BucketId::keyToBucketId(it.getKey()))
+ << " Value: " << _impl->entry_from_iterator(it) << "\n" << indent << " ";
+ }
+ out << "\n" << indent << " Locked keys: ";
+ _lockedKeys.print(out, verbose, indent + " ");
+ }
+ out << "} : ";
+}
+
+template <typename T>
+void BTreeLockableMap<T>::LockIdSet::print(std::ostream& out, bool verbose,
+ const std::string& indent) const
+{
+ out << "hash {";
+ for (const auto& entry : *this) {
+ if (verbose) {
+ out << "\n" << indent << " ";
+ } else {
+ out << " ";
+ }
+ out << entry;
+ }
+ if (verbose) {
+ out << "\n" << indent;
+ }
+ out << " }";
+}
+
+
+
+template <typename T>
+void BTreeLockableMap<T>::unlock(const key_type& key) {
+ std::lock_guard guard(_lock);
+ _lockedKeys.erase(LockId(key, ""));
+ _cond.notify_all();
+}
+
+template <typename T>
+void BTreeLockableMap<T>::addAndLockResults(
+ const std::vector<BucketId::Type>& keys,
+ const char* clientId,
+ std::map<BucketId, WrappedEntry>& results,
+ std::unique_lock<std::mutex> &guard)
+{
+ // Wait until all buckets are free to be added, then add them all.
+ while (true) {
+ bool allOk = true;
+ key_type waitingFor(0);
+
+ for (const auto key : keys) {
+ if (_lockedKeys.exists(LockId(key, clientId))) {
+ waitingFor = key;
+ allOk = false;
+ break;
+ }
+ }
+
+ if (!allOk) {
+ auto waitId = _lockWaiters.insert(LockId(waitingFor, clientId));
+ _cond.wait(guard);
+ _lockWaiters.erase(waitId);
+ } else {
+ for (const auto key : keys) {
+ auto iter = _impl->find(key);
+ if (iter.valid()) {
+ _lockedKeys.insert(LockId(key, clientId));
+ results[BucketId(BucketId::keyToBucketId(key))] = WrappedEntry(
+ *this, key, _impl->entry_from_iterator(iter), clientId, true);
+ }
+ }
+ break;
+ }
+ }
+}
+
+template <typename T>
+typename BTreeLockableMap<T>::EntryMap
+BTreeLockableMap<T>::getContained(const BucketId& bucket,
+ const char* clientId)
+{
+ std::unique_lock guard(_lock);
+ std::map<BucketId, WrappedEntry> results;
+
+ std::vector<BucketId::Type> keys;
+ _impl->find_parents_and_self(bucket, [&keys](uint64_t key, [[maybe_unused]]const auto& value){
+ keys.emplace_back(key);
+ });
+
+ if (!keys.empty()) {
+ addAndLockResults(keys, clientId, results, guard);
+ }
+
+ return results;
+}
+
+template <typename T>
+void BTreeLockableMap<T>::getAllWithoutLocking(const BucketId& bucket,
+ std::vector<BucketId::Type>& keys)
+{
+ _impl->find_parents_self_and_children(bucket, [&keys](uint64_t key, [[maybe_unused]]const auto& value){
+ keys.emplace_back(key);
+ });
+}
+
+/**
+ * Returns the given bucket, its super buckets and its sub buckets.
+ */
+template <typename T>
+typename BTreeLockableMap<T>::EntryMap
+BTreeLockableMap<T>::getAll(const BucketId& bucket, const char* clientId) {
+ std::unique_lock guard(_lock);
+
+ std::map<BucketId, WrappedEntry> results;
+ std::vector<BucketId::Type> keys;
+
+ getAllWithoutLocking(bucket, keys);
+ addAndLockResults(keys, clientId, results, guard);
+
+ return results;
+}
+
+template <typename T>
+bool BTreeLockableMap<T>::isConsistent(const BTreeLockableMap::WrappedEntry& entry) {
+ std::lock_guard guard(_lock);
+ uint64_t n_buckets = 0;
+ _impl->find_parents_self_and_children(entry.getBucketId(),
+ [&n_buckets]([[maybe_unused]] uint64_t key, [[maybe_unused]] const auto& value) {
+ ++n_buckets;
+ });
+ return (n_buckets == 1);
+}
+
+template <typename T>
+void BTreeLockableMap<T>::showLockClients(vespalib::asciistream& out) const {
+ std::lock_guard guard(_lock);
+ out << "Currently grabbed locks:";
+ for (const auto& locked : _lockedKeys) {
+ out << "\n "
+ << BucketId(BucketId::keyToBucketId(locked._key))
+ << " - " << locked._owner;
+ }
+ out << "\nClients waiting for keys:";
+ for (const auto& waiter : _lockWaiters) {
+ out << "\n "
+ << BucketId(BucketId::keyToBucketId(waiter.second._key))
+ << " - " << waiter.second._owner;
+ }
+}
+
+}
diff --git a/storage/src/vespa/storage/bucketdb/bucketmanager.cpp b/storage/src/vespa/storage/bucketdb/bucketmanager.cpp
index c8cdaaaaa23..4dbeb5a9a22 100644
--- a/storage/src/vespa/storage/bucketdb/bucketmanager.cpp
+++ b/storage/src/vespa/storage/bucketdb/bucketmanager.cpp
@@ -116,12 +116,12 @@ namespace {
: _state(*distribution, systemState),
_result(result),
_factory(factory),
- _storageDistribution(distribution)
+ _storageDistribution(std::move(distribution))
{
}
StorBucketDatabase::Decision operator()(uint64_t bucketId,
- StorBucketDatabase::Entry& data)
+ const StorBucketDatabase::Entry& data)
{
document::BucketId b(document::BucketId::keyToBucketId(bucketId));
try{
@@ -155,7 +155,7 @@ namespace {
.getDistributionConfigHash().c_str(),
_state.getClusterState().toString().c_str());
}
- return StorBucketDatabase::CONTINUE;
+ return StorBucketDatabase::Decision::CONTINUE;
}
};
@@ -180,7 +180,7 @@ namespace {
StorBucketDatabase::Decision operator()(
document::BucketId::Type bucketId,
- StorBucketDatabase::Entry& data)
+ const StorBucketDatabase::Entry& data)
{
document::BucketId bucket(
document::BucketId::keyToBucketId(bucketId));
@@ -202,7 +202,7 @@ namespace {
}
}
- return StorBucketDatabase::CONTINUE;
+ return StorBucketDatabase::Decision::CONTINUE;
};
void add(const MetricsUpdater& rhs) {
@@ -242,7 +242,7 @@ BucketManager::updateMetrics(bool updateDocCount)
MetricsUpdater total(diskCount);
for (auto& space : _component.getBucketSpaceRepo()) {
MetricsUpdater m(diskCount);
- space.second->bucketDatabase().chunkedAll(m, "BucketManager::updateMetrics");
+ space.second->bucketDatabase().for_each_chunked(std::ref(m), "BucketManager::updateMetrics");
total.add(m);
if (updateDocCount) {
auto bm = _metrics->bucket_spaces.find(space.first);
@@ -338,10 +338,10 @@ namespace {
class BucketDBDumper {
vespalib::XmlOutputStream& _xos;
public:
- BucketDBDumper(vespalib::XmlOutputStream& xos) : _xos(xos) {}
+ explicit BucketDBDumper(vespalib::XmlOutputStream& xos) : _xos(xos) {}
StorBucketDatabase::Decision operator()(
- uint64_t bucketId, StorBucketDatabase::Entry& info)
+ uint64_t bucketId, const StorBucketDatabase::Entry& info)
{
using namespace vespalib::xml;
document::BucketId bucket(
@@ -356,7 +356,7 @@ namespace {
info.getBucketInfo().printXml(_xos);
_xos << XmlAttribute("disk", info.disk);
_xos << XmlEndTag();
- return StorBucketDatabase::CONTINUE;
+ return StorBucketDatabase::Decision::CONTINUE;
};
};
}
@@ -378,8 +378,8 @@ BucketManager::reportStatus(std::ostream& out,
xmlReporter << XmlTag("bucket-space")
<< XmlAttribute("name", document::FixedBucketSpaces::to_string(space.first));
BucketDBDumper dumper(xmlReporter.getStream());
- _component.getBucketSpaceRepo().get(space.first).bucketDatabase().chunkedAll(
- dumper, "BucketManager::reportStatus");
+ _component.getBucketSpaceRepo().get(space.first).bucketDatabase().for_each_chunked(
+ std::ref(dumper), "BucketManager::reportStatus");
xmlReporter << XmlEndTag();
}
xmlReporter << XmlEndTag();
@@ -655,12 +655,12 @@ BucketManager::processRequestBucketInfoCommands(document::BucketSpace bucketSpac
if (LOG_WOULD_LOG(spam)) {
DistributorInfoGatherer<true> builder(
*clusterState, result, idFac, distribution);
- _component.getBucketDatabase(bucketSpace).chunkedAll(builder,
+ _component.getBucketDatabase(bucketSpace).for_each_chunked(std::ref(builder),
"BucketManager::processRequestBucketInfoCommands-1");
} else {
DistributorInfoGatherer<false> builder(
*clusterState, result, idFac, distribution);
- _component.getBucketDatabase(bucketSpace).chunkedAll(builder,
+ _component.getBucketDatabase(bucketSpace).for_each_chunked(std::ref(builder),
"BucketManager::processRequestBucketInfoCommands-2");
}
_metrics->fullBucketInfoLatency.addValue(
diff --git a/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.cpp b/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.cpp
new file mode 100644
index 00000000000..bcc471cc903
--- /dev/null
+++ b/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.cpp
@@ -0,0 +1,36 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "generic_btree_bucket_database.h"
+
+namespace storage::bucketdb {
+
+using document::BucketId;
+
+// TODO dedupe and unify common code
+uint8_t
+getMinDiffBits(uint16_t minBits, const BucketId& a, const BucketId& b) {
+ for (uint32_t i = minBits; i <= std::min(a.getUsedBits(), b.getUsedBits()); i++) {
+ BucketId a1(i, a.getRawId());
+ BucketId b1(i, b.getRawId());
+ if (b1.getId() != a1.getId()) {
+ return i;
+ }
+ }
+ return minBits;
+}
+
+uint8_t next_parent_bit_seek_level(uint8_t minBits, const BucketId& a, const BucketId& b) {
+ const uint8_t min_used = std::min(a.getUsedBits(), b.getUsedBits());
+ assert(min_used >= minBits); // Always monotonically descending towards leaves
+ for (uint32_t i = minBits; i <= min_used; i++) {
+ BucketId a1(i, a.getRawId());
+ BucketId b1(i, b.getRawId());
+ if (b1.getId() != a1.getId()) {
+ return i;
+ }
+ }
+ // The bit prefix is equal, which means that one node is a parent of the other. In this
+ // case we have to force the seek to continue from the next level in the tree.
+ return std::max(min_used, minBits) + 1;
+}
+
+}
diff --git a/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.h b/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.h
new file mode 100644
index 00000000000..15de7f3525b
--- /dev/null
+++ b/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.h
@@ -0,0 +1,242 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/document/bucket/bucketid.h>
+#include <vespa/vespalib/btree/btree.h>
+#include <vespa/vespalib/btree/minmaxaggregated.h>
+#include <vespa/vespalib/btree/minmaxaggrcalc.h>
+
+namespace storage::bucketdb {
+
+/**
+ * Database implementation-specific interface for appending entries
+ * during a merge() operation.
+ */
+template <typename ValueT>
+struct TrailingInserter {
+ virtual ~TrailingInserter() = default;
+ /**
+ * Insert a new database entry at the end of the current bucket space.
+ *
+ * Precondition: the bucket ID must sort after all entries that
+ * have already been iterated over or inserted via insert_at_end().
+ */
+ virtual void insert_at_end(const document::BucketId& bucket_id, const ValueT&) = 0;
+};
+
+/**
+ * Database implementation-specific interface for accessing bucket
+ * entries and prepending entries during a merge() operation.
+ */
+template <typename ValueT>
+struct Merger {
+ virtual ~Merger() = default;
+
+ // TODO this should ideally be separated into read/write functions, but this
+ // will suffice for now to avoid too many changes.
+
+ /**
+ * Bucket key/ID of the currently iterated entry. Unless the information stored
+ * in the DB Entry is needed, using one of these methods should be preferred to
+ * getting the bucket ID via current_entry(). The underlying DB is expected to
+ * have cheap access to the ID but _may_ have expensive access to the entry itself.
+ */
+ [[nodiscard]] virtual uint64_t bucket_key() const noexcept = 0;
+ [[nodiscard]] virtual document::BucketId bucket_id() const noexcept = 0;
+ /**
+ * Returns a mutable representation of the currently iterated database
+ * entry. If changes are made to this object, Result::Update must be
+ * returned from merge(). Otherwise, mutation visibility is undefined.
+ */
+ [[nodiscard]] virtual ValueT& current_entry() = 0;
+ /**
+ * Insert a new entry into the bucket database that is ordered before the
+ * currently iterated entry.
+ *
+ * Preconditions:
+ * - The bucket ID must sort _before_ the currently iterated
+ * entry's bucket ID, in "reversed bits" bucket key order.
+ * - The bucket ID must sort _after_ any entries previously
+ * inserted with insert_before_current().
+ * - The bucket ID must not be the same as a bucket that was
+ * already iterated over as part of the DB merge() call or inserted
+ * via a previous call to insert_before_current().
+ * Such buckets must be handled by explicitly updating the provided
+ * entry for the iterated bucket and returning Result::Update.
+ */
+ virtual void insert_before_current(const document::BucketId& bucket_id, const ValueT&) = 0;
+};
+
+/**
+ * Interface to be implemented by callers that wish to receive callbacks
+ * during a bucket merge() operation.
+ */
+template <typename ValueT>
+struct MergingProcessor {
+ // See merge() for semantics on enum values.
+ enum class Result {
+ Update,
+ KeepUnchanged,
+ Skip
+ };
+
+ virtual ~MergingProcessor() = default;
+ /**
+ * Invoked for each existing bucket in the database, in bucket key order.
+ * The provided Merge instance may be used to access the current entry
+ * and prepend entries to the DB.
+ *
+ * Return value semantics:
+ * - Result::Update:
+ * when merge() returns, the changes made to the current entry will
+ * become visible in the bucket database.
+ * - Result::KeepUnchanged:
+ * when merge() returns, the entry will remain in the same state as
+ * it was when merge() was originally called.
+ * - Result::Skip:
+ * when merge() returns, the entry will no longer be part of the DB.
+ * Any entries added via insert_before_current() _will_ be present.
+ *
+ */
+ virtual Result merge(Merger<ValueT>&) = 0;
+ /**
+ * Invoked once after all existing buckets have been iterated over.
+ * The provided TrailingInserter instance may be used to append
+ * an arbitrary number of entries to the database.
+ *
+ * This is used to handle elements remaining at the end of a linear
+ * merge operation.
+ */
+ virtual void insert_remaining_at_end(TrailingInserter<ValueT>&) {}
+};
+
+/*
+ * Bucket database implementation built around lock-free single-writer/multiple-readers B+tree.
+ *
+ * Key is always treated as a 64-bit uint bucket ID key.
+ * Value is a 64-bit uint whose semantics are handled by the provided DataStoreTraitsT.
+ * All DataStore access and value type (un)marshalling is deferred to the traits type,
+ * allowing this class to be used for both fixed-sized and dynamic-sized value types.
+ *
+ * Buckets in our tree are represented by their 64-bit numeric key, in what's known as
+ * "reversed bit order with appended used-bits" form. I.e. a bucket ID (16, 0xcafe), which
+ * in its canonical representation has 16 (the used-bits) in its 6 MSBs and 0xcafe in its
+ * LSBs is transformed into 0x7f53000000000010. This key is logically comprised of two parts:
+ * - the reversed bucket ID itself (0xcafe - 0x7f53) with all trailing zeroes for unset bits
+ * - the _non-reversed_ used-bits appended as the LSBs
+ *
+ * This particular transformation gives us keys with the following invariants:
+ * - all distinct bucket IDs map to exactly 1 key
+ * - buckets with the same ID but different used-bits are ordered in such a way that buckets
+ * with higher used-bits sort after buckets with lower used-bits
+ * - the key ordering represents an implicit in-order traversal of the binary bucket tree
+ * - consequently, all parent buckets are ordered before their child buckets
+ *
+ * The in-order traversal invariant is fundamental to many of the algorithms that operate
+ * on the bucket tree.
+ */
+template <typename DataStoreTraitsT>
+class GenericBTreeBucketDatabase {
+public:
+ using DataStoreType = typename DataStoreTraitsT::DataStoreType;
+ using ValueType = typename DataStoreTraitsT::ValueType;
+ using ConstValueRef = typename DataStoreTraitsT::ConstValueRef;
+ using GenerationHandler = vespalib::GenerationHandler;
+
+ struct KeyUsedBitsMinMaxAggrCalc : vespalib::btree::MinMaxAggrCalc {
+ constexpr static bool aggregate_over_values() { return false; }
+ constexpr static int32_t getVal(uint64_t key) noexcept {
+ static_assert(document::BucketId::CountBits == 6u);
+ return static_cast<int32_t>(key & 0b11'1111U); // 6 LSB of key contains used-bits
+ }
+ };
+
+ using BTree = vespalib::btree::BTree<uint64_t, uint64_t,
+ vespalib::btree::MinMaxAggregated,
+ std::less<>,
+ vespalib::btree::BTreeDefaultTraits,
+ KeyUsedBitsMinMaxAggrCalc>;
+ using BTreeConstIterator = typename BTree::ConstIterator;
+
+ BTree _tree;
+ DataStoreType _store;
+ GenerationHandler _generation_handler;
+
+ template <typename... DataStoreArgs>
+ explicit GenericBTreeBucketDatabase(DataStoreArgs&&... data_store_args)
+ : _store(std::forward<DataStoreArgs>(data_store_args)...)
+ {}
+
+ GenericBTreeBucketDatabase(const GenericBTreeBucketDatabase&) = delete;
+ GenericBTreeBucketDatabase& operator=(const GenericBTreeBucketDatabase&) = delete;
+ GenericBTreeBucketDatabase(GenericBTreeBucketDatabase&&) = delete;
+ GenericBTreeBucketDatabase& operator=(GenericBTreeBucketDatabase&&) = delete;
+
+ // TODO move
+ struct EntryProcessor {
+ virtual ~EntryProcessor() = default;
+ /** Return false to stop iterating. */
+ virtual bool process(const typename DataStoreTraitsT::ConstValueRef& e) = 0;
+ };
+
+ ValueType entry_from_iterator(const BTreeConstIterator& iter) const;
+ ConstValueRef const_value_ref_from_valid_iterator(const BTreeConstIterator& iter) const;
+
+ static document::BucketId bucket_from_valid_iterator(const BTreeConstIterator& iter);
+
+ BTreeConstIterator find(uint64_t key) const noexcept;
+ BTreeConstIterator lower_bound(uint64_t key) const noexcept;
+ BTreeConstIterator begin() const noexcept;
+
+ void clear() noexcept;
+ [[nodiscard]] size_t size() const noexcept;
+ [[nodiscard]] bool empty() const noexcept;
+ [[nodiscard]] vespalib::MemoryUsage memory_usage() const noexcept;
+
+ ValueType get(const document::BucketId& bucket) const;
+ ValueType get_by_raw_key(uint64_t key) const;
+ // Return true if bucket existed in DB, false otherwise.
+ bool remove(const document::BucketId& bucket);
+ bool remove_by_raw_key(uint64_t key);
+ // Returns true if bucket pre-existed in the DB, false otherwise
+ bool update(const document::BucketId& bucket, const ValueType& new_entry);
+ bool update_by_raw_key(uint64_t bucket_key, const ValueType& new_entry);
+
+ template <typename Func>
+ void find_parents_and_self(const document::BucketId& bucket,
+ Func func) const;
+
+ template <typename Func>
+ void find_parents_self_and_children(const document::BucketId& bucket,
+ Func func) const;
+
+ void for_each(EntryProcessor& proc, const document::BucketId& after) const;
+
+ document::BucketId getAppropriateBucket(uint16_t minBits, const document::BucketId& bid) const;
+
+ [[nodiscard]] uint32_t child_subtree_count(const document::BucketId& bucket) const;
+
+ const DataStoreType& store() const noexcept { return _store; }
+ DataStoreType& store() noexcept { return _store; }
+
+ void merge(MergingProcessor<ValueType>& proc);
+private:
+ // Functor is called for each found element in key order, with raw u64 keys and values.
+ template <typename Func>
+ BTreeConstIterator find_parents_internal(const typename BTree::FrozenView& frozen_view,
+ const document::BucketId& bucket,
+ Func func) const;
+ template <typename Func>
+ void find_parents_and_self_internal(const typename BTree::FrozenView& frozen_view,
+ const document::BucketId& bucket,
+ Func func) const;
+ void commit_tree_changes();
+
+ template <typename DataStoreTraitsT2> friend struct BTreeBuilderMerger;
+ template <typename DataStoreTraitsT2> friend struct BTreeTrailingInserter;
+};
+
+uint8_t getMinDiffBits(uint16_t minBits, const document::BucketId& a, const document::BucketId& b);
+uint8_t next_parent_bit_seek_level(uint8_t minBits, const document::BucketId& a, const document::BucketId& b);
+
+}
diff --git a/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.hpp b/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.hpp
new file mode 100644
index 00000000000..4b1b507d95a
--- /dev/null
+++ b/storage/src/vespa/storage/bucketdb/generic_btree_bucket_database.hpp
@@ -0,0 +1,494 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include "generic_btree_bucket_database.h"
+
+namespace storage::bucketdb {
+
+using document::BucketId;
+
+template <typename DataStoreTraitsT>
+BucketId GenericBTreeBucketDatabase<DataStoreTraitsT>::bucket_from_valid_iterator(const BTreeConstIterator& iter) {
+ return BucketId(BucketId::keyToBucketId(iter.getKey()));
+}
+
+template <typename DataStoreTraitsT>
+void GenericBTreeBucketDatabase<DataStoreTraitsT>::commit_tree_changes() {
+ // TODO break up and refactor
+ // TODO verify semantics and usage
+ // TODO make BTree wrapping API which abstracts away all this stuff via reader/writer interfaces
+ _tree.getAllocator().freeze();
+
+ auto current_gen = _generation_handler.getCurrentGeneration();
+ _store.transferHoldLists(current_gen);
+ _tree.getAllocator().transferHoldLists(current_gen);
+
+ _generation_handler.incGeneration();
+
+ auto used_gen = _generation_handler.getFirstUsedGeneration();
+ _store.trimHoldLists(used_gen);
+ _tree.getAllocator().trimHoldLists(used_gen);
+}
+
+template <typename DataStoreTraitsT>
+void GenericBTreeBucketDatabase<DataStoreTraitsT>::clear() noexcept {
+ _tree.clear();
+ commit_tree_changes();
+}
+
+template <typename DataStoreTraitsT>
+size_t GenericBTreeBucketDatabase<DataStoreTraitsT>::size() const noexcept {
+ return _tree.size();
+}
+
+template <typename DataStoreTraitsT>
+bool GenericBTreeBucketDatabase<DataStoreTraitsT>::empty() const noexcept {
+ return !_tree.begin().valid();
+}
+
+template <typename DataStoreTraitsT>
+vespalib::MemoryUsage GenericBTreeBucketDatabase<DataStoreTraitsT>::memory_usage() const noexcept {
+ auto mem_usage = _tree.getMemoryUsage();
+ mem_usage.merge(_store.getMemoryUsage());
+ return mem_usage;
+}
+
+template <typename DataStoreTraitsT>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::ValueType
+GenericBTreeBucketDatabase<DataStoreTraitsT>::entry_from_iterator(const BTreeConstIterator& iter) const {
+ if (!iter.valid()) {
+ return DataStoreTraitsT::make_invalid_value();
+ }
+ const auto value = iter.getData();
+ std::atomic_thread_fence(std::memory_order_acquire);
+ return DataStoreTraitsT::unwrap_from_key_value(_store, iter.getKey(), value);
+}
+
+template <typename DataStoreTraitsT>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::ConstValueRef
+GenericBTreeBucketDatabase<DataStoreTraitsT>::const_value_ref_from_valid_iterator(const BTreeConstIterator& iter) const {
+ const auto value = iter.getData();
+ std::atomic_thread_fence(std::memory_order_acquire);
+ return DataStoreTraitsT::unwrap_const_ref_from_key_value(_store, iter.getKey(), value);
+}
+
+template <typename DataStoreTraitsT>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::BTreeConstIterator
+GenericBTreeBucketDatabase<DataStoreTraitsT>::lower_bound(uint64_t key) const noexcept {
+ return _tree.lowerBound(key);
+}
+
+template <typename DataStoreTraitsT>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::BTreeConstIterator
+GenericBTreeBucketDatabase<DataStoreTraitsT>::find(uint64_t key) const noexcept {
+ return _tree.find(key);
+}
+
+template <typename DataStoreTraitsT>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::BTreeConstIterator
+GenericBTreeBucketDatabase<DataStoreTraitsT>::begin() const noexcept {
+ return _tree.begin();
+}
+
+/*
+ * Finding the complete set of parents of a given bucket is not obvious how to
+ * do efficiently, as we only know that the parents are ordered before their
+ * children, but we do not a-priori know if any exist at all. The Judy DB impl
+ * does O(b) explicit point lookups (where b is the number of used bits in the
+ * bucket), starting at the leaf bit and working towards the root. To avoid
+ * having to re-create iterators and perform a full tree search every time, we
+ * turn this on its head and start from the root, progressing towards the leaf.
+ * This allows us to reuse a single iterator and to continue seeking forwards
+ * from its current position.
+ *
+ * To speed up the process of converging on the target bucket without needing
+ * to check many unrelated subtrees, we let the underlying B-tree automatically
+ * aggregate the min/max range of the used-bits of all contained bucket keys.
+ * If we e.g. know that the minimum number of used bits in the DB is 16, we can
+ * immediately seek to this level in the tree instead of working our way down
+ * one bit at a time. By definition, no parents can exist above this level.
+ * This is a very important optimization, as bucket trees are usually very well
+ * balanced due to randomized distribution of data (combined with a cluster-wide
+ * minimum tree level imposed by distribution bits). It is common that the minimum
+ * number of used bits == max number of used bits, i.e. a totally even split.
+ * This means that for a system without inconsistently split buckets (i.e. no
+ * parents) we're highly likely to converge on the target bucket in a single seek.
+ *
+ * Algorithm:
+ *
+ * Core invariant: every subsequent iterator seek performed in this algorithm
+ * is for a key that is strictly higher than the one the iterator is currently at.
+ *
+ * 1. Lbound seek to the lowest key that is known to exclude all already visited
+ * parents. On the first iteration we use a bit count equal to the minimum number
+ * of key used-bits in the entire DB, allowing us to potentially skip most subtrees.
+ * 2. If the current node's key is greater than that of the requested bucket's key,
+ * we've either descended to--or beyond--it in its own subtree or we've entered
+ * a disjoint subtree. Since we know that all parents must sort before any given
+ * child bucket, no more parents may be found at this point. Algorithm terminates.
+ * 3. As the main body of the loop is entered, we know one of following must hold:
+ * 3.1 The current node is an explicitly present parent of our bucket.
+ * 3.2 The current node is contained in a left subtree branch of a parent that
+ * does not have a bucket explicitly present in the tree. It cannot be in
+ * a right subtree of any parent, as that would imply the node is ordered
+ * _after_ our own bucket in an in-order traversal, which would contradict
+ * the check in step 2 above.
+ * 4. If the current node contains the requested bucket, we're at a parent
+ * node of the bucket; add it to the result set.
+ * If this is _not_ the case, we're in a different subtree. Example: the
+ * requested bucket has a key whose MSB is 1 but the first bucket in the
+ * tree has a key with an MSB of 0. Either way we need to update our search
+ * key to home in on the target subtree where more parents may be found;
+ * 5. Update the seek key to find the next possible parent. To ensure this key is
+ * strictly greater than the iterator's current key we find the largest shared
+ * prefix of bits in common between the current node's key and the requested
+ * bucket's key. The prefix length + 1 is then the depth in the tree at which the
+ * two subtrees branch off and diverge.
+ * The new key is then the MSB prefix length + 1 requested bucket's key with a
+ * matching number of used-bits set. Forward lbound-seek the iterator to this key.
+ * `--> TODO elaborate on prefix semantics when they are equal wrt. min used bits
+ * 6. Iff iterator is still valid, go to step 2
+ *
+ * This algorithm is able to skip through large parts of the tree in a sparsely populated
+ * tree, but the number of seeks will trend towards O(b - min_bits) as with the legacy
+ * implementation when a tree is densely populated (where `b` is the used-bits count of the
+ * most specific node in the tree for the target bucket, and min_bits is the minimum number
+ * of used-bits for any key in the database). This because all logical inner nodes in the tree
+ * will have subtrees under them. Even in the worst case we should be more efficient than the
+ * legacy Judy-based implementation since we've cut any dense search space in half for each
+ * invocation of seek() on the iterator.
+ */
+template <typename DataStoreTraitsT>
+template <typename Func>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::BTreeConstIterator
+GenericBTreeBucketDatabase<DataStoreTraitsT>::find_parents_internal(
+ const typename BTree::FrozenView& frozen_view,
+ const BucketId& bucket,
+ Func func) const
+{
+ const uint64_t bucket_key = bucket.toKey();
+ if (frozen_view.empty()) {
+ return frozen_view.begin(); // Will be invalid.
+ }
+ const auto min_db_bits = frozen_view.getAggregated().getMin();
+ assert(min_db_bits >= static_cast<int32_t>(BucketId::minNumBits));
+ assert(min_db_bits <= static_cast<int32_t>(BucketId::maxNumBits));
+ // Start at the lowest possible tree level no parents can exist above,
+ // descending towards the bucket itself.
+ // Note: important to use getId() rather than getRawId(), as min_db_bits may be
+ // greater than the used bits of the queried bucket. If we used the raw ID, we'd
+ // end up looking at undefined bits.
+ const auto first_key = BucketId(min_db_bits, bucket.getId()).toKey();
+ auto iter = frozen_view.lowerBound(first_key);
+ // Try skipping as many levels of the tree as possible as we go.
+ uint32_t bits = min_db_bits;
+ while (iter.valid() && (iter.getKey() < bucket_key)) {
+ auto candidate = BucketId(BucketId::keyToBucketId(iter.getKey()));
+ if (candidate.contains(bucket)) {
+ assert(candidate.getUsedBits() >= bits);
+ func(iter.getKey(), const_value_ref_from_valid_iterator(iter));
+ }
+ bits = next_parent_bit_seek_level(bits, candidate, bucket);
+ const auto parent_key = BucketId(bits, bucket.getRawId()).toKey();
+ assert(parent_key > iter.getKey());
+ iter.seek(parent_key);
+ }
+ return iter;
+}
+
+template <typename DataStoreTraitsT>
+template <typename Func>
+void GenericBTreeBucketDatabase<DataStoreTraitsT>::find_parents_and_self_internal(
+ const typename BTree::FrozenView& frozen_view,
+ const BucketId& bucket,
+ Func func) const
+{
+ auto iter = find_parents_internal(frozen_view, bucket, func);
+ if (iter.valid() && iter.getKey() == bucket.toKey()) {
+ func(iter.getKey(), entry_from_iterator(iter));
+ }
+}
+
+template <typename DataStoreTraitsT>
+template <typename Func>
+void GenericBTreeBucketDatabase<DataStoreTraitsT>::find_parents_and_self(
+ const document::BucketId& bucket,
+ Func func) const
+{
+ auto view = _tree.getFrozenView();
+ find_parents_and_self_internal(view, bucket, std::move(func));
+}
+
+template <typename DataStoreTraitsT>
+template <typename Func>
+void GenericBTreeBucketDatabase<DataStoreTraitsT>::find_parents_self_and_children(
+ const BucketId& bucket,
+ Func func) const
+{
+ auto view = _tree.getFrozenView();
+ auto iter = find_parents_internal(view, bucket, func);
+ // `iter` is already pointing at, or beyond, one of the bucket's subtrees.
+ for (; iter.valid(); ++iter) {
+ auto candidate = BucketId(BucketId::keyToBucketId(iter.getKey()));
+ if (bucket.contains(candidate)) {
+ func(iter.getKey(), entry_from_iterator(iter));
+ } else {
+ break;
+ }
+ }
+}
+
+template <typename DataStoreTraitsT>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::ValueType
+GenericBTreeBucketDatabase<DataStoreTraitsT>::get(const BucketId& bucket) const {
+ return entry_from_iterator(_tree.find(bucket.toKey()));
+}
+
+template <typename DataStoreTraitsT>
+typename GenericBTreeBucketDatabase<DataStoreTraitsT>::ValueType
+GenericBTreeBucketDatabase<DataStoreTraitsT>::get_by_raw_key(uint64_t key) const {
+ return entry_from_iterator(_tree.find(key));
+}
+
+template <typename DataStoreTraitsT>
+bool GenericBTreeBucketDatabase<DataStoreTraitsT>::remove_by_raw_key(uint64_t key) {
+ auto iter = _tree.find(key);
+ if (!iter.valid()) {
+ return false;
+ }
+ const auto value = iter.getData();
+ DataStoreTraitsT::remove_by_wrapped_value(_store, value);
+ _tree.remove(iter);
+ commit_tree_changes();
+ return true;
+}
+
+template <typename DataStoreTraitsT>
+bool GenericBTreeBucketDatabase<DataStoreTraitsT>::remove(const BucketId& bucket) {
+ return remove_by_raw_key(bucket.toKey());
+}
+
+template <typename DataStoreTraitsT>
+bool GenericBTreeBucketDatabase<DataStoreTraitsT>::update_by_raw_key(uint64_t bucket_key,
+ const ValueType& new_entry)
+{
+ const auto new_value = DataStoreTraitsT::wrap_and_store_value(_store, new_entry);
+ auto iter = _tree.lowerBound(bucket_key);
+ const bool pre_existed = (iter.valid() && (iter.getKey() == bucket_key));
+ if (pre_existed) {
+ DataStoreTraitsT::remove_by_wrapped_value(_store, iter.getData());
+ // In-place update of value; does not require tree structure modification
+ std::atomic_thread_fence(std::memory_order_release); // Must ensure visibility when new array ref is observed
+ iter.writeData(new_value);
+ } else {
+ _tree.insert(iter, bucket_key, new_value);
+ }
+ commit_tree_changes(); // TODO does publishing a new root imply an implicit memory fence?
+ return pre_existed;
+}
+
+template <typename DataStoreTraitsT>
+bool GenericBTreeBucketDatabase<DataStoreTraitsT>::update(const BucketId& bucket,
+ const ValueType& new_entry)
+{
+ return update_by_raw_key(bucket.toKey(), new_entry);
+}
+
+// TODO need snapshot read with guarding
+// FIXME semantics of for-each in judy and bit tree DBs differ, former expects lbound, latter ubound..!
+// FIXME but bit-tree code says "lowerBound" in impl and "after" in declaration???
+template <typename DataStoreTraitsT>
+void GenericBTreeBucketDatabase<DataStoreTraitsT>::for_each(EntryProcessor& proc, const BucketId& after) const {
+ for (auto iter = _tree.upperBound(after.toKey()); iter.valid(); ++iter) {
+ // TODO memory fencing once we use snapshots!
+ if (!proc.process(DataStoreTraitsT::unwrap_const_ref_from_key_value(_store, iter.getKey(), iter.getData()))) {
+ break;
+ }
+ }
+}
+
+/*
+ * Returns the bucket ID which, based on the buckets already existing in the DB,
+ * is the most specific location in the tree in which it should reside. This may
+ * or may not be a bucket that already exists.
+ *
+ * Example: if there is a single bucket (1, 1) in the tree, a query for (1, 1) or
+ * (1, 3) will return (1, 1) as that is the most specific leaf in that subtree.
+ * A query for (1, 0) will return (1, 0) even though this doesn't currently exist,
+ * as there is no existing bucket that can contain the queried bucket. It is up to
+ * the caller to create this bucket according to its needs.
+ *
+ * Usually this function will be called with an ID whose used-bits is at max (58), in
+ * order to find a leaf bucket to route an incoming document operation to.
+ *
+ * TODO rename this function, it's very much _not_ obvious what an "appropriate" bucket is..!
+ * TODO this should be possible to do concurrently
+ */
+template <typename DataStoreTraitsT>
+BucketId GenericBTreeBucketDatabase<DataStoreTraitsT>::getAppropriateBucket(uint16_t minBits, const BucketId& bid) const {
+ // The bucket tree is ordered in such a way that it represents a
+ // natural in-order traversal of all buckets, with inner nodes being
+ // visited before leaf nodes. This means that a lower bound seek will
+ // never return a parent of a seeked bucket. The iterator will be pointing
+ // to a bucket that is either the actual bucket given as the argument to
+ // lowerBound() or the next in-order bucket (or end() if none exists).
+ // TODO snapshot
+ auto iter = _tree.lowerBound(bid.toKey());
+ if (iter.valid()) {
+ // Find the first level in the tree where the paths through the bucket tree
+ // diverge for the target bucket and the current bucket.
+ minBits = getMinDiffBits(minBits, bucket_from_valid_iterator(iter), bid);
+ }
+ // TODO is it better to copy original iterator and do begin() on the copy?
+ auto first_iter = _tree.begin();
+ // Original iterator might be in a different subtree than that of our
+ // target bucket. If possible, rewind one node to discover any parent or
+ // leftmost sibling of our node. If there's no such node, we'll still
+ // discover the greatest equal bit prefix.
+ if (iter != first_iter) {
+ --iter;
+ minBits = getMinDiffBits(minBits, bucket_from_valid_iterator(iter), bid);
+ }
+ return BucketId(minBits, bid.getRawId());
+}
+
+/*
+ * Enumerate the number of child subtrees under `bucket`. The value returned is in the
+ * range [0, 2] regardless of how many subtrees are present further down in the tree.
+ *
+ * Finding this number is reasonably straight forward; we construct two buckets that
+ * represent the key ranges for the left and right subtrees under `bucket` and check
+ * if there are any ranges in the tree's keyspace that are contained in these.
+ */
+template <typename DataStoreTraitsT>
+uint32_t GenericBTreeBucketDatabase<DataStoreTraitsT>::child_subtree_count(const BucketId& bucket) const {
+ assert(bucket.getUsedBits() < BucketId::maxNumBits);
+ BucketId lhs_bucket(bucket.getUsedBits() + 1, bucket.getId());
+ BucketId rhs_bucket(bucket.getUsedBits() + 1, (1ULL << bucket.getUsedBits()) | bucket.getId());
+
+ auto iter = _tree.lowerBound(lhs_bucket.toKey());
+ if (!iter.valid()) {
+ return 0;
+ }
+ if (lhs_bucket.contains(bucket_from_valid_iterator(iter))) {
+ iter.seek(rhs_bucket.toKey());
+ if (!iter.valid()) {
+ return 1; // lhs subtree only
+ }
+ return (rhs_bucket.contains(bucket_from_valid_iterator(iter)) ? 2 : 1);
+ } else if (rhs_bucket.contains(bucket_from_valid_iterator(iter))) {
+ return 1; // rhs subtree only
+ }
+ return 0;
+}
+
+template <typename DataStoreTraitsT>
+struct BTreeBuilderMerger final : Merger<typename DataStoreTraitsT::ValueType> {
+ using DBType = GenericBTreeBucketDatabase<DataStoreTraitsT>;
+ using ValueType = typename DataStoreTraitsT::ValueType;
+ using BTreeBuilderType = typename DBType::BTree::Builder;
+
+ DBType& _db;
+ BTreeBuilderType& _builder;
+ uint64_t _current_key;
+ uint64_t _current_value;
+ ValueType _cached_value;
+ bool _valid_cached_value;
+
+ BTreeBuilderMerger(DBType& db, BTreeBuilderType& builder)
+ : _db(db),
+ _builder(builder),
+ _current_key(0),
+ _current_value(0),
+ _cached_value(),
+ _valid_cached_value(false)
+ {}
+ ~BTreeBuilderMerger() override = default;
+
+ uint64_t bucket_key() const noexcept override {
+ return _current_key;
+ }
+ BucketId bucket_id() const noexcept override {
+ return BucketId(BucketId::keyToBucketId(_current_key));
+ }
+ ValueType& current_entry() override {
+ if (!_valid_cached_value) {
+ _cached_value = DataStoreTraitsT::unwrap_from_key_value(_db.store(), _current_key, _current_value);
+ _valid_cached_value = true;
+ }
+ return _cached_value;
+ }
+ void insert_before_current(const BucketId& bucket_id, const ValueType& e) override {
+ const uint64_t bucket_key = bucket_id.toKey();
+ assert(bucket_key < _current_key);
+ const auto new_value = DataStoreTraitsT::wrap_and_store_value(_db.store(), e);
+ _builder.insert(bucket_key, new_value);
+ }
+
+ void update_iteration_state(uint64_t key, uint64_t value) {
+ _current_key = key;
+ _current_value = value;
+ _valid_cached_value = false;
+ }
+};
+
+template <typename DataStoreTraitsT>
+struct BTreeTrailingInserter final : TrailingInserter<typename DataStoreTraitsT::ValueType> {
+ using DBType = GenericBTreeBucketDatabase<DataStoreTraitsT>;
+ using ValueType = typename DataStoreTraitsT::ValueType;
+ using BTreeBuilderType = typename DBType::BTree::Builder;
+
+ DBType& _db;
+ BTreeBuilderType& _builder;
+
+ BTreeTrailingInserter(DBType& db, BTreeBuilderType& builder)
+ : _db(db),
+ _builder(builder)
+ {}
+
+ ~BTreeTrailingInserter() override = default;
+
+ void insert_at_end(const BucketId& bucket_id, const ValueType& e) override {
+ const uint64_t bucket_key = bucket_id.toKey();
+ const auto new_value = DataStoreTraitsT::wrap_and_store_value(_db.store(), e);
+ _builder.insert(bucket_key, new_value);
+ }
+};
+
+// TODO lbound arg?
+template <typename DataStoreTraitsT>
+void GenericBTreeBucketDatabase<DataStoreTraitsT>::merge(MergingProcessor<ValueType>& proc) {
+ typename BTree::Builder builder(_tree.getAllocator());
+ BTreeBuilderMerger<DataStoreTraitsT> merger(*this, builder);
+
+ // TODO for_each instead?
+ for (auto iter = _tree.begin(); iter.valid(); ++iter) {
+ const uint64_t key = iter.getKey();
+ const uint64_t value = iter.getData();
+ merger.update_iteration_state(key, value);
+
+ auto result = proc.merge(merger);
+
+ if (result == MergingProcessor<ValueType>::Result::KeepUnchanged) {
+ builder.insert(key, value); // Reuse array store ref with no changes
+ } else if (result == MergingProcessor<ValueType>::Result::Update) {
+ assert(merger._valid_cached_value); // Must actually have been touched
+ assert(merger._cached_value.valid());
+ DataStoreTraitsT::remove_by_wrapped_value(_store, value);
+ const auto new_value = DataStoreTraitsT::wrap_and_store_value(_store, merger._cached_value);
+ builder.insert(key, new_value);
+ } else if (result == MergingProcessor<ValueType>::Result::Skip) {
+ DataStoreTraitsT::remove_by_wrapped_value(_store, value);
+ } else {
+ abort();
+ }
+ }
+ BTreeTrailingInserter<DataStoreTraitsT> inserter(*this, builder);
+ proc.insert_remaining_at_end(inserter);
+
+ _tree.assign(builder);
+ commit_tree_changes();
+}
+
+
+}
diff --git a/storage/src/vespa/storage/bucketdb/lockablemap.h b/storage/src/vespa/storage/bucketdb/lockablemap.h
index 8b4e403b899..83ecac0a94f 100644
--- a/storage/src/vespa/storage/bucketdb/lockablemap.h
+++ b/storage/src/vespa/storage/bucketdb/lockablemap.h
@@ -14,6 +14,7 @@
*/
#pragma once
+#include "abstract_bucket_map.h"
#include <map>
#include <vespa/vespalib/util/printable.h>
#include <vespa/vespalib/stllike/hash_map.h>
@@ -26,98 +27,19 @@
namespace storage {
-template<typename Map>
-class LockableMap : public vespalib::Printable
+template <typename Map>
+class LockableMap
+ : public bucketdb::AbstractBucketMap<typename Map::mapped_type>
{
public:
- typedef typename Map::key_type key_type;
- typedef typename Map::mapped_type mapped_type;
- typedef typename Map::value_type value_type;
- typedef typename Map::size_type size_type;
- using BucketId = document::BucketId;
- struct WrappedEntry;
-
- /** Responsible for releasing lock in map when out of scope. */
- class LockKeeper {
- friend struct WrappedEntry;
- LockableMap<Map>& _map;
- key_type _key;
- bool _locked;
-
- LockKeeper(LockableMap<Map>& map, key_type key)
- : _map(map), _key(key), _locked(true) {}
- void unlock() { _map.unlock(_key); _locked = false;}
- public:
- ~LockKeeper() { if (_locked) unlock(); }
- };
-
- struct WrappedEntry {
- WrappedEntry() : _exists(false), _lockKeeper(), _value() {}
- WrappedEntry(WrappedEntry &&) = default;
- WrappedEntry & operator = (WrappedEntry &&) = default;
- ~WrappedEntry() { }
-
- mapped_type* operator->() { return &_value; }
- const mapped_type* operator->() const { return &_value; }
- mapped_type& operator*() { return _value; }
- const mapped_type& operator*() const { return _value; }
-
- const mapped_type *get() const { return &_value; }
- mapped_type *get() { return &_value; }
-
- void write();
- void remove();
- void unlock();
- bool exist() const { return _exists; }
- bool preExisted() const { return _preExisted; }
- bool locked() const { return _lockKeeper.get(); }
- const key_type& getKey() const { return _lockKeeper->_key; };
-
- BucketId getBucketId() const {
- return BucketId(BucketId::keyToBucketId(getKey()));
- }
-
- protected:
- WrappedEntry(LockableMap<Map>& map,
- const key_type& key, const mapped_type& val,
- const char* clientId, bool preExisted_)
- : _exists(true),
- _preExisted(preExisted_),
- _lockKeeper(new LockKeeper(map, key)),
- _value(val),
- _clientId(clientId) {}
- WrappedEntry(LockableMap<Map>& map, const key_type& key,
- const char* clientId)
- : _exists(false),
- _preExisted(false),
- _lockKeeper(new LockKeeper(map, key)),
- _value(),
- _clientId(clientId) {}
-
- bool _exists;
- bool _preExisted;
- std::unique_ptr<LockKeeper> _lockKeeper;
- mapped_type _value;
- const char* _clientId;
- friend class LockableMap<Map>;
- };
-
- struct LockId {
- key_type _key;
- const char* _owner;
-
- LockId() : _key(0), _owner("none - empty token") {}
- LockId(key_type key, const char* owner)
- : _key(key), _owner(owner)
- {
- assert(_owner != 0);
- }
-
- size_t hash() const { return _key; }
- size_t operator%(size_t val) const { return _key % val; }
- bool operator==(const LockId& id) const { return (_key == id._key); }
- operator key_type() const { return _key; }
- };
+ using ParentType = bucketdb::AbstractBucketMap<typename Map::mapped_type>;
+ using WrappedEntry = typename ParentType::WrappedEntry;
+ using key_type = typename ParentType::key_type;
+ using mapped_type = typename ParentType::mapped_type;
+ using LockId = typename ParentType::LockId;
+ using EntryMap = typename ParentType::EntryMap;
+ using Decision = typename ParentType::Decision;
+ using BucketId = document::BucketId;
LockableMap();
~LockableMap();
@@ -126,14 +48,20 @@ public:
return ! (*this == other);
}
bool operator<(const LockableMap& other) const;
- typename Map::size_type size() const;
- size_type getMemoryUsage() const;
- bool empty() const;
+ size_t size() const noexcept override;
+ size_t getMemoryUsage() const noexcept override;
+ bool empty() const noexcept override;
void swap(LockableMap&);
- WrappedEntry get(const key_type& key, const char* clientId,
- bool createIfNonExisting = false,
- bool lockIfNonExistingAndNotCreating = false);
+ WrappedEntry get(const key_type& key, const char* clientId, bool createIfNonExisting) override;
+ WrappedEntry get(const key_type& key, const char* clientId) {
+ return get(key, clientId, false);
+ }
+
+ bool erase(const key_type& key, const char* clientId, bool haslock) override;
+ void insert(const key_type& key, const mapped_type& value,
+ const char* clientId, bool haslock, bool& preExisted) override;
+
bool erase(const key_type& key, const char* clientId)
{ return erase(key, clientId, false); }
void insert(const key_type& key, const mapped_type& value,
@@ -141,42 +69,6 @@ public:
{ return insert(key, value, clientId, false, preExisted); }
void clear();
- enum Decision { ABORT, UPDATE, REMOVE, CONTINUE, DECISION_COUNT };
-
- template<typename Functor>
- void each(Functor& functor, const char* clientId,
- const key_type& first = key_type(),
- const key_type& last = key_type() - 1 );
-
- template<typename Functor>
- void each(const Functor& functor, const char* clientId,
- const key_type& first = key_type(),
- const key_type& last = key_type() - 1 );
-
- template<typename Functor>
- void all(Functor& functor, const char* clientId,
- const key_type& first = key_type(),
- const key_type& last = key_type()-1);
-
- template<typename Functor>
- void all(const Functor& functor, const char* clientId,
- const key_type& first = key_type(),
- const key_type& last = key_type() - 1 );
-
- static constexpr uint32_t DEFAULT_CHUNK_SIZE = 1000;
-
- /**
- * Iterate over the entire database contents, holding the global database
- * mutex for `chunkSize` processed entries at a time, yielding the current
- * thread between each such such to allow other threads to get a chance
- * at acquiring a bucket lock.
- */
- template <typename Functor>
- void chunkedAll(Functor& functor,
- const char* clientId,
- vespalib::duration yieldTime = 10us,
- uint32_t chunkSize = DEFAULT_CHUNK_SIZE);
-
void print(std::ostream& out, bool verbose, const std::string& indent) const override;
/**
@@ -184,18 +76,13 @@ public:
* bucket. Usually, there should be only one such bucket, but in the case
* of inconsistent splitting, there may be more than one.
*/
- std::map<BucketId, WrappedEntry>
- getContained(const BucketId& bucketId, const char* clientId);
-
- typedef std::map<BucketId, WrappedEntry> EntryMap;
+ EntryMap getContained(const BucketId& bucketId, const char* clientId) override;
/**
* Returns all buckets in the bucket database that can contain the given
* bucket, and all buckets that that bucket contains.
- *
- * If sibling is != 0, also fetch that bucket if possible.
*/
- EntryMap getAll(const BucketId& bucketId, const char* clientId, const BucketId& sibling = BucketId(0));
+ EntryMap getAll(const BucketId& bucketId, const char* clientId) override;
/**
* Returns true iff bucket has no superbuckets or sub-buckets in the
@@ -203,9 +90,9 @@ public:
* bucket to become inconsistent will require taking its lock, so by
* requiring the lock to be provided here we avoid race conditions.
*/
- bool isConsistent(const WrappedEntry& entry);
+ bool isConsistent(const WrappedEntry& entry) override;
- void showLockClients(vespalib::asciistream & out) const;
+ void showLockClients(vespalib::asciistream & out) const override;
private:
struct hasher {
@@ -217,7 +104,7 @@ private:
LockIdSet();
~LockIdSet();
void print(std::ostream& out, bool verbose, const std::string& indent) const;
- bool exist(const LockId & lid) const { return this->find(lid) != Hash::end(); }
+ bool exist(const LockId& lid) const { return this->find(lid) != Hash::end(); }
size_t getMemoryUsage() const;
};
@@ -243,15 +130,27 @@ private:
LockIdSet _lockedKeys;
LockWaiters _lockWaiters;
- bool erase(const key_type& key, const char* clientId, bool haslock);
- void insert(const key_type& key, const mapped_type& value,
- const char* clientId, bool haslock, bool& preExisted);
- void unlock(const key_type& key);
+ void unlock(const key_type& key) override;
bool findNextKey(key_type& key, mapped_type& val, const char* clientId,
std::unique_lock<std::mutex> &guard);
bool handleDecision(key_type& key, mapped_type& val, Decision decision);
void acquireKey(const LockId & lid, std::unique_lock<std::mutex> &guard);
+ void do_for_each_mutable(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last) override;
+
+ void do_for_each(std::function<Decision(uint64_t, const mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last) override;
+
+ void do_for_each_chunked(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* clientId,
+ vespalib::duration yieldTime,
+ uint32_t chunkSize) override;
+
/**
* Process up to `chunkSize` bucket database entries from--and possibly
* including--the bucket pointed to by `key`.
@@ -263,17 +162,15 @@ private:
* Modifies `key` in-place to point to the next key to process for the next
* invocation of this function.
*/
- template <typename Functor>
- bool processNextChunk(Functor& functor,
+ bool processNextChunk(std::function<Decision(uint64_t, mapped_type&)>& func,
key_type& key,
const char* clientId,
- const uint32_t chunkSize);
+ uint32_t chunkSize);
/**
* Returns the given bucket, its super buckets and its sub buckets.
*/
void getAllWithoutLocking(const BucketId& bucket,
- const BucketId& sibling,
std::vector<BucketId::Type>& keys);
/**
diff --git a/storage/src/vespa/storage/bucketdb/lockablemap.hpp b/storage/src/vespa/storage/bucketdb/lockablemap.hpp
index 2ca2183ae26..fcdde0a810c 100644
--- a/storage/src/vespa/storage/bucketdb/lockablemap.hpp
+++ b/storage/src/vespa/storage/bucketdb/lockablemap.hpp
@@ -16,7 +16,7 @@ template<typename Map>
LockableMap<Map>::LockIdSet::LockIdSet() : Hash() { }
template<typename Map>
-LockableMap<Map>::LockIdSet::~LockIdSet() { }
+LockableMap<Map>::LockIdSet::~LockIdSet() = default;
template<typename Map>
size_t
@@ -28,7 +28,7 @@ template<typename Map>
LockableMap<Map>::LockWaiters::LockWaiters() : _id(0), _map() { }
template<typename Map>
-LockableMap<Map>::LockWaiters::~LockWaiters() { }
+LockableMap<Map>::LockWaiters::~LockWaiters() = default;
template<typename Map>
size_t
@@ -39,35 +39,6 @@ LockableMap<Map>::LockWaiters::insert(const LockId & lid) {
}
template<typename Map>
-void
-LockableMap<Map>::WrappedEntry::write()
-{
- assert(_lockKeeper->_locked);
- assert(_value.verifyLegal());
- bool b;
- _lockKeeper->_map.insert(_lockKeeper->_key, _value, _clientId, true, b);
- _lockKeeper->unlock();
-}
-
-template<typename Map>
-void
-LockableMap<Map>::WrappedEntry::remove()
-{
- assert(_lockKeeper->_locked);
- assert(_exists);
- _lockKeeper->_map.erase(_lockKeeper->_key, _clientId, true);
- _lockKeeper->unlock();
-}
-
-template<typename Map>
-void
-LockableMap<Map>::WrappedEntry::unlock()
-{
- assert(_lockKeeper->_locked);
- _lockKeeper->unlock();
-}
-
-template<typename Map>
LockableMap<Map>::LockableMap()
: _map(),
_lock(),
@@ -77,7 +48,7 @@ LockableMap<Map>::LockableMap()
{}
template<typename Map>
-LockableMap<Map>::~LockableMap() {}
+LockableMap<Map>::~LockableMap() = default;
template<typename Map>
bool
@@ -98,16 +69,16 @@ LockableMap<Map>::operator<(const LockableMap<Map>& other) const
}
template<typename Map>
-typename Map::size_type
-LockableMap<Map>::size() const
+size_t
+LockableMap<Map>::size() const noexcept
{
std::lock_guard<std::mutex> guard(_lock);
return _map.size();
}
template<typename Map>
-typename Map::size_type
-LockableMap<Map>::getMemoryUsage() const
+size_t
+LockableMap<Map>::getMemoryUsage() const noexcept
{
std::lock_guard<std::mutex> guard(_lock);
return _map.getMemoryUsage() + _lockedKeys.getMemoryUsage() +
@@ -116,7 +87,7 @@ LockableMap<Map>::getMemoryUsage() const
template<typename Map>
bool
-LockableMap<Map>::empty() const
+LockableMap<Map>::empty() const noexcept
{
std::lock_guard<std::mutex> guard(_lock);
return _map.empty();
@@ -145,9 +116,7 @@ void LockableMap<Map>::acquireKey(const LockId & lid, std::unique_lock<std::mute
template<typename Map>
typename LockableMap<Map>::WrappedEntry
-LockableMap<Map>::get(const key_type& key, const char* clientId,
- bool createIfNonExisting,
- bool lockIfNonExistingAndNotCreating)
+LockableMap<Map>::get(const key_type& key, const char* clientId, bool createIfNonExisting)
{
LockId lid(key, clientId);
std::unique_lock<std::mutex> guard(_lock);
@@ -157,71 +126,34 @@ LockableMap<Map>::get(const key_type& key, const char* clientId,
_map.find(key, createIfNonExisting, preExisted);
if (it == _map.end()) {
- if (lockIfNonExistingAndNotCreating) {
- return WrappedEntry(*this, key, clientId);
- } else {
- return WrappedEntry();
- }
+ return WrappedEntry();
}
_lockedKeys.insert(lid);
return WrappedEntry(*this, key, it->second, clientId, preExisted);
}
-#ifdef ENABLE_BUCKET_OPERATION_LOGGING
-
-namespace bucketdb {
-struct StorageBucketInfo;
-struct BucketInfo;
-}
-
-namespace debug {
-
-template <typename T> struct TypeTag {};
-// Storage
-void logBucketDbInsert(uint64_t key, const bucketdb::StorageBucketInfo& entry);
-void logBucketDbErase(uint64_t key, const TypeTag<bucketdb::StorageBucketInfo>&);
-
-// Distributor
-void logBucketDbInsert(uint64_t key, const bucketdb::BucketInfo& entry);
-void logBucketDbErase(uint64_t key, const TypeTag<bucketdb::BucketInfo>&);
-
-template <typename DummyValue>
-inline void logBucketDbErase(uint64_t, const TypeTag<DummyValue>&) {}
-template <typename DummyKey, typename DummyValue>
-inline void logBucketDbInsert(const DummyKey&, const DummyValue&) {}
-
-}
-
-#endif // ENABLE_BUCKET_OPERATION_LOGGING
-
template<typename Map>
bool
-LockableMap<Map>::erase(const key_type& key, const char* clientId, bool haslock)
+LockableMap<Map>::erase(const key_type& key, const char* client_id, bool has_lock)
{
- LockId lid(key, clientId);
+ LockId lid(key, client_id);
std::unique_lock<std::mutex> guard(_lock);
- if (!haslock) {
+ if (!has_lock) {
acquireKey(lid, guard);
}
-#ifdef ENABLE_BUCKET_OPERATION_LOGGING
- debug::logBucketDbErase(key, debug::TypeTag<mapped_type>());
-#endif
return _map.erase(key);
}
template<typename Map>
void
LockableMap<Map>::insert(const key_type& key, const mapped_type& value,
- const char* clientId, bool haslock, bool& preExisted)
+ const char* client_id, bool has_lock, bool& preExisted)
{
- LockId lid(key, clientId);
+ LockId lid(key, client_id);
std::unique_lock<std::mutex> guard(_lock);
- if (!haslock) {
+ if (!has_lock) {
acquireKey(lid, guard);
}
-#ifdef ENABLE_BUCKET_OPERATION_LOGGING
- debug::logBucketDbInsert(key, value);
-#endif
_map.insert(key, value, preExisted);
}
@@ -242,12 +174,14 @@ LockableMap<Map>::findNextKey(key_type& key, mapped_type& val,
// Wait for next value to unlock.
typename Map::iterator it(_map.lower_bound(key));
while (it != _map.end() && _lockedKeys.exist(LockId(it->first, ""))) {
- typename LockWaiters::Key waitId(_lockWaiters.insert(LockId(it->first, clientId)));
+ auto wait_id = _lockWaiters.insert(LockId(it->first, clientId));
_cond.wait(guard);
- _lockWaiters.erase(waitId);
+ _lockWaiters.erase(wait_id);
it = _map.lower_bound(key);
}
- if (it == _map.end()) return true;
+ if (it == _map.end()) {
+ return true;
+ }
key = it->first;
val = it->second;
return false;
@@ -260,127 +194,60 @@ LockableMap<Map>::handleDecision(key_type& key, mapped_type& val,
{
bool b;
switch (decision) {
- case UPDATE: _map.insert(key, val, b);
- break;
- case REMOVE: _map.erase(key);
- break;
- case ABORT: return true;
- case CONTINUE: break;
- default:
- HDR_ABORT("should not be reached");
+ case Decision::UPDATE:
+ _map.insert(key, val, b);
+ break;
+ case Decision::REMOVE:
+ _map.erase(key);
+ break;
+ case Decision::ABORT:
+ return true;
+ case Decision::CONTINUE:
+ break;
+ default:
+ HDR_ABORT("should not be reached");
}
return false;
}
template<typename Map>
-template<typename Functor>
-void
-LockableMap<Map>::each(Functor& functor, const char* clientId,
- const key_type& first, const key_type& last)
-{
- key_type key = first;
- mapped_type val;
- Decision decision;
- {
- std::unique_lock<std::mutex> guard(_lock);
- if (findNextKey(key, val, clientId, guard) || key > last) return;
- _lockedKeys.insert(LockId(key, clientId));
- }
- try{
- while (true) {
- decision = functor(const_cast<const key_type&>(key), val);
- std::unique_lock<std::mutex> guard(_lock);
- _lockedKeys.erase(LockId(key, clientId));
- _cond.notify_all();
- if (handleDecision(key, val, decision)) return;
- ++key;
- if (findNextKey(key, val, clientId, guard) || key > last) return;
- _lockedKeys.insert(LockId(key, clientId));
- }
- } catch (...) {
- // Assuming only the functor call can throw exceptions, we need
- // to unlock the current key before exiting
- std::lock_guard<std::mutex> guard(_lock);
- _lockedKeys.erase(LockId(key, clientId));
- _cond.notify_all();
- throw;
- }
-}
-
-template<typename Map>
-template<typename Functor>
-void
-LockableMap<Map>::each(const Functor& functor, const char* clientId,
- const key_type& first, const key_type& last)
-{
- key_type key = first;
- mapped_type val;
- Decision decision;
- {
- std::unique_lock<std::mutex> guard(_lock);
- if (findNextKey(key, val, clientId, guard) || key > last) return;
- _lockedKeys.insert(LockId(key, clientId));
- }
- try{
- while (true) {
- decision = functor(const_cast<const key_type&>(key), val);
- std::unique_lock<std::mutex> guard(_lock);
- _lockedKeys.erase(LockId(key, clientId));
- _cond.notify_all();
- if (handleDecision(key, val, decision)) return;
- ++key;
- if (findNextKey(key, val, clientId, guard) || key > last) return;
- _lockedKeys.insert(LockId(key, clientId));
- }
- } catch (...) {
- // Assuming only the functor call can throw exceptions, we need
- // to unlock the current key before exiting
- std::lock_guard<std::mutex> guard(_lock);
- _lockedKeys.erase(LockId(key, clientId));
- _cond.notify_all();
- throw;
- }
-}
-
-template<typename Map>
-template<typename Functor>
-void
-LockableMap<Map>::all(Functor& functor, const char* clientId,
- const key_type& first, const key_type& last)
+void LockableMap<Map>::do_for_each_mutable(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last)
{
key_type key = first;
mapped_type val;
std::unique_lock<std::mutex> guard(_lock);
while (true) {
if (findNextKey(key, val, clientId, guard) || key > last) return;
- Decision d(functor(const_cast<const key_type&>(key), val));
+ Decision d(func(const_cast<const key_type&>(key), val));
if (handleDecision(key, val, d)) return;
++key;
}
}
template<typename Map>
-template<typename Functor>
-void
-LockableMap<Map>::all(const Functor& functor, const char* clientId,
- const key_type& first, const key_type& last)
+void LockableMap<Map>::do_for_each(std::function<Decision(uint64_t, const mapped_type&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last)
{
key_type key = first;
mapped_type val;
std::unique_lock<std::mutex> guard(_lock);
while (true) {
if (findNextKey(key, val, clientId, guard) || key > last) return;
- Decision d(functor(const_cast<const key_type&>(key), val));
- assert(d == ABORT || d == CONTINUE);
+ Decision d(func(const_cast<const key_type&>(key), val));
+ assert(d == Decision::ABORT || d == Decision::CONTINUE);
if (handleDecision(key, val, d)) return;
++key;
}
}
template <typename Map>
-template <typename Functor>
bool
-LockableMap<Map>::processNextChunk(Functor& functor,
+LockableMap<Map>::processNextChunk(std::function<Decision(uint64_t, mapped_type&)>& func,
key_type& key,
const char* clientId,
const uint32_t chunkSize)
@@ -391,7 +258,7 @@ LockableMap<Map>::processNextChunk(Functor& functor,
if (findNextKey(key, val, clientId, guard)) {
return false;
}
- Decision d(functor(const_cast<const key_type&>(key), val));
+ Decision d(func(const_cast<const key_type&>(key), val));
if (handleDecision(key, val, d)) {
return false;
}
@@ -401,15 +268,13 @@ LockableMap<Map>::processNextChunk(Functor& functor,
}
template <typename Map>
-template <typename Functor>
-void
-LockableMap<Map>::chunkedAll(Functor& functor,
- const char* clientId,
- vespalib::duration yieldTime,
- uint32_t chunkSize)
+void LockableMap<Map>::do_for_each_chunked(std::function<Decision(uint64_t, mapped_type&)> func,
+ const char* clientId,
+ vespalib::duration yieldTime,
+ uint32_t chunkSize)
{
key_type key{};
- while (processNextChunk(functor, key, clientId, chunkSize)) {
+ while (processNextChunk(func, key, clientId, chunkSize)) {
// Rationale: delay iteration for as short a time as possible while
// allowing another thread blocked on the main DB mutex to acquire it
// in the meantime. Simply yielding the thread does not have the
@@ -591,7 +456,7 @@ LockableMap<Map>::addAndLockResults(
uint8_t getMinDiffBits(uint16_t minBits, const document::BucketId& a, const document::BucketId& b);
template<typename Map>
-std::map<document::BucketId, typename LockableMap<Map>::WrappedEntry>
+typename LockableMap<Map>::EntryMap
LockableMap<Map>::getContained(const BucketId& bucket,
const char* clientId)
{
@@ -626,7 +491,6 @@ LockableMap<Map>::getContained(const BucketId& bucket,
template<typename Map>
void
LockableMap<Map>::getAllWithoutLocking(const BucketId& bucket,
- const BucketId& sibling,
std::vector<BucketId::Type>& keys)
{
BucketId result;
@@ -674,26 +538,21 @@ LockableMap<Map>::getAllWithoutLocking(const BucketId& bucket,
break;
}
}
-
- if (sibling.getRawId() != 0) {
- keys.push_back(sibling.toKey());
- }
}
/**
* Returns the given bucket, its super buckets and its sub buckets.
*/
template<typename Map>
-std::map<document::BucketId, typename LockableMap<Map>::WrappedEntry>
-LockableMap<Map>::getAll(const BucketId& bucket, const char* clientId,
- const BucketId& sibling)
+typename LockableMap<Map>::EntryMap
+LockableMap<Map>::getAll(const BucketId& bucket, const char* clientId)
{
std::unique_lock<std::mutex> guard(_lock);
std::map<BucketId, WrappedEntry> results;
std::vector<BucketId::Type> keys;
- getAllWithoutLocking(bucket, sibling, keys);
+ getAllWithoutLocking(bucket, keys);
addAndLockResults(keys, clientId, results, guard);
@@ -706,10 +565,9 @@ LockableMap<Map>::isConsistent(const typename LockableMap<Map>::WrappedEntry& en
{
std::lock_guard<std::mutex> guard(_lock);
- BucketId sibling(0);
std::vector<BucketId::Type> keys;
- getAllWithoutLocking(entry.getBucketId(), sibling, keys);
+ getAllWithoutLocking(entry.getBucketId(), keys);
assert(keys.size() >= 1);
assert(keys.size() != 1 || keys[0] == entry.getKey());
diff --git a/storage/src/vespa/storage/bucketdb/stdmapwrapper.h b/storage/src/vespa/storage/bucketdb/stdmapwrapper.h
deleted file mode 100644
index 889227f1747..00000000000
--- a/storage/src/vespa/storage/bucketdb/stdmapwrapper.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-/**
- * @class StdMapWrapper
- * @ingroup bucketdb
- *
- * @brief Wrapper for std::map to add functionality in JudyMultiMap.
- *
- * To remove the need for partial template specialization in lockablemap
- */
-
-#pragma once
-
-#include <map>
-#include <vespa/vespalib/util/printable.h>
-#include <ostream>
-
-namespace storage {
-
-template<typename Key, typename Value>
-class StdMapWrapper : public std::map<Key, Value>,
- public vespalib::Printable
-{
-public:
- StdMapWrapper() {}
-
- virtual void print(std::ostream& out, bool verbose,
- const std::string& indent) const;
-
- typename std::map<Key, Value>::iterator find(Key key);
-
- typename std::map<Key, Value>::iterator find(Key key, bool insert, bool&);
-
- void insert(Key key, const Value& val, bool&);
-
- uint32_t getMemoryUsage() const;
-};
-
-template<class Key, class Value>
-uint32_t
-StdMapWrapper<Key, Value>::getMemoryUsage() const
-{
- Value val;
-
- return (32 + sizeof(val)) * this->size();
-}
-
-template<class Key, class Value>
-void
-StdMapWrapper<Key, Value>::print(std::ostream& out,
- bool,
- const std::string& indent) const
-{
- out << "StdMapWrapper(";
- for (typename std::map<Key, Value>::const_iterator i = this->begin();
- i != this->end(); ++i)
- {
- out << "\n" << indent << " " << "Key: " << i->first << ", Value: "
- << i->second;
- }
- out << ")";
-}
-
-template<class Key, class Value>
-inline typename std::map<Key, Value>::iterator
-StdMapWrapper<Key, Value>::
-find(Key key)
-{
- bool tmp;
- return find(key, false, tmp);
-}
-
-template<class Key, class Value>
-inline typename std::map<Key, Value>::iterator
-StdMapWrapper<Key, Value>::
-find(Key key, bool insertIfNonExisting, bool&)
-{
- if (insertIfNonExisting) {
- std::pair<typename std::map<Key, Value>::iterator, bool> result
- = std::map<Key, Value>::insert(std::pair<Key, Value>(key, Value()));
- return result.first;
- } else {
- return std::map<Key, Value>::find(key);
- }
-}
-
-template<class Key, class Value>
-void
-StdMapWrapper<Key, Value>::
-insert(Key key, const Value& val, bool&)
-{
- this->operator[](key) = val;
-}
-
-}
-
diff --git a/storage/src/vespa/storage/bucketdb/storagebucketdbinitializer.cpp b/storage/src/vespa/storage/bucketdb/storagebucketdbinitializer.cpp
index 84352df5ec9..ed83ad268e5 100644
--- a/storage/src/vespa/storage/bucketdb/storagebucketdbinitializer.cpp
+++ b/storage/src/vespa/storage/bucketdb/storagebucketdbinitializer.cpp
@@ -462,13 +462,13 @@ namespace {
_next(), _alreadySet(0) {}
StorBucketDatabase::Decision operator()(
- uint64_t revBucket, StorBucketDatabase::Entry& entry)
+ uint64_t revBucket, const StorBucketDatabase::Entry& entry)
{
BucketId bucket(BucketId::keyToBucketId(revBucket));
if (bucket == _iterator) {
//LOG(spam, "Ignoring bucket %s as it has value of current "
// "iterator", bucket.toString().c_str());
- return StorBucketDatabase::CONTINUE;
+ return StorBucketDatabase::Decision::CONTINUE;
}
_iterator = bucket;
if (entry.disk != _disk) {
@@ -487,10 +487,10 @@ namespace {
LOG(spam, "Aborting iterating for disk %u as we have "
"enough results. Leaving iterator at %s",
uint32_t(_disk), _iterator.toString().c_str());
- return StorBucketDatabase::ABORT;
+ return StorBucketDatabase::Decision::ABORT;
}
}
- return StorBucketDatabase::CONTINUE;
+ return StorBucketDatabase::Decision::CONTINUE;
}
};
}
@@ -513,9 +513,10 @@ StorageBucketDBInitializer::sendReadBucketInfo(spi::PartitionId disk, document::
NextBucketOnDiskFinder finder(disk, state._databaseIterator, count);
LOG(spam, "Iterating bucket db further. Starting at iterator %s",
state._databaseIterator.toString().c_str());
- _system.getBucketDatabase(bucketSpace).all(finder,
- "StorageBucketDBInitializer::readBucketInfo",
- state._databaseIterator.stripUnused().toKey());
+ _system.getBucketDatabase(bucketSpace).for_each(
+ std::ref(finder),
+ "StorageBucketDBInitializer::readBucketInfo",
+ state._databaseIterator.stripUnused().toKey());
if (finder._alreadySet > 0) {
_metrics._infoSetByLoad.inc(finder._alreadySet);
_state._infoSetByLoad += finder._alreadySet;
diff --git a/storage/src/vespa/storage/bucketdb/storbucketdb.cpp b/storage/src/vespa/storage/bucketdb/storbucketdb.cpp
index df9e8e4e5b5..7066ea115fd 100644
--- a/storage/src/vespa/storage/bucketdb/storbucketdb.cpp
+++ b/storage/src/vespa/storage/bucketdb/storbucketdb.cpp
@@ -1,11 +1,15 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "storbucketdb.h"
+#include "btree_lockable_map.h"
#include "judymultimap.hpp"
+#include "lockablemap.h"
#include <vespa/log/log.h>
LOG_SETUP(".storage.bucketdb.stor_bucket_db");
+using document::BucketId;
+
namespace storage {
namespace bucketdb {
@@ -16,15 +20,15 @@ print(std::ostream& out, bool, const std::string&) const
out << info << ", disk " << disk;
}
-bool StorageBucketInfo::operator == (const StorageBucketInfo & b) const {
+bool StorageBucketInfo::operator==(const StorageBucketInfo& b) const {
return disk == b.disk;
}
-bool StorageBucketInfo::operator != (const StorageBucketInfo & b) const {
+bool StorageBucketInfo::operator!=(const StorageBucketInfo& b) const {
return !(*this == b);
}
-bool StorageBucketInfo::operator < (const StorageBucketInfo & b) const {
+bool StorageBucketInfo::operator<(const StorageBucketInfo& b) const {
return disk < b.disk;
}
@@ -34,8 +38,25 @@ operator<<(std::ostream& out, const StorageBucketInfo& info) {
return out;
}
+namespace {
+
+std::unique_ptr<AbstractBucketMap<StorageBucketInfo>> make_legacy_db_impl() {
+ return std::make_unique<LockableMap<JudyMultiMap<StorageBucketInfo>>>();
+}
+
+std::unique_ptr<AbstractBucketMap<StorageBucketInfo>> make_btree_db_impl() {
+ return std::make_unique<BTreeLockableMap<StorageBucketInfo>>();
+}
+
+}
+
} // bucketdb
+StorBucketDatabase::StorBucketDatabase(bool use_btree_db)
+ : _impl(use_btree_db ? bucketdb::make_btree_db_impl()
+ : bucketdb::make_legacy_db_impl())
+{}
+
void
StorBucketDatabase::insert(const document::BucketId& bucket,
const bucketdb::StorageBucketInfo& entry,
@@ -43,26 +64,14 @@ StorBucketDatabase::insert(const document::BucketId& bucket,
{
assert(entry.disk != 0xff);
bool preExisted;
-#if __WORDSIZE == 64
- return LockableMap<JudyMultiMap<Entry> >::insert(
- bucket.toKey(), entry, clientId, preExisted);
-#else
- return LockableMap<StdMapWrapper<document::BucketId::Type, Entry> >::insert(
- bucket.toKey(), entry, clientId, preExisted);
-#endif
+ return _impl->insert(bucket.toKey(), entry, clientId, false, preExisted);
}
bool
StorBucketDatabase::erase(const document::BucketId& bucket,
const char* clientId)
{
-#if __WORDSIZE == 64
- return LockableMap<JudyMultiMap<Entry> >::erase(
- bucket.stripUnused().toKey(), clientId);
-#else
- return LockableMap<StdMapWrapper<document::BucketId::Type, Entry> >::erase(
- bucket.stripUnused().toKey(), clientId);
-#endif
+ return _impl->erase(bucket.stripUnused().toKey(), clientId, false);
}
StorBucketDatabase::WrappedEntry
@@ -71,16 +80,60 @@ StorBucketDatabase::get(const document::BucketId& bucket,
Flag flags)
{
bool createIfNonExisting = (flags & CREATE_IF_NONEXISTING);
- bool lockIfNonExisting = (flags & LOCK_IF_NONEXISTING_AND_NOT_CREATING);
-#if __WORDSIZE == 64
- return LockableMap<JudyMultiMap<Entry> >::get(
- bucket.stripUnused().toKey(), clientId, createIfNonExisting,
- lockIfNonExisting);
-#else
- return LockableMap<StdMapWrapper<document::BucketId::Type, Entry> >::get(
- bucket.stripUnused().toKey(), clientId,
- createIfNonExisting, lockIfNonExisting);
-#endif
+ return _impl->get(bucket.stripUnused().toKey(), clientId, createIfNonExisting);
+}
+
+size_t StorBucketDatabase::size() const {
+ return _impl->size();
+}
+
+size_t StorBucketDatabase::getMemoryUsage() const {
+ return _impl->getMemoryUsage();
+}
+
+void StorBucketDatabase::showLockClients(vespalib::asciistream& out) const {
+ _impl->showLockClients(out);
+}
+
+StorBucketDatabase::EntryMap
+StorBucketDatabase::getAll(const BucketId& bucketId, const char* clientId) {
+ return _impl->getAll(bucketId, clientId);
+}
+
+StorBucketDatabase::EntryMap
+StorBucketDatabase::getContained(const BucketId& bucketId, const char* clientId) {
+ return _impl->getContained(bucketId, clientId);
+}
+
+bool StorBucketDatabase::isConsistent(const WrappedEntry& entry) {
+ return _impl->isConsistent(entry);
+}
+
+void StorBucketDatabase::for_each_chunked(
+ std::function<Decision(uint64_t, const bucketdb::StorageBucketInfo&)> func,
+ const char* clientId,
+ vespalib::duration yieldTime,
+ uint32_t chunkSize)
+{
+ _impl->for_each_chunked(std::move(func), clientId, yieldTime, chunkSize);
+}
+
+void StorBucketDatabase::for_each_mutable(
+ std::function<Decision(uint64_t, bucketdb::StorageBucketInfo&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last)
+{
+ _impl->for_each_mutable(std::move(func), clientId, first, last);
+}
+
+void StorBucketDatabase::for_each(
+ std::function<Decision(uint64_t, const bucketdb::StorageBucketInfo&)> func,
+ const char* clientId,
+ const key_type& first,
+ const key_type& last)
+{
+ _impl->for_each(std::move(func), clientId, first, last);
}
template class JudyMultiMap<bucketdb::StorageBucketInfo>;
diff --git a/storage/src/vespa/storage/bucketdb/storbucketdb.h b/storage/src/vespa/storage/bucketdb/storbucketdb.h
index 15d1004a00d..87e5d80c01b 100644
--- a/storage/src/vespa/storage/bucketdb/storbucketdb.h
+++ b/storage/src/vespa/storage/bucketdb/storbucketdb.h
@@ -1,51 +1,84 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-/**
- * \class StorageBucketInfo
- * \ingroup bucketdb
- *
- * \brief An entry in the storage bucket database.
- *
- * \class StorBucketDatabase
- * \ingroup bucketdb
- *
- * \brief The storage bucket database.
- */
#pragma once
-#include "judymultimap.h"
-#include "lockablemap.h"
-#include "stdmapwrapper.h"
+#include "abstract_bucket_map.h"
#include "storagebucketinfo.h"
#include <vespa/storageapi/defs.h>
+#include <memory>
namespace storage {
-
-class StorBucketDatabase
-#if __WORDSIZE == 64
- : public LockableMap<JudyMultiMap<bucketdb::StorageBucketInfo> >
-#else
-# warning Bucket database cannot use Judy on non-64 bit platforms
- : public LockableMap<StdMapWrapper<document::BucketId::Type, bucketdb::StorageBucketInfo> >
-#endif
-{
+class StorBucketDatabase {
+ std::unique_ptr<bucketdb::AbstractBucketMap<bucketdb::StorageBucketInfo>> _impl;
public:
+ using Entry = bucketdb::StorageBucketInfo;
+ using key_type = bucketdb::AbstractBucketMap<Entry>::key_type;
+ using Decision = bucketdb::AbstractBucketMap<Entry>::Decision;
+ using WrappedEntry = bucketdb::AbstractBucketMap<Entry>::WrappedEntry;
+ using EntryMap = bucketdb::AbstractBucketMap<Entry>::EntryMap;
+ using BucketId = document::BucketId;
+
enum Flag {
NONE = 0,
- CREATE_IF_NONEXISTING = 1,
- LOCK_IF_NONEXISTING_AND_NOT_CREATING = 2
+ CREATE_IF_NONEXISTING = 1
};
- typedef bucketdb::StorageBucketInfo Entry;
- StorBucketDatabase() {};
+ explicit StorBucketDatabase(bool use_btree_db = false);
void insert(const document::BucketId&, const bucketdb::StorageBucketInfo&,
const char* clientId);
bool erase(const document::BucketId&, const char* clientId);
- WrappedEntry get(const document::BucketId& bucket, const char* clientId,
- Flag flags = NONE);
+ WrappedEntry get(const document::BucketId& bucket, const char* clientId, Flag flags = NONE);
+
+ size_t size() const;
+
+ /**
+ * Returns all buckets in the bucket database that can contain the given
+ * bucket, and all buckets that that bucket contains.
+ */
+ EntryMap getAll(const BucketId& bucketId, const char* clientId);
+
+ /**
+ * Returns all buckets in the bucket database that can contain the given
+ * bucket. Usually, there should be only one such bucket, but in the case
+ * of inconsistent splitting, there may be more than one.
+ */
+ EntryMap getContained(const BucketId& bucketId, const char* clientId);
+
+ /**
+ * Iterate over the entire database contents, holding the global database
+ * mutex for `chunkSize` processed entries at a time, yielding the current
+ * thread between each such such to allow other threads to get a chance
+ * at acquiring a bucket lock.
+ */
+ void for_each_chunked(std::function<Decision(uint64_t, const bucketdb::StorageBucketInfo&)> func,
+ const char* clientId,
+ vespalib::duration yieldTime = 10us,
+ uint32_t chunkSize = bucketdb::AbstractBucketMap<bucketdb::StorageBucketInfo>::DEFAULT_CHUNK_SIZE);
+
+ void for_each_mutable(std::function<Decision(uint64_t, bucketdb::StorageBucketInfo&)> func,
+ const char* clientId,
+ const key_type& first = key_type(),
+ const key_type& last = key_type() - 1);
+
+ void for_each(std::function<Decision(uint64_t, const bucketdb::StorageBucketInfo&)> func,
+ const char* clientId,
+ const key_type& first = key_type(),
+ const key_type& last = key_type() - 1);
+
+ /**
+ * Returns true iff bucket has no superbuckets or sub-buckets in the
+ * database. Usage assumption is that any operation that can cause the
+ * bucket to become inconsistent will require taking its lock, so by
+ * requiring the lock to be provided here we avoid race conditions.
+ */
+ bool isConsistent(const WrappedEntry& entry);
+
+ size_t getMemoryUsage() const;
+ void showLockClients(vespalib::asciistream & out) const;
+
};
} // storage
diff --git a/storage/src/vespa/storage/common/content_bucket_space.cpp b/storage/src/vespa/storage/common/content_bucket_space.cpp
index 0827c721100..e293c4bc336 100644
--- a/storage/src/vespa/storage/common/content_bucket_space.cpp
+++ b/storage/src/vespa/storage/common/content_bucket_space.cpp
@@ -4,9 +4,9 @@
namespace storage {
-ContentBucketSpace::ContentBucketSpace(document::BucketSpace bucketSpace)
+ContentBucketSpace::ContentBucketSpace(document::BucketSpace bucketSpace, bool use_btree_db)
: _bucketSpace(bucketSpace),
- _bucketDatabase(),
+ _bucketDatabase(use_btree_db),
_lock(),
_clusterState(),
_distribution(),
diff --git a/storage/src/vespa/storage/common/content_bucket_space.h b/storage/src/vespa/storage/common/content_bucket_space.h
index 81ce6234879..bf001f8b6f2 100644
--- a/storage/src/vespa/storage/common/content_bucket_space.h
+++ b/storage/src/vespa/storage/common/content_bucket_space.h
@@ -26,7 +26,7 @@ private:
public:
using UP = std::unique_ptr<ContentBucketSpace>;
- ContentBucketSpace(document::BucketSpace bucketSpace);
+ explicit ContentBucketSpace(document::BucketSpace bucketSpace, bool use_btree_db = false);
document::BucketSpace bucketSpace() const noexcept { return _bucketSpace; }
StorBucketDatabase &bucketDatabase() { return _bucketDatabase; }
diff --git a/storage/src/vespa/storage/common/content_bucket_space_repo.cpp b/storage/src/vespa/storage/common/content_bucket_space_repo.cpp
index 774ddb81578..2cc03e4d1e5 100644
--- a/storage/src/vespa/storage/common/content_bucket_space_repo.cpp
+++ b/storage/src/vespa/storage/common/content_bucket_space_repo.cpp
@@ -7,11 +7,13 @@ using document::BucketSpace;
namespace storage {
-ContentBucketSpaceRepo::ContentBucketSpaceRepo()
+ContentBucketSpaceRepo::ContentBucketSpaceRepo(bool use_btree_db)
: _map()
{
- _map.emplace(document::FixedBucketSpaces::default_space(), std::make_unique<ContentBucketSpace>(document::FixedBucketSpaces::default_space()));
- _map.emplace(document::FixedBucketSpaces::global_space(), std::make_unique<ContentBucketSpace>(document::FixedBucketSpaces::global_space()));
+ _map.emplace(document::FixedBucketSpaces::default_space(),
+ std::make_unique<ContentBucketSpace>(document::FixedBucketSpaces::default_space(), use_btree_db));
+ _map.emplace(document::FixedBucketSpaces::global_space(),
+ std::make_unique<ContentBucketSpace>(document::FixedBucketSpaces::global_space(), use_btree_db));
}
ContentBucketSpace &
diff --git a/storage/src/vespa/storage/common/content_bucket_space_repo.h b/storage/src/vespa/storage/common/content_bucket_space_repo.h
index 0d4ddb86bcf..142bb5ea1d5 100644
--- a/storage/src/vespa/storage/common/content_bucket_space_repo.h
+++ b/storage/src/vespa/storage/common/content_bucket_space_repo.h
@@ -19,7 +19,7 @@ private:
BucketSpaceMap _map;
public:
- ContentBucketSpaceRepo();
+ explicit ContentBucketSpaceRepo(bool use_btree_db = false);
ContentBucketSpace &get(document::BucketSpace bucketSpace) const;
BucketSpaceMap::const_iterator begin() const { return _map.begin(); }
BucketSpaceMap::const_iterator end() const { return _map.end(); }
@@ -31,7 +31,7 @@ public:
void forEachBucket(Functor &functor,
const char *clientId) const {
for (const auto &elem : _map) {
- elem.second->bucketDatabase().all(functor, clientId);
+ elem.second->bucketDatabase().for_each(std::ref(functor), clientId);
}
}
@@ -39,7 +39,7 @@ public:
void forEachBucketChunked(Functor &functor,
const char *clientId) const {
for (const auto &elem : _map) {
- elem.second->bucketDatabase().chunkedAll(functor, clientId);
+ elem.second->bucketDatabase().for_each_chunked(std::ref(functor), clientId);
}
}
diff --git a/storage/src/vespa/storage/config/stor-distributormanager.def b/storage/src/vespa/storage/config/stor-distributormanager.def
index 71059d717a6..db2bfb61376 100644
--- a/storage/src/vespa/storage/config/stor-distributormanager.def
+++ b/storage/src/vespa/storage/config/stor-distributormanager.def
@@ -203,7 +203,7 @@ simulated_db_merging_latency_msec int default=0
## Whether to use a B-tree data structure for the distributor bucket database instead
## of the legacy database. Setting this option may trigger alternate code paths for
## read only operations, as the B-tree database is thread safe for concurrent reads.
-use_btree_database bool default=false restart
+use_btree_database bool default=true restart
## If a bucket is inconsistent and an Update operation is received, a two-phase
## write-repair path is triggered in which a Get is sent to all diverging replicas.
@@ -243,4 +243,4 @@ enable_metadata_only_fetch_phase_for_inconsistent_updates bool default=false
## maintenance scans from being done as part of the tick for up to N rounds of ticking.
## This is to reduce the amount of CPU spent on ideal state calculations and bucket DB
## accesses when the distributor is heavily loaded with feed operations.
-max_consecutively_inhibited_maintenance_ticks int default=20 \ No newline at end of file
+max_consecutively_inhibited_maintenance_ticks int default=20
diff --git a/storage/src/vespa/storage/config/stor-server.def b/storage/src/vespa/storage/config/stor-server.def
index 5d303b59d81..e1446aa8ed1 100644
--- a/storage/src/vespa/storage/config/stor-server.def
+++ b/storage/src/vespa/storage/config/stor-server.def
@@ -84,3 +84,7 @@ bucket_rechecking_chunk_size int default=100
## full bucket info requests. The latency is added per batch of operations processed.
## Only useful for testing!
simulated_bucket_request_latency_msec int default=0
+
+## If set, content node processes will use a B-tree backed bucket database implementation
+## instead of the legacy Judy-based implementation.
+use_content_node_btree_bucket_db bool default=false restart
diff --git a/storage/src/vespa/storage/distributor/distributor.cpp b/storage/src/vespa/storage/distributor/distributor.cpp
index 5dade47c2a6..5080b16c727 100644
--- a/storage/src/vespa/storage/distributor/distributor.cpp
+++ b/storage/src/vespa/storage/distributor/distributor.cpp
@@ -109,10 +109,6 @@ Distributor::Distributor(DistributorComponentRegister& compReg,
_must_send_updated_host_info(false),
_use_btree_database(use_btree_database)
{
- if (use_btree_database) {
- LOG(info, "Using new B-tree bucket database implementation instead of legacy implementation"); // TODO remove this once default is swapped
- }
-
_component.registerMetric(*_metrics);
_component.registerMetricUpdateHook(_metricUpdateHook,
framework::SecondTime(0));
diff --git a/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.cpp b/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.cpp
index 888f1e816a1..da45b61701e 100644
--- a/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.cpp
+++ b/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.cpp
@@ -9,9 +9,9 @@ namespace storage {
using vespalib::IllegalStateException;
-ServiceLayerComponentRegisterImpl::ServiceLayerComponentRegisterImpl()
+ServiceLayerComponentRegisterImpl::ServiceLayerComponentRegisterImpl(bool use_btree_db)
: _diskCount(0),
- _bucketSpaceRepo()
+ _bucketSpaceRepo(use_btree_db)
{ }
void
diff --git a/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.h b/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.h
index deb3b2c0767..9988b861422 100644
--- a/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.h
+++ b/storage/src/vespa/storage/frameworkimpl/component/servicelayercomponentregisterimpl.h
@@ -27,7 +27,7 @@ class ServiceLayerComponentRegisterImpl
public:
typedef std::unique_ptr<ServiceLayerComponentRegisterImpl> UP;
- ServiceLayerComponentRegisterImpl();
+ ServiceLayerComponentRegisterImpl(bool use_btree_db = false);
uint16_t getDiskCount() const { return _diskCount; }
ContentBucketSpaceRepo& getBucketSpaceRepo() { return _bucketSpaceRepo; }
diff --git a/storage/src/vespa/storage/frameworkimpl/component/storagecomponentregisterimpl.cpp b/storage/src/vespa/storage/frameworkimpl/component/storagecomponentregisterimpl.cpp
index 2f66027ccab..654fcbd1380 100644
--- a/storage/src/vespa/storage/frameworkimpl/component/storagecomponentregisterimpl.cpp
+++ b/storage/src/vespa/storage/frameworkimpl/component/storagecomponentregisterimpl.cpp
@@ -25,7 +25,7 @@ StorageComponentRegisterImpl::StorageComponentRegisterImpl()
{
}
-StorageComponentRegisterImpl::~StorageComponentRegisterImpl() { }
+StorageComponentRegisterImpl::~StorageComponentRegisterImpl() = default;
void
StorageComponentRegisterImpl::registerStorageComponent(StorageComponent& smc)
diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
index eec8ac3e327..021c94464df 100644
--- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
+++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp
@@ -848,7 +848,7 @@ namespace {
StorBucketDatabase::Decision operator()(document::BucketId::Type, StorBucketDatabase::Entry& data)
{
data.info.setActive(false);
- return StorBucketDatabase::UPDATE;
+ return StorBucketDatabase::Decision::UPDATE;
}
};
}
@@ -870,7 +870,8 @@ FileStorManager::updateState()
if (contentBucketSpace.getNodeUpInLastNodeStateSeenByProvider() && !nodeUp) {
LOG(debug, "Received cluster state where this node is down; de-activating all buckets in database for bucket space %s", bucketSpace.toString().c_str());
Deactivator deactivator;
- contentBucketSpace.bucketDatabase().all(deactivator, "FileStorManager::updateState");
+ contentBucketSpace.bucketDatabase().for_each_mutable(
+ std::ref(deactivator), "FileStorManager::updateState");
}
contentBucketSpace.setNodeUpInLastNodeStateSeenByProvider(nodeUp);
spi::ClusterState spiState(*derivedClusterState, _component.getIndex(), *contentBucketSpace.getDistribution());
diff --git a/storage/src/vespa/storage/storageserver/servicelayernodecontext.cpp b/storage/src/vespa/storage/storageserver/servicelayernodecontext.cpp
index ecd52295d38..bbfc9afc7aa 100644
--- a/storage/src/vespa/storage/storageserver/servicelayernodecontext.cpp
+++ b/storage/src/vespa/storage/storageserver/servicelayernodecontext.cpp
@@ -4,9 +4,8 @@
namespace storage {
-ServiceLayerNodeContext::ServiceLayerNodeContext(
- framework::Clock::UP clock)
- : StorageNodeContext(StorageComponentRegisterImpl::UP(new ServiceLayerComponentRegisterImpl),
+ServiceLayerNodeContext::ServiceLayerNodeContext(framework::Clock::UP clock, bool use_btree_db)
+ : StorageNodeContext(std::make_unique<ServiceLayerComponentRegisterImpl>(use_btree_db),
std::move(clock)),
_componentRegister(dynamic_cast<ComponentRegister&>(StorageNodeContext::getComponentRegister()))
{
diff --git a/storage/src/vespa/storage/storageserver/servicelayernodecontext.h b/storage/src/vespa/storage/storageserver/servicelayernodecontext.h
index 9266dc2c7e9..0d7ac5bff69 100644
--- a/storage/src/vespa/storage/storageserver/servicelayernodecontext.h
+++ b/storage/src/vespa/storage/storageserver/servicelayernodecontext.h
@@ -29,8 +29,7 @@ struct ServiceLayerNodeContext : public StorageNodeContext {
* You can provide your own clock implementation. Useful in testing where
* you want to fake the clock.
*/
- ServiceLayerNodeContext(
- framework::Clock::UP clock = framework::Clock::UP(new RealClock));
+ ServiceLayerNodeContext(framework::Clock::UP clock, bool use_btree_db);
/**
* Get the actual component register. Available as the actual type as the
diff --git a/storageapi/src/tests/CMakeLists.txt b/storageapi/src/tests/CMakeLists.txt
index 8b820adb467..f07b9d0a2bc 100644
--- a/storageapi/src/tests/CMakeLists.txt
+++ b/storageapi/src/tests/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storageapi_gtest_runner_app TEST
SOURCES
gtest_runner.cpp
@@ -8,7 +9,7 @@ vespa_add_executable(storageapi_gtest_runner_app TEST
storageapi_testmbusprot
storageapi_testmessageapi
storageapi
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storageapi/src/tests/buckets/CMakeLists.txt b/storageapi/src/tests/buckets/CMakeLists.txt
index 42310be56f0..ea2cc109986 100644
--- a/storageapi/src/tests/buckets/CMakeLists.txt
+++ b/storageapi/src/tests/buckets/CMakeLists.txt
@@ -1,8 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storageapi_testbuckets
SOURCES
bucketinfotest.cpp
DEPENDS
storageapi
- gtest
+ GTest::GTest
)
diff --git a/storageapi/src/tests/mbusprot/CMakeLists.txt b/storageapi/src/tests/mbusprot/CMakeLists.txt
index 2801c9a91dd..84b8ff0f1b0 100644
--- a/storageapi/src/tests/mbusprot/CMakeLists.txt
+++ b/storageapi/src/tests/mbusprot/CMakeLists.txt
@@ -1,8 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storageapi_testmbusprot
SOURCES
storageprotocoltest.cpp
DEPENDS
storageapi
- gtest
+ GTest::GTest
)
diff --git a/storageframework/src/tests/CMakeLists.txt b/storageframework/src/tests/CMakeLists.txt
index d658605a911..1cf40b8a2ef 100644
--- a/storageframework/src/tests/CMakeLists.txt
+++ b/storageframework/src/tests/CMakeLists.txt
@@ -1,5 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
+
# Runner for unit tests written in gtest.
# NOTE: All new test classes should be added here.
vespa_add_executable(storageframework_gtest_runner_app TEST
@@ -8,7 +10,7 @@ vespa_add_executable(storageframework_gtest_runner_app TEST
DEPENDS
storageframework_testclock
storageframework_testthread
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storageframework/src/tests/clock/CMakeLists.txt b/storageframework/src/tests/clock/CMakeLists.txt
index f887de61239..62bb6eeedc1 100644
--- a/storageframework/src/tests/clock/CMakeLists.txt
+++ b/storageframework/src/tests/clock/CMakeLists.txt
@@ -1,8 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storageframework_testclock
SOURCES
timetest.cpp
DEPENDS
storageframework
- gtest
+ GTest::GTest
)
diff --git a/storageframework/src/tests/thread/CMakeLists.txt b/storageframework/src/tests/thread/CMakeLists.txt
index 3e6db8c9b57..810b0c59912 100644
--- a/storageframework/src/tests/thread/CMakeLists.txt
+++ b/storageframework/src/tests/thread/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(storageframework_testthread
SOURCES
tickingthreadtest.cpp
taskthreadtest.cpp
DEPENDS
storageframework
- gtest
+ GTest::GTest
)
diff --git a/storageserver/src/tests/CMakeLists.txt b/storageserver/src/tests/CMakeLists.txt
index 9c475543b81..2981d452d5f 100644
--- a/storageserver/src/tests/CMakeLists.txt
+++ b/storageserver/src/tests/CMakeLists.txt
@@ -1,5 +1,6 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(storageserver_gtest_runner_app TEST
SOURCES
storageservertest.cpp
@@ -8,7 +9,7 @@ vespa_add_executable(storageserver_gtest_runner_app TEST
DEPENDS
storageserver_storageapp
vdstestlib
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/storageserver/src/vespa/storageserver/app/distributorprocess.h b/storageserver/src/vespa/storageserver/app/distributorprocess.h
index 48fa331ba54..e416f285268 100644
--- a/storageserver/src/vespa/storageserver/app/distributorprocess.h
+++ b/storageserver/src/vespa/storageserver/app/distributorprocess.h
@@ -12,7 +12,7 @@
namespace storage {
-class DistributorProcess : public Process {
+class DistributorProcess final : public Process {
DistributorNodeContext _context;
DistributorNode::NeedActiveState _activeFlag;
bool _use_btree_database;
@@ -23,7 +23,7 @@ class DistributorProcess : public Process {
_visitDispatcherConfigHandler;
public:
- DistributorProcess(const config::ConfigUri & configUri);
+ explicit DistributorProcess(const config::ConfigUri & configUri);
~DistributorProcess() override;
void shutdown() override;
diff --git a/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp b/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp
index bde93b9e4fb..4ff3810d85f 100644
--- a/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp
+++ b/storageserver/src/vespa/storageserver/app/servicelayerprocess.cpp
@@ -1,6 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "servicelayerprocess.h"
+#include <vespa/config/helper/configgetter.hpp>
+#include <vespa/storage/config/config-stor-server.h>
#include <vespa/storage/storageserver/servicelayernode.h>
#include <vespa/searchvisitor/searchvisitor.h>
@@ -9,8 +11,21 @@ LOG_SETUP(".storageserver.service_layer_process");
namespace storage {
-ServiceLayerProcess::ServiceLayerProcess(const config::ConfigUri & configUri)
- : Process(configUri)
+namespace {
+
+bool configured_to_use_btree_db(const config::ConfigUri& config_uri) {
+ using vespa::config::content::core::StorServerConfig;
+ auto server_config = config::ConfigGetter<StorServerConfig>::getConfig(
+ config_uri.getConfigId(), config_uri.getContext());
+ return server_config->useContentNodeBtreeBucketDb;
+}
+
+}
+
+ServiceLayerProcess::ServiceLayerProcess(const config::ConfigUri& configUri)
+ : Process(configUri),
+ _context(std::make_unique<framework::defaultimplementation::RealClock>(),
+ configured_to_use_btree_db(configUri))
{
}
diff --git a/storageserver/src/vespa/storageserver/app/servicelayerprocess.h b/storageserver/src/vespa/storageserver/app/servicelayerprocess.h
index 27c2db8ed6f..b24640cbbd7 100644
--- a/storageserver/src/vespa/storageserver/app/servicelayerprocess.h
+++ b/storageserver/src/vespa/storageserver/app/servicelayerprocess.h
@@ -36,8 +36,8 @@ protected:
ServiceLayerNodeContext _context;
public:
- ServiceLayerProcess(const config::ConfigUri & configUri);
- ~ServiceLayerProcess();
+ explicit ServiceLayerProcess(const config::ConfigUri & configUri);
+ ~ServiceLayerProcess() override;
void shutdown() override;
diff --git a/tenant-auth/OWNERS b/tenant-auth/OWNERS
deleted file mode 100644
index d0a102ecbf4..00000000000
--- a/tenant-auth/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-jonmv
diff --git a/tenant-auth/README.md b/tenant-auth/README.md
deleted file mode 100644
index c7552bd987c..00000000000
--- a/tenant-auth/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-<!-- Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-# Utilities that authenticate users to the hosted Vespa API, or to hosted Vespa applications.
diff --git a/tenant-auth/src/test/java/ai/vespa/hosted/auth/AuthenticatorTest.java b/tenant-auth/src/test/java/ai/vespa/hosted/auth/AuthenticatorTest.java
deleted file mode 100644
index 64282c26849..00000000000
--- a/tenant-auth/src/test/java/ai/vespa/hosted/auth/AuthenticatorTest.java
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.auth;
-
-public class AuthenticatorTest {
-
-}
diff --git a/tenant-cd-api/CMakeLists.txt b/tenant-cd-api/CMakeLists.txt
new file mode 100644
index 00000000000..971aa974aa2
--- /dev/null
+++ b/tenant-cd-api/CMakeLists.txt
@@ -0,0 +1,2 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+install_fat_java_artifact(tenant-cd-api)
diff --git a/tenant-cd-api/pom.xml b/tenant-cd-api/pom.xml
index b19d42d094f..23e5f3ec3f4 100644
--- a/tenant-cd-api/pom.xml
+++ b/tenant-cd-api/pom.xml
@@ -57,9 +57,7 @@
<artifactId>bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
- <attachBundleArtifact>true</attachBundleArtifact>
- <bundleClassifierName>deploy</bundleClassifierName>
- <useCommonAssemblyIds>false</useCommonAssemblyIds>
+ <useCommonAssemblyIds>true</useCommonAssemblyIds>
</configuration>
</plugin>
<plugin>
diff --git a/tenant-cd-commons/OWNERS b/tenant-cd-commons/OWNERS
new file mode 100644
index 00000000000..0a0d219e4eb
--- /dev/null
+++ b/tenant-cd-commons/OWNERS
@@ -0,0 +1,2 @@
+bjorncs
+mortent
diff --git a/tenant-cd-commons/README.md b/tenant-cd-commons/README.md
new file mode 100644
index 00000000000..b1cd95606c8
--- /dev/null
+++ b/tenant-cd-commons/README.md
@@ -0,0 +1,3 @@
+# tenant-cd-commons
+
+Contains shared implementations of APIs/interfaces of `tenant-cd-api`. \ No newline at end of file
diff --git a/tenant-auth/pom.xml b/tenant-cd-commons/pom.xml
index be8b42dd6c2..4d92be95c0b 100644
--- a/tenant-auth/pom.xml
+++ b/tenant-cd-commons/pom.xml
@@ -1,40 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<!-- Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
<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">
-
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>tenant-cd-commons</artifactId>
+ <packaging>jar</packaging>
+
<parent>
<groupId>com.yahoo.vespa</groupId>
<artifactId>parent</artifactId>
<version>7-SNAPSHOT</version>
- <relativePath>../parent/pom.xml</relativePath>
+ <relativePath>../parent</relativePath>
</parent>
- <artifactId>tenant-auth</artifactId>
- <description>Provides resources for authenticating with the hosted Vespa API or application containers</description>
<dependencies>
+ <!-- compile -->
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>hosted-api</artifactId>
+ <artifactId>tenant-cd-api</artifactId>
<version>${project.version}</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>config-provisioning</artifactId>
+ <artifactId>security-utils</artifactId>
<version>${project.version}</version>
+ <scope>provided</scope>
</dependency>
+
+ <!-- compile -->
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>security-utils</artifactId>
+ <artifactId>hosted-api</artifactId>
<version>${project.version}</version>
</dependency>
-
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-provisioning</artifactId>
+ <version>${project.version}</version>
</dependency>
</dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
</project>
diff --git a/tenant-auth/src/main/java/ai/vespa/hosted/auth/EndpointAuthenticator.java b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/DefaultEndpointAuthenticator.java
index a13e2d8ee56..89414cc069a 100644
--- a/tenant-auth/src/main/java/ai/vespa/hosted/auth/EndpointAuthenticator.java
+++ b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/DefaultEndpointAuthenticator.java
@@ -1,5 +1,5 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.auth;
+package ai.vespa.hosted.cd.commons;
import ai.vespa.hosted.api.Properties;
import com.yahoo.config.provision.SystemName;
@@ -26,12 +26,12 @@ import static ai.vespa.hosted.api.Properties.getNonBlankProperty;
*
* @author jonmv
*/
-public class EndpointAuthenticator implements ai.vespa.hosted.api.EndpointAuthenticator {
+public class DefaultEndpointAuthenticator implements EndpointAuthenticator {
- private static final Logger logger = Logger.getLogger(EndpointAuthenticator.class.getName());
+ private static final Logger logger = Logger.getLogger(DefaultEndpointAuthenticator.class.getName());
/** Don't touch. */
- public EndpointAuthenticator(@SuppressWarnings("unused") SystemName __) { }
+ public DefaultEndpointAuthenticator(@SuppressWarnings("unused") SystemName __) { }
/**
* If {@code System.getProperty("vespa.test.credentials.root")} is set, key and certificate files
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/EndpointAuthenticator.java b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/EndpointAuthenticator.java
index 81813335a63..e7936ddea7a 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/EndpointAuthenticator.java
+++ b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/EndpointAuthenticator.java
@@ -1,10 +1,9 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.api;
+package ai.vespa.hosted.cd.commons;
import javax.net.ssl.SSLContext;
import java.net.http.HttpRequest;
import java.security.NoSuchAlgorithmException;
-import java.util.Optional;
/**
* Adds environment dependent authentication to HTTP request against Vespa deployments.
diff --git a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/http/HttpDeployment.java b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/HttpDeployment.java
index 65210455b85..90a33bcb513 100644
--- a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/http/HttpDeployment.java
+++ b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/HttpDeployment.java
@@ -1,7 +1,6 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.cd.impl.http;
+package ai.vespa.hosted.cd.commons;
-import ai.vespa.hosted.api.EndpointAuthenticator;
import ai.vespa.hosted.cd.Deployment;
import ai.vespa.hosted.cd.Endpoint;
diff --git a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/http/HttpEndpoint.java b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/HttpEndpoint.java
index f48973b7382..cf8865df878 100644
--- a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/impl/http/HttpEndpoint.java
+++ b/tenant-cd-commons/src/main/java/ai/vespa/hosted/cd/commons/HttpEndpoint.java
@@ -1,7 +1,6 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.cd.impl.http;
+package ai.vespa.hosted.cd.commons;
-import ai.vespa.hosted.api.EndpointAuthenticator;
import ai.vespa.hosted.cd.Endpoint;
import javax.net.ssl.SSLParameters;
diff --git a/vdslib/src/tests/CMakeLists.txt b/vdslib/src/tests/CMakeLists.txt
index f552808f97c..458f3bda01d 100644
--- a/vdslib/src/tests/CMakeLists.txt
+++ b/vdslib/src/tests/CMakeLists.txt
@@ -1,5 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
+
# Runner for unit tests written in gtest.
# NOTE: All new test classes should be added here.
vespa_add_executable(vdslib_gtest_runner_app TEST
@@ -11,7 +13,7 @@ vespa_add_executable(vdslib_gtest_runner_app TEST
vdslib_testdistribution
vdslib_teststate
vdslib_testthread
- gtest
+ GTest::GTest
)
vespa_add_test(
diff --git a/vdslib/src/tests/bucketdistribution/CMakeLists.txt b/vdslib/src/tests/bucketdistribution/CMakeLists.txt
index ecb29e9b12d..986a7ff26ff 100644
--- a/vdslib/src/tests/bucketdistribution/CMakeLists.txt
+++ b/vdslib/src/tests/bucketdistribution/CMakeLists.txt
@@ -1,8 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(vdslib_bucketdistributiontest
SOURCES
bucketdistributiontest.cpp
DEPENDS
vdslib
- gtest
+ GTest::GTest
)
diff --git a/vdslib/src/tests/container/CMakeLists.txt b/vdslib/src/tests/container/CMakeLists.txt
index 9f7b2e33efa..47ffbcf7b82 100644
--- a/vdslib/src/tests/container/CMakeLists.txt
+++ b/vdslib/src/tests/container/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(vdslib_containertest
SOURCES
parameterstest.cpp
@@ -6,5 +7,5 @@ vespa_add_library(vdslib_containertest
documentsummarytest.cpp
DEPENDS
vdslib
- gtest
+ GTest::GTest
)
diff --git a/vdslib/src/tests/distribution/CMakeLists.txt b/vdslib/src/tests/distribution/CMakeLists.txt
index cdcfcfce5f9..61563c4c448 100644
--- a/vdslib/src/tests/distribution/CMakeLists.txt
+++ b/vdslib/src/tests/distribution/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(vdslib_testdistribution
SOURCES
distributiontest.cpp
@@ -6,5 +7,5 @@ vespa_add_library(vdslib_testdistribution
idealnodecalculatorimpltest.cpp
DEPENDS
vdslib
- gtest
+ GTest::GTest
)
diff --git a/vdslib/src/tests/distribution/distributiontest.cpp b/vdslib/src/tests/distribution/distributiontest.cpp
index f0b48faebef..5a337433bdb 100644
--- a/vdslib/src/tests/distribution/distributiontest.cpp
+++ b/vdslib/src/tests/distribution/distributiontest.cpp
@@ -13,10 +13,14 @@
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/stllike/lexical_cast.h>
#include <vespa/vespalib/text/stringtokenizer.h>
+#include <vespa/vespalib/util/benchmark_timer.h>
+#include <gmock/gmock.h>
#include <chrono>
#include <thread>
#include <fstream>
+using namespace ::testing;
+
namespace storage::lib {
template <typename T>
@@ -1143,4 +1147,106 @@ TEST(DistributionTest, test_hierarchical_distribute_less_than_redundancy)
}
}
+TEST(DistributionTest, wildcard_top_level_distribution_gives_expected_node_results) {
+ std::string raw_config = R"(redundancy 2
+initial_redundancy 2
+ensure_primary_persisted true
+ready_copies 2
+active_per_leaf_group false
+distributor_auto_ownership_transfer_on_whole_group_down true
+group[0].index "invalid"
+group[0].name "invalid"
+group[0].capacity 5
+group[0].partitions "*"
+group[1].index "0"
+group[1].name "switch0"
+group[1].capacity 3
+group[1].partitions ""
+group[1].nodes[0].index 0
+group[1].nodes[0].retired false
+group[1].nodes[1].index 1
+group[1].nodes[1].retired false
+group[1].nodes[2].index 2
+group[1].nodes[2].retired false
+group[2].index "1"
+group[2].name "switch1"
+group[2].capacity 2
+group[2].partitions ""
+group[2].nodes[0].index 3
+group[2].nodes[0].retired false
+group[2].nodes[1].index 4
+group[2].nodes[1].retired false
+disk_distribution "MODULO_BID"
+)";
+ Distribution distr(raw_config);
+ ClusterState state("version:1 distributor:5 storage:5");
+
+ auto nodes_of = [&](uint32_t bucket){
+ std::vector<uint16_t> actual;
+ distr.getIdealNodes(NodeType::STORAGE, state, document::BucketId(16, bucket), actual);
+ return actual;
+ };
+
+ EXPECT_THAT(nodes_of(1), ElementsAre(0, 2));
+ EXPECT_THAT(nodes_of(2), ElementsAre(2, 0));
+ EXPECT_THAT(nodes_of(3), ElementsAre(4, 3));
+ EXPECT_THAT(nodes_of(4), ElementsAre(3, 4));
+ EXPECT_THAT(nodes_of(5), ElementsAre(4, 3));
+ EXPECT_THAT(nodes_of(6), ElementsAre(1, 0));
+ EXPECT_THAT(nodes_of(7), ElementsAre(2, 0));
+}
+
+namespace {
+
+std::string generate_config_with_n_1node_groups(int n_groups) {
+ std::ostringstream config_os;
+ std::ostringstream partition_os;
+ for (int i = 0; i < n_groups - 1; ++i) {
+ partition_os << "1|";
+ }
+ partition_os << '*';
+ config_os << "redundancy " << n_groups << "\n"
+ << "initial_redundancy " << n_groups << "\n"
+ << "ensure_primary_persisted true\n"
+ << "ready_copies " << n_groups << "\n"
+ << "active_per_leaf_group true\n"
+ << "distributor_auto_ownership_transfer_on_whole_group_down true\n"
+ << "group[0].index \"invalid\"\n"
+ << "group[0].name \"invalid\"\n"
+ << "group[0].capacity " << n_groups << "\n"
+ << "group[0].partitions \"" << partition_os.str() << "\"\n";
+
+ for (int i = 0; i < n_groups; ++i) {
+ int g = i + 1;
+ config_os << "group[" << g << "].index \"" << i << "\"\n"
+ << "group[" << g << "].name \"group" << g << "\"\n"
+ << "group[" << g << "].capacity 1\n"
+ << "group[" << g << "].partitions \"\"\n"
+ << "group[" << g << "].nodes[0].index \"" << i << "\"\n"
+ << "group[" << g << "].nodes[0].retired false\n";
+ }
+ return config_os.str();
+}
+
+std::string generate_state_with_n_nodes_up(int n_nodes) {
+ std::ostringstream state_os;
+ state_os << "version:1 bits:8 distributor:" << n_nodes << " storage:" << n_nodes;
+ return state_os.str();
+}
+
+}
+
+TEST(DistributionTest, DISABLED_benchmark_ideal_state_for_many_groups) {
+ const int n_groups = 150;
+ Distribution distr(generate_config_with_n_1node_groups(n_groups));
+ ClusterState state(generate_state_with_n_nodes_up(n_groups));
+
+ std::vector<uint16_t> actual;
+ uint32_t bucket = 0;
+ auto min_time = vespalib::BenchmarkTimer::benchmark([&]{
+ distr.getIdealNodes(NodeType::STORAGE, state, document::BucketId(16, (bucket++ & 0xffffU)), actual);
+ }, 5.0);
+ fprintf(stderr, "%.10f seconds\n", min_time);
+}
+
}
diff --git a/vdslib/src/tests/state/CMakeLists.txt b/vdslib/src/tests/state/CMakeLists.txt
index 0e057d12226..d28ff47798a 100644
--- a/vdslib/src/tests/state/CMakeLists.txt
+++ b/vdslib/src/tests/state/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(vdslib_teststate
SOURCES
cluster_state_bundle_test.cpp
@@ -7,5 +8,5 @@ vespa_add_library(vdslib_teststate
nodestatetest.cpp
DEPENDS
vdslib
- gtest
+ GTest::GTest
)
diff --git a/vdslib/src/tests/thread/CMakeLists.txt b/vdslib/src/tests/thread/CMakeLists.txt
index 4d1e753a8f6..df2e8bf43f9 100644
--- a/vdslib/src/tests/thread/CMakeLists.txt
+++ b/vdslib/src/tests/thread/CMakeLists.txt
@@ -1,8 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_library(vdslib_testthread
SOURCES
taskschedulertest.cpp
DEPENDS
vdslib
- gtest
+ GTest::GTest
)
diff --git a/vdslib/src/vespa/vdslib/distribution/distribution.cpp b/vdslib/src/vespa/vdslib/distribution/distribution.cpp
index 0abb99e17d2..4c74923c58b 100644
--- a/vdslib/src/vespa/vdslib/distribution/distribution.cpp
+++ b/vdslib/src/vespa/vdslib/distribution/distribution.cpp
@@ -351,6 +351,7 @@ namespace {
const Group* _group;
double _score;
+ ScoredGroup() : _group(nullptr), _score(0) {}
ScoredGroup(const Group* group, double score)
: _group(group), _score(score) {}
@@ -430,40 +431,36 @@ Distribution::getIdealGroups(const document::BucketId& bucket,
std::vector<ResultGroup>& results) const
{
if (parent.isLeafGroup()) {
- results.push_back(ResultGroup(parent, redundancy));
+ results.emplace_back(parent, redundancy);
return;
}
- const Group::Distribution& redundancyArray(
- parent.getDistribution(redundancy));
- std::vector<ScoredGroup> tmpResults(redundancyArray.size(),
- ScoredGroup(0, 0));
- uint32_t seed(getGroupSeed(bucket, clusterState, parent));
+ const Group::Distribution& redundancyArray = parent.getDistribution(redundancy);
+ uint32_t seed = getGroupSeed(bucket, clusterState, parent);
RandomGen random(seed);
uint32_t currentIndex = 0;
- const std::map<uint16_t, Group*>& subGroups(parent.getSubGroups());
- for (std::map<uint16_t, Group*>::const_iterator it = subGroups.begin();
- it != subGroups.end(); ++it)
- {
- while (it->first < currentIndex++) random.nextDouble();
- double score = random.nextDouble();
- if (it->second->getCapacity() != 1) {
- // Capacity shouldn't possibly be 0.
- // Verified in Group::setCapacity()
- score = std::pow(score, 1.0 / it->second->getCapacity().getValue());
+ const auto& subGroups = parent.getSubGroups();
+ std::vector<ScoredGroup> tmpResults;
+ tmpResults.reserve(subGroups.size());
+ for (const auto& g : subGroups) {
+ while (g.first < currentIndex++) {
+ random.nextDouble();
}
- if (score > tmpResults.back()._score) {
- tmpResults.push_back(ScoredGroup(it->second, score));
- std::sort(tmpResults.begin(), tmpResults.end());
- tmpResults.pop_back();
+ double score = random.nextDouble();
+ if (g.second->getCapacity() != 1) {
+ // Capacity shouldn't possibly be 0.
+ // Verified in Group::setCapacity()
+ score = std::pow(score, 1.0 / g.second->getCapacity().getValue());
}
+ tmpResults.emplace_back(g.second, score);
}
- while (tmpResults.back()._group == nullptr) {
- tmpResults.pop_back();
+ std::sort(tmpResults.begin(), tmpResults.end());
+ if (tmpResults.size() > redundancyArray.size()) {
+ tmpResults.resize(redundancyArray.size());
}
for (uint32_t i=0, n=tmpResults.size(); i<n; ++i) {
ScoredGroup& group(tmpResults[i]);
- // This should never happen. Config should verify that each group
- // has enough groups beneath them.
+ // This should never happen. Config should verify that each group
+ // has enough groups beneath them.
assert(group._group != nullptr);
getIdealGroups(bucket, clusterState, *group._group,
redundancyArray[i], results);
diff --git a/vespa-osgi-testrunner/CMakeLists.txt b/vespa-osgi-testrunner/CMakeLists.txt
new file mode 100644
index 00000000000..58aba186710
--- /dev/null
+++ b/vespa-osgi-testrunner/CMakeLists.txt
@@ -0,0 +1,2 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+install_fat_java_artifact(vespa-osgi-testrunner)
diff --git a/vespa-osgi-testrunner/pom.xml b/vespa-osgi-testrunner/pom.xml
index 5ed9f75c9eb..62ea578f14f 100644
--- a/vespa-osgi-testrunner/pom.xml
+++ b/vespa-osgi-testrunner/pom.xml
@@ -79,9 +79,7 @@
<version>${project.version}</version>
<extensions>true</extensions>
<configuration>
- <attachBundleArtifact>true</attachBundleArtifact>
- <bundleClassifierName>deploy</bundleClassifierName>
- <useCommonAssemblyIds>false</useCommonAssemblyIds>
+ <useCommonAssemblyIds>true</useCommonAssemblyIds>
</configuration>
</plugin>
<plugin>
diff --git a/vespajlib/abi-spec.json b/vespajlib/abi-spec.json
index 154b6871392..7e7e376a8df 100644
--- a/vespajlib/abi-spec.json
+++ b/vespajlib/abi-spec.json
@@ -1904,6 +1904,7 @@
],
"methods": [
"public abstract java.lang.Double apply(com.yahoo.tensor.evaluation.EvaluationContext)",
+ "public java.util.Optional asTensorFunction()",
"public java.lang.String toString(com.yahoo.tensor.functions.ToStringContext)",
"public bridge synthetic java.lang.Object apply(java.lang.Object)"
],
@@ -2609,6 +2610,7 @@
"public abstract com.yahoo.tensor.TensorType type(com.yahoo.tensor.evaluation.TypeContext)",
"public final com.yahoo.tensor.Tensor evaluate()",
"public abstract java.lang.String toString(com.yahoo.tensor.functions.ToStringContext)",
+ "public java.util.Optional asScalarFunction()",
"public java.lang.String toString()"
],
"fields": []
diff --git a/vespalib/src/tests/alloc/alloc_test.cpp b/vespalib/src/tests/alloc/alloc_test.cpp
index d46d2374dfc..4dbeba62ee1 100644
--- a/vespalib/src/tests/alloc/alloc_test.cpp
+++ b/vespalib/src/tests/alloc/alloc_test.cpp
@@ -8,6 +8,14 @@
using namespace vespalib;
using namespace vespalib::alloc;
+#ifndef __SANITIZE_ADDRESS__
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+#define __SANITIZE_ADDRESS__
+#endif
+#endif
+#endif
+
template <typename T>
void
testSwap(T & a, T & b)
@@ -192,6 +200,13 @@ TEST("auto alloced mmap alloc can not be extended if no room") {
TEST_DO(verifyNoExtensionWhenNoRoom(buf, reserved, SZ));
}
+/*
+ * The two following tests are disabled when address sanitizer is
+ * enabled since extra instrumentation code might trigger extra mmap
+ * or munmap calls, breaking some of the assumptions in the disabled
+ * tests.
+ */
+#ifndef __SANITIZE_ADDRESS__
TEST("mmap alloc can be extended if room") {
Alloc dummy = Alloc::allocMMap(100);
Alloc reserved = Alloc::allocMMap(100);
@@ -209,6 +224,7 @@ TEST("mmap alloc can not be extended if no room") {
TEST_DO(verifyNoExtensionWhenNoRoom(buf, reserved, 4096));
}
#endif
+#endif
TEST("heap alloc can not be shrinked") {
Alloc buf = Alloc::allocHeap(101);
diff --git a/vespalib/src/tests/crypto/CMakeLists.txt b/vespalib/src/tests/crypto/CMakeLists.txt
index b930b5715b5..d0661461dd4 100644
--- a/vespalib/src/tests/crypto/CMakeLists.txt
+++ b/vespalib/src/tests/crypto/CMakeLists.txt
@@ -1,10 +1,11 @@
# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_crypto_crypto_test_app TEST
SOURCES
crypto_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_crypto_crypto_test_app COMMAND vespalib_crypto_crypto_test_app)
diff --git a/vespalib/src/tests/datastore/datastore/CMakeLists.txt b/vespalib/src/tests/datastore/datastore/CMakeLists.txt
index eb3e0a4576a..631c1812a7f 100644
--- a/vespalib/src/tests/datastore/datastore/CMakeLists.txt
+++ b/vespalib/src/tests/datastore/datastore/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_datastore_test_app TEST
SOURCES
datastore_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_datastore_test_app COMMAND vespalib_datastore_test_app)
diff --git a/vespalib/src/tests/datastore/unique_store/CMakeLists.txt b/vespalib/src/tests/datastore/unique_store/CMakeLists.txt
index d72e8c10ad5..29b34e93247 100644
--- a/vespalib/src/tests/datastore/unique_store/CMakeLists.txt
+++ b/vespalib/src/tests/datastore/unique_store/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_unique_store_test_app TEST
SOURCES
unique_store_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_unique_store_test_app COMMAND vespalib_unique_store_test_app)
diff --git a/vespalib/src/tests/datastore/unique_store_dictionary/CMakeLists.txt b/vespalib/src/tests/datastore/unique_store_dictionary/CMakeLists.txt
index b1478dea22c..2208902b5bb 100644
--- a/vespalib/src/tests/datastore/unique_store_dictionary/CMakeLists.txt
+++ b/vespalib/src/tests/datastore/unique_store_dictionary/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_unique_store_dictionary_test_app TEST
SOURCES
unique_store_dictionary_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_unique_store_dictionary_test_app COMMAND vespalib_unique_store_dictionary_test_app)
diff --git a/vespalib/src/tests/datastore/unique_store_string_allocator/CMakeLists.txt b/vespalib/src/tests/datastore/unique_store_string_allocator/CMakeLists.txt
index c2a9999d545..9984877ab21 100644
--- a/vespalib/src/tests/datastore/unique_store_string_allocator/CMakeLists.txt
+++ b/vespalib/src/tests/datastore/unique_store_string_allocator/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_unique_store_string_allocator_test_app TEST
SOURCES
unique_store_string_allocator_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_unique_store_string_allocator_test_app COMMAND vespalib_unique_store_string_allocator_test_app)
diff --git a/vespalib/src/tests/overload/CMakeLists.txt b/vespalib/src/tests/overload/CMakeLists.txt
index 67aa6230225..a03f6fbdc8d 100644
--- a/vespalib/src/tests/overload/CMakeLists.txt
+++ b/vespalib/src/tests/overload/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_overload_test_app TEST
SOURCES
overload_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_overload_test_app COMMAND vespalib_overload_test_app)
diff --git a/vespalib/src/tests/stllike/CMakeLists.txt b/vespalib/src/tests/stllike/CMakeLists.txt
index ebf7de9c747..41e0b9e8507 100644
--- a/vespalib/src/tests/stllike/CMakeLists.txt
+++ b/vespalib/src/tests/stllike/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_hash_test_app TEST
SOURCES
hash_test.cpp
@@ -52,6 +53,6 @@ vespa_add_executable(vespalib_replace_variable_test_app TEST
replace_variable_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_replace_variable_test_app COMMAND vespalib_replace_variable_test_app)
diff --git a/vespalib/src/tests/time/CMakeLists.txt b/vespalib/src/tests/time/CMakeLists.txt
index e43bd9097e5..ba639f2392e 100644
--- a/vespalib/src/tests/time/CMakeLists.txt
+++ b/vespalib/src/tests/time/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_time_box_test_app TEST
SOURCES
time_box_test.cpp
@@ -11,6 +12,6 @@ vespa_add_executable(vespalib_time_test_app TEST
time_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_time_test_app COMMAND vespalib_time_test_app)
diff --git a/vespalib/src/tests/typify/CMakeLists.txt b/vespalib/src/tests/typify/CMakeLists.txt
index 29e95af1988..c8e53d6baca 100644
--- a/vespalib/src/tests/typify/CMakeLists.txt
+++ b/vespalib/src/tests/typify/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_typify_test_app TEST
SOURCES
typify_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_typify_test_app COMMAND vespalib_typify_test_app)
diff --git a/vespalib/src/tests/util/reusable_set/CMakeLists.txt b/vespalib/src/tests/util/reusable_set/CMakeLists.txt
index 9c46b5ba61e..edbbc2ff11f 100644
--- a/vespalib/src/tests/util/reusable_set/CMakeLists.txt
+++ b/vespalib/src/tests/util/reusable_set/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_reusable_set_test_app TEST
SOURCES
reusable_set_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_reusable_set_test_app COMMAND vespalib_reusable_set_test_app)
diff --git a/vespalib/src/tests/visit_ranges/CMakeLists.txt b/vespalib/src/tests/visit_ranges/CMakeLists.txt
index de94b2ebb1e..3c51d7c1e34 100644
--- a/vespalib/src/tests/visit_ranges/CMakeLists.txt
+++ b/vespalib/src/tests/visit_ranges/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalib_visit_ranges_test_app TEST
SOURCES
visit_ranges_test.cpp
DEPENDS
vespalib
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalib_visit_ranges_test_app COMMAND vespalib_visit_ranges_test_app)
diff --git a/vespalib/src/vespa/vespalib/text/utf8.cpp b/vespalib/src/vespa/vespalib/text/utf8.cpp
index 660178aaaa1..58b587d45b5 100644
--- a/vespalib/src/vespa/vespalib/text/utf8.cpp
+++ b/vespalib/src/vespa/vespalib/text/utf8.cpp
@@ -175,8 +175,9 @@ Utf8ReaderForZTS::getComplexChar(unsigned char firstbyte, uint32_t fallback)
}
-Utf8Writer&
-Utf8Writer::putChar(uint32_t codepoint)
+template <typename Target>
+Utf8Writer<Target>&
+Utf8Writer<Target>::putChar(uint32_t codepoint)
{
if (codepoint < 0x80) {
_target.push_back((char)codepoint);
@@ -229,5 +230,23 @@ Utf8Writer::putChar(uint32_t codepoint)
return *this;
}
+template class Utf8Writer<vespalib::string>;
+template class Utf8Writer<std::string>;
-} // namespace vespalib
+template <typename T>
+T Utf8::filter_invalid_sequences(const T& input)
+{
+ T retval;
+ Utf8Reader reader(input.c_str(), input.size());
+ Utf8Writer writer(retval);
+ while (reader.hasMore()) {
+ uint32_t ch = reader.getChar();
+ writer.putChar(ch);
+ }
+ return retval;
+}
+
+template vespalib::string Utf8::filter_invalid_sequences(const vespalib::string&);
+template std::string Utf8::filter_invalid_sequences(const std::string&);
+
+} // namespace
diff --git a/vespalib/src/vespa/vespalib/text/utf8.h b/vespalib/src/vespa/vespalib/text/utf8.h
index 0c75203fbbe..e65aaee9708 100644
--- a/vespalib/src/vespa/vespalib/text/utf8.h
+++ b/vespalib/src/vespa/vespalib/text/utf8.h
@@ -28,6 +28,15 @@ public:
};
/**
+ * Filter a string (std::string or vespalib::string)
+ * and replace any invalid UTF8 sequences with the
+ * standard replacement char U+FFFD; note that any
+ * UTF-8 encoded surrogates are also considered invalid.
+ **/
+ template <typename T>
+ static T filter_invalid_sequences(const T& input);
+
+ /**
* check if a byte is valid as the first byte of an UTF-8 character.
* @param c the byte to be checked
* @return true if a valid UTF-8 character can start with this byte
@@ -155,7 +164,7 @@ protected:
first_high_surrogate = 0xD800,
last_high_surrogate = 0xDBFF,
first_low_surrogate = 0xDC00,
- last_low_surrogate = 0xDCFF
+ last_low_surrogate = 0xDFFF
};
};
@@ -321,9 +330,10 @@ public:
/**
* @brief Writer class that appends UTF-8 characters to a string
**/
+template <typename Target>
class Utf8Writer : public Utf8
{
- string &_target;
+ Target &_target;
public:
/**
* construct a writer appending to the given string
@@ -331,7 +341,7 @@ public:
* that the writer will append to. Must be writable
* and must be kept alive while the writer is active.
**/
- Utf8Writer(string &target) : _target(target) {}
+ Utf8Writer(Target &target) : _target(target) {}
/**
* append the given character to the target string.
diff --git a/vespalog/src/test/log_message/CMakeLists.txt b/vespalog/src/test/log_message/CMakeLists.txt
index 51c888f67f3..a42e7f24bf3 100644
--- a/vespalog/src/test/log_message/CMakeLists.txt
+++ b/vespalog/src/test/log_message/CMakeLists.txt
@@ -1,9 +1,10 @@
# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+find_package(GTest REQUIRED)
vespa_add_executable(vespalog_log_message_test_app TEST
SOURCES
log_message_test.cpp
DEPENDS
vespalog
- gtest
+ GTest::GTest
)
vespa_add_test(NAME vespalog_log_message_test_app COMMAND vespalog_log_message_test_app)