diff --git a/include/knowhere/cluster/cluster.h b/include/knowhere/cluster/cluster.h index 84fc340e0..6343c8cc3 100644 --- a/include/knowhere/cluster/cluster.h +++ b/include/knowhere/cluster/cluster.h @@ -117,16 +117,16 @@ class Cluster { } expected - Train(const DataSet& dataset, const Json& json); + Train(const DataSet& dataset, const Json& json) noexcept; expected - Assign(const DataSet& dataset); + Assign(const DataSet& dataset) noexcept; expected - GetCentroids() const; + GetCentroids() const noexcept; std::string - Type() const; + Type() const noexcept; ~Cluster() { if (node == nullptr) diff --git a/include/knowhere/cluster/cluster_factory.h b/include/knowhere/cluster/cluster_factory.h index 8b2c60c20..4f6f26a15 100644 --- a/include/knowhere/cluster/cluster_factory.h +++ b/include/knowhere/cluster/cluster_factory.h @@ -24,12 +24,12 @@ class ClusterFactory { public: template expected> - Create(const std::string& name, const Object& object = nullptr); + Create(const std::string& name, const Object& object = nullptr) noexcept; template const ClusterFactory& Register(const std::string& name, std::function(const Object&)> func); static ClusterFactory& - Instance(); + Instance() noexcept; private: struct FunMapValueBase { diff --git a/include/knowhere/comp/brute_force.h b/include/knowhere/comp/brute_force.h index cf0782be0..0ef7e45ed 100644 --- a/include/knowhere/comp/brute_force.h +++ b/include/knowhere/comp/brute_force.h @@ -28,43 +28,44 @@ class BruteForce { template static expected Search(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, const BitsetView& bitset, - milvus::OpContext* op_context = nullptr); + milvus::OpContext* op_context = nullptr) noexcept; template static Status SearchWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, float* dis, - const Json& config, const BitsetView& bitset, milvus::OpContext* op_context = nullptr); + const Json& config, const BitsetView& bitset, milvus::OpContext* op_context = nullptr) noexcept; template static Status SearchOnChunkWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, float* dis, - const Json& config, const BitsetView& bitset, milvus::OpContext* op_context = nullptr); + const Json& config, const BitsetView& bitset, + milvus::OpContext* op_context = nullptr) noexcept; template static expected RangeSearch(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset, milvus::OpContext* op_context = nullptr); + const BitsetView& bitset, milvus::OpContext* op_context = nullptr) noexcept; // Perform row oriented sparse vector brute force search. static expected SearchSparse(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset, milvus::OpContext* op_context = nullptr); + const BitsetView& bitset, milvus::OpContext* op_context = nullptr) noexcept; static Status SearchSparseWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, sparse::label_t* ids, float* dis, - const Json& config, const BitsetView& bitset, milvus::OpContext* op_context = nullptr); + const Json& config, const BitsetView& bitset, milvus::OpContext* op_context = nullptr) noexcept; template static expected> AnnIterator(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, const BitsetView& bitset, bool use_knowhere_search_pool = true, - milvus::OpContext* op_context = nullptr); + milvus::OpContext* op_context = nullptr) noexcept; template static expected> AnnIteratorOnChunk(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, const BitsetView& bitset, bool use_knowhere_search_pool = true, - milvus::OpContext* op_context = nullptr); + milvus::OpContext* op_context = nullptr) noexcept; }; } // namespace knowhere diff --git a/include/knowhere/expected.h b/include/knowhere/expected.h index bdf1a1fcb..45430b564 100644 --- a/include/knowhere/expected.h +++ b/include/knowhere/expected.h @@ -13,9 +13,21 @@ #define EXPECTED_H #include +#include #include +#include +#include #include +#include #include +#include +#include + +#if defined(SWIG) +#define KNOWHERE_NODISCARD +#else +#define KNOWHERE_NODISCARD [[nodiscard]] +#endif namespace knowhere { @@ -52,8 +64,67 @@ enum class Status { brute_force_inner_error = 30, emb_list_inner_error = 31, aisaq_error = 32, + knowhere_inner_error = 33, }; +enum class StatusCategory { + success = 0, + input_error = 1, + inner_error = 2, +}; + +inline constexpr StatusCategory +StatusCategoryOf(knowhere::Status status) { + switch (status) { + case knowhere::Status::success: + return StatusCategory::success; + case knowhere::Status::invalid_args: + case knowhere::Status::invalid_param_in_json: + case knowhere::Status::out_of_range_in_json: + case knowhere::Status::type_conflict_in_json: + case knowhere::Status::invalid_metric_type: + case knowhere::Status::empty_index: + case knowhere::Status::not_implemented: + case knowhere::Status::index_not_trained: + case knowhere::Status::index_already_trained: + case knowhere::Status::invalid_value_in_json: + case knowhere::Status::arithmetic_overflow: + case knowhere::Status::invalid_binary_set: + case knowhere::Status::invalid_instruction_set: + case knowhere::Status::invalid_index_error: + case knowhere::Status::invalid_cluster_error: + case knowhere::Status::invalid_serialized_index_type: + return StatusCategory::input_error; + case knowhere::Status::faiss_inner_error: + case knowhere::Status::hnsw_inner_error: + case knowhere::Status::malloc_error: + case knowhere::Status::diskann_inner_error: + case knowhere::Status::disk_file_error: + case knowhere::Status::cuvs_inner_error: + case knowhere::Status::cuda_runtime_error: + case knowhere::Status::cluster_inner_error: + case knowhere::Status::timeout: + case knowhere::Status::internal_error: + case knowhere::Status::sparse_inner_error: + case knowhere::Status::brute_force_inner_error: + case knowhere::Status::emb_list_inner_error: + case knowhere::Status::aisaq_error: + case knowhere::Status::knowhere_inner_error: + default: + return StatusCategory::inner_error; + } +} + +inline constexpr bool +IsInputError(knowhere::Status status) { + return StatusCategoryOf(status) == StatusCategory::input_error; +} + +inline constexpr bool +IsInnerError(knowhere::Status status) { + return StatusCategoryOf(status) == StatusCategory::inner_error; +} + inline std::string Status2String(knowhere::Status status) { switch (status) { @@ -113,13 +184,15 @@ Status2String(knowhere::Status status) { return "emb_list inner error"; case knowhere::Status::aisaq_error: return "internal AiSAQ error"; + case knowhere::Status::knowhere_inner_error: + return "knowhere inner error"; default: return "unexpected status"; } } template -class expected { +class KNOWHERE_NODISCARD expected { public: template expected(Args&&... args) : val(std::make_optional(std::forward(args)...)), err(Status::success) { @@ -202,6 +275,94 @@ class expected { std::string msg; }; +#if !defined(SWIG) + +namespace detail { + +template +struct is_expected : std::false_type {}; + +template +struct is_expected> : std::true_type {}; + +template +inline constexpr bool is_expected_v = is_expected>::value; + +template +struct expected_value; + +template +struct expected_value> { + using type = T; +}; + +inline std::string +ExceptionMessage(const char* prefix, const std::string& what) { + if (what.empty()) { + return prefix; + } + return std::string(prefix) + ": " + what; +} + +template +std::decay_t +GuardedFailure(Status status, std::string msg) noexcept { + using Result = std::decay_t; + if constexpr (std::is_same_v) { + return status; + } else if constexpr (is_expected_v) { + using Value = typename expected_value::type; + return expected::Err(status, std::move(msg)); + } else if constexpr (std::is_same_v) { + return false; + } else if constexpr (std::is_integral_v || std::is_floating_point_v) { + return Result{}; + } else if constexpr (std::is_same_v) { + return {}; + } else if constexpr (std::is_default_constructible_v) { + return Result{}; + } else { + static_assert(std::is_default_constructible_v, + "GuardedCall requires Status, expected, or a default-constructible return type"); + } +} + +template +std::decay_t +GuardedCallFailure(Status status, std::string msg) noexcept { + if constexpr (std::is_void_v) { + return; + } else { + return GuardedFailure(status, std::move(msg)); + } +} + +} // namespace detail + +template +std::decay_t> +GuardedCall(Func&& func, Args&&... args) noexcept { + using Result = std::invoke_result_t; + try { + if constexpr (std::is_void_v) { + std::invoke(std::forward(func), std::forward(args)...); + return; + } else { + return std::invoke(std::forward(func), std::forward(args)...); + } + } catch (const std::bad_alloc& e) { + return detail::GuardedCallFailure(Status::malloc_error, + detail::ExceptionMessage("bad alloc", e.what())); + } catch (const std::exception& e) { + return detail::GuardedCallFailure(Status::knowhere_inner_error, + detail::ExceptionMessage("unhandled exception", e.what())); + } catch (...) { + return detail::GuardedCallFailure(Status::knowhere_inner_error, "unknown exception"); + } +} + +#endif + // Evaluates expr that returns a Status. Does nothing if the returned Status is // a Status::success, otherwise returns the Status from the current function. #define RETURN_IF_ERROR(expr) \ diff --git a/include/knowhere/index/index.h b/include/knowhere/index/index.h index 6a2c4d8f8..5c7e7180c 100644 --- a/include/knowhere/index/index.h +++ b/include/knowhere/index/index.h @@ -140,70 +140,70 @@ class Index { } Status - Build(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true); + Build(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true) noexcept; #ifdef KNOWHERE_WITH_CARDINAL const std::shared_ptr BuildAsync(const DataSetPtr dataset, const Json& json, - const std::chrono::seconds timeout = std::chrono::seconds::max()); + const std::chrono::seconds timeout = std::chrono::seconds::max()) noexcept; #else const std::shared_ptr - BuildAsync(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true); + BuildAsync(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true) noexcept; #endif Status - Train(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true); + Train(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true) noexcept; Status - Add(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true); + Add(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool = true) noexcept; expected Search(const DataSetPtr dataset, const Json& json, const BitsetView& bitset, - milvus::OpContext* op_context = nullptr) const; + milvus::OpContext* op_context = nullptr) const noexcept; expected> AnnIterator(const DataSetPtr dataset, const Json& json, const BitsetView& bitset, - bool use_knowhere_search_pool = true, milvus::OpContext* op_context = nullptr) const; + bool use_knowhere_search_pool = true, milvus::OpContext* op_context = nullptr) const noexcept; expected RangeSearch(const DataSetPtr dataset, const Json& json, const BitsetView& bitset, - milvus::OpContext* op_context = nullptr) const; + milvus::OpContext* op_context = nullptr) const noexcept; expected - GetVectorByIds(const DataSetPtr dataset, milvus::OpContext* op_context = nullptr) const; + GetVectorByIds(const DataSetPtr dataset, milvus::OpContext* op_context = nullptr) const noexcept; bool - HasRawData(const std::string& metric_type) const; + HasRawData(const std::string& metric_type) const noexcept; bool - IsAdditionalScalarSupported(bool is_mv_only) const; + IsAdditionalScalarSupported(bool is_mv_only) const noexcept; expected - GetIndexMeta(const Json& json) const; + GetIndexMeta(const Json& json) const noexcept; Status - Serialize(BinarySet& binset) const; + Serialize(BinarySet& binset) const noexcept; Status - Deserialize(const BinarySet& binset, const Json& json = {}); + Deserialize(const BinarySet& binset, const Json& json = {}) noexcept; Status - DeserializeFromFile(const std::string& filename, const Json& json = {}); + DeserializeFromFile(const std::string& filename, const Json& json = {}) noexcept; int64_t - Dim() const; + Dim() const noexcept; int64_t - Size() const; + Size() const noexcept; int64_t - Count() const; + Count() const noexcept; std::string - Type() const; + Type() const noexcept; [[nodiscard]] bool - LoadIndexWithStream() const; + LoadIndexWithStream() const noexcept; ~Index() { if (node == nullptr) diff --git a/include/knowhere/index/index_factory.h b/include/knowhere/index/index_factory.h index e8e2308b5..b8bf6abbc 100644 --- a/include/knowhere/index/index_factory.h +++ b/include/knowhere/index/index_factory.h @@ -28,7 +28,7 @@ class IndexFactory { public: template expected> - Create(const std::string& name, const int32_t& version, const Object& object = nullptr); + Create(const std::string& name, const int32_t& version, const Object& object = nullptr) noexcept; template const IndexFactory& @@ -36,18 +36,18 @@ class IndexFactory { const uint64_t features); static IndexFactory& - Instance(); + Instance() noexcept; typedef std::tuple>, std::set, std::set> GlobalIndexTable; bool - FeatureCheck(const std::string& name, uint64_t feature) const; + FeatureCheck(const std::string& name, uint64_t feature) const noexcept; static const std::map& - GetIndexFeatures(); + GetIndexFeatures() noexcept; static GlobalIndexTable& - StaticIndexTableInstance(); + StaticIndexTableInstance() noexcept; private: struct FunMapValueBase { diff --git a/include/knowhere/index/index_static.h b/include/knowhere/index/index_static.h index e019df6d1..f6279f613 100644 --- a/include/knowhere/index/index_static.h +++ b/include/knowhere/index/index_static.h @@ -60,11 +60,11 @@ class IndexStaticFaced { * @return generate the config binding with the corresponding vector index */ static std::unique_ptr - CreateConfig(const knowhere::IndexType& indexType, const knowhere::IndexVersion& version); + CreateConfig(const knowhere::IndexType& indexType, const knowhere::IndexVersion& version) noexcept; static knowhere::Status ConfigCheck(const knowhere::IndexType& indexType, const knowhere::IndexVersion& version, - const knowhere::Json& params, std::string& msg); + const knowhere::Json& params, std::string& msg) noexcept; /** * @brief estimate the memory and disk resource usage before index loading by index params @@ -79,7 +79,7 @@ class IndexStaticFaced { static expected EstimateLoadResource(const knowhere::IndexType& indexType, const knowhere::IndexVersion& version, const uint64_t file_size_in_bytes, const int64_t num_rows, const int64_t dim, - const knowhere::Json& params); + const knowhere::Json& params) noexcept; /** * @brief determine whether the index contains the raw data before loading the index by index params @@ -90,7 +90,7 @@ class IndexStaticFaced { */ static bool HasRawData(const knowhere::IndexType& indexType, const knowhere::IndexVersion& version, - const knowhere::Json& params); + const knowhere::Json& params) noexcept; template IndexStaticFaced& diff --git a/src/cluster/cluster.cc b/src/cluster/cluster.cc index 64d9b31b6..72d3c7d0d 100644 --- a/src/cluster/cluster.cc +++ b/src/cluster/cluster.cc @@ -35,32 +35,34 @@ LoadConfig(Config* cfg, const Json& json, knowhere::PARAM_TYPE param_type, const template inline expected -Cluster::Train(const DataSet& dataset, const Json& json) { - auto cfg = this->node->CreateConfig(); - std::string msg; - auto status = LoadConfig(cfg.get(), json, knowhere::CLUSTER, "Train", &msg); - if (status != Status::success) { - return expected::Err(status, msg); - } - return this->node->Train(dataset, *cfg); +Cluster::Train(const DataSet& dataset, const Json& json) noexcept { + return GuardedCall([&]() -> expected { + auto cfg = this->node->CreateConfig(); + std::string msg; + auto status = LoadConfig(cfg.get(), json, knowhere::CLUSTER, "Train", &msg); + if (status != Status::success) { + return expected::Err(status, msg); + } + return this->node->Train(dataset, *cfg); + }); } template inline expected -Cluster::Assign(const DataSet& dataset) { - return this->node->Assign(dataset); +Cluster::Assign(const DataSet& dataset) noexcept { + return GuardedCall([&]() { return this->node->Assign(dataset); }); } template inline expected -Cluster::GetCentroids() const { - return this->node->GetCentroids(); +Cluster::GetCentroids() const noexcept { + return GuardedCall([&]() { return this->node->GetCentroids(); }); } template inline std::string -Cluster::Type() const { - return this->node->Type(); +Cluster::Type() const noexcept { + return GuardedCall([&]() { return this->node->Type(); }); } template class Cluster; diff --git a/src/cluster/cluster_factory.cc b/src/cluster/cluster_factory.cc index fbcde0b8f..82edca92b 100644 --- a/src/cluster/cluster_factory.cc +++ b/src/cluster/cluster_factory.cc @@ -17,18 +17,20 @@ namespace knowhere { template expected> -ClusterFactory::Create(const std::string& name, const Object& object) { - static_assert(KnowhereDataTypeCheck::value == true); - auto& func_mapping_ = MapInstance(); - auto key = GetKey(name); - if (func_mapping_.find(key) == func_mapping_.end()) { - LOG_KNOWHERE_ERROR_ << "failed to find cluster type " << key << " in factory"; - return expected>::Err(Status::invalid_cluster_error, "cluster type not supported"); - } - LOG_KNOWHERE_INFO_ << "use key " << key << " to create knowhere cluster worker " << name; - auto fun_map_v = (FunMapValue>*)(func_mapping_[key].get()); +ClusterFactory::Create(const std::string& name, const Object& object) noexcept { + return GuardedCall([&]() -> expected> { + static_assert(KnowhereDataTypeCheck::value == true); + auto& func_mapping_ = MapInstance(); + auto key = GetKey(name); + if (func_mapping_.find(key) == func_mapping_.end()) { + LOG_KNOWHERE_ERROR_ << "failed to find cluster type " << key << " in factory"; + return expected>::Err(Status::invalid_cluster_error, "cluster type not supported"); + } + LOG_KNOWHERE_INFO_ << "use key " << key << " to create knowhere cluster worker " << name; + auto fun_map_v = static_cast>*>(func_mapping_[key].get()); - return fun_map_v->fun_value(object); + return fun_map_v->fun_value(object); + }); } template @@ -43,7 +45,7 @@ ClusterFactory::Register(const std::string& name, std::function +Status +BruteForceSearchWithBufImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, float* dis, + const Json& config, const BitsetView& bitset, milvus::OpContext* op_context); + +template +Status +BruteForceSearchOnChunkWithBufImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, + float* dis, const Json& config, const BitsetView& bitset, + milvus::OpContext* op_context); + +template +expected> +BruteForceAnnIteratorOnChunkImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, bool use_knowhere_search_pool, + milvus::OpContext* op_context); + template expected -BruteForce::Search(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset_, milvus::OpContext* op_context) { +BruteForceSearchImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset_, milvus::OpContext* op_context) { BruteForceConfig cfg; std::string msg; auto status = Config::Load(cfg, config, knowhere::SEARCH, &msg); @@ -161,11 +178,11 @@ BruteForce::Search(const DataSetPtr base_dataset, const DataSetPtr query_dataset Status search_status; bool base_is_chunk = base_dataset->GetIsChunk(); if (base_is_chunk) { - search_status = SearchOnChunkWithBuf(base_dataset, query_dataset, labels.get(), distances.get(), - config, bitset_, op_context); + search_status = BruteForceSearchOnChunkWithBufImpl(base_dataset, query_dataset, labels.get(), + distances.get(), config, bitset_, op_context); } else { - search_status = SearchWithBuf(base_dataset, query_dataset, labels.get(), distances.get(), config, - bitset_, op_context); + search_status = BruteForceSearchWithBufImpl(base_dataset, query_dataset, labels.get(), + distances.get(), config, bitset_, op_context); } if (search_status != Status::success) { return expected::Err(search_status, "search failed"); @@ -445,11 +462,12 @@ brute_force_emb_list_impl(const void* xq, size_t query_el_idx, const void* xb, i template Status -BruteForce::SearchWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, float* dis, - const Json& config, const BitsetView& bitset_, milvus::OpContext* op_context) { +BruteForceSearchWithBufImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, float* dis, + const Json& config, const BitsetView& bitset_, milvus::OpContext* op_context) { auto base_is_chunk = base_dataset->GetIsChunk(); if (base_is_chunk) { - return SearchOnChunkWithBuf(base_dataset, query_dataset, ids, dis, config, bitset_, op_context); + return BruteForceSearchOnChunkWithBufImpl(base_dataset, query_dataset, ids, dis, config, bitset_, + op_context); } auto xb = base_dataset->GetTensor(); auto nb = base_dataset->GetRows(); @@ -587,9 +605,9 @@ BruteForce::SearchWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_ template Status -BruteForce::SearchOnChunkWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, - float* dis, const Json& config, const BitsetView& bitset_, - milvus::OpContext* op_context) { +BruteForceSearchOnChunkWithBufImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, + float* dis, const Json& config, const BitsetView& bitset_, + milvus::OpContext* op_context) { auto base_is_chunk = base_dataset->GetIsChunk(); if (!base_is_chunk) { LOG_KNOWHERE_ERROR_ << "Base dataset is not chunk, should NOT use it."; @@ -886,8 +904,8 @@ BruteForce::SearchOnChunkWithBuf(const DataSetPtr base_dataset, const DataSetPtr */ template expected -BruteForce::RangeSearch(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset_, milvus::OpContext* op_context) { +BruteForceRangeSearchImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset_, milvus::OpContext* op_context) { DataSetPtr query(query_dataset); auto xb = base_dataset->GetTensor(); auto nb = base_dataset->GetRows(); @@ -1103,9 +1121,9 @@ BruteForce::RangeSearch(const DataSetPtr base_dataset, const DataSetPtr query_da } Status -BruteForce::SearchSparseWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, sparse::label_t* labels, - float* distances, const Json& config, const BitsetView& bitset, - milvus::OpContext* op_context) { +BruteForceSearchSparseWithBufImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, + sparse::label_t* labels, float* distances, const Json& config, + const BitsetView& bitset, milvus::OpContext* op_context) { auto base = static_cast*>(base_dataset->GetTensor()); auto rows = base_dataset->GetRows(); auto xb_id_offset = base_dataset->GetTensorBeginId(); @@ -1205,8 +1223,8 @@ BruteForce::SearchSparseWithBuf(const DataSetPtr base_dataset, const DataSetPtr } expected -BruteForce::SearchSparse(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset, milvus::OpContext* op_context) { +BruteForceSearchSparseImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, milvus::OpContext* op_context) { auto nq = query_dataset->GetRows(); BruteForceConfig cfg; std::string msg; @@ -1219,18 +1237,22 @@ BruteForce::SearchSparse(const DataSetPtr base_dataset, const DataSetPtr query_d auto labels = std::make_unique(nq * topk); auto distances = std::make_unique(nq * topk); - SearchSparseWithBuf(base_dataset, query_dataset, labels.get(), distances.get(), config, bitset, op_context); + auto search_status = BruteForceSearchSparseWithBufImpl(base_dataset, query_dataset, labels.get(), distances.get(), + config, bitset, op_context); + if (search_status != Status::success) { + return expected::Err(search_status, "sparse search failed"); + } return GenResultDataSet(nq, topk, std::move(labels), std::move(distances)); } template expected> -BruteForce::AnnIterator(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset_, bool use_knowhere_search_pool, milvus::OpContext* op_context) { +BruteForceAnnIteratorImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset_, bool use_knowhere_search_pool, milvus::OpContext* op_context) { auto base_is_chunk = base_dataset->GetIsChunk(); if (base_is_chunk) { - return AnnIteratorOnChunk(base_dataset, query_dataset, config, bitset_, use_knowhere_search_pool, - op_context); + return BruteForceAnnIteratorOnChunkImpl(base_dataset, query_dataset, config, bitset_, + use_knowhere_search_pool, op_context); } auto nb = base_dataset->GetRows(); auto dim = base_dataset->GetDim(); @@ -1413,9 +1435,9 @@ BruteForce::AnnIterator(const DataSetPtr base_dataset, const DataSetPtr query_da template expected> -BruteForce::AnnIteratorOnChunk(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset_, bool use_knowhere_search_pool, - milvus::OpContext* op_context) { +BruteForceAnnIteratorOnChunkImpl(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset_, bool use_knowhere_search_pool, + milvus::OpContext* op_context) { auto base_is_chunk = base_dataset->GetIsChunk(); if (!base_is_chunk) { LOG_KNOWHERE_ERROR_ << "Base dataset is not chunk, should NOT use it."; @@ -1658,10 +1680,10 @@ BruteForce::AnnIteratorOnChunk(const DataSetPtr base_dataset, const DataSetPtr q template <> expected> -BruteForce::AnnIterator>(const DataSetPtr base_dataset, - const DataSetPtr query_dataset, const Json& config, - const BitsetView& bitset, bool use_knowhere_search_pool, - milvus::OpContext* op_context) { +BruteForceAnnIteratorImpl>(const DataSetPtr base_dataset, + const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, bool use_knowhere_search_pool, + milvus::OpContext* op_context) { auto rows = base_dataset->GetRows(); auto xb_id_offset = base_dataset->GetTensorBeginId(); auto nq = query_dataset->GetRows(); @@ -1751,6 +1773,84 @@ BruteForce::AnnIterator>(const DataSetPtr bas return vec; } +template +expected +BruteForce::Search(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> expected { + return BruteForceSearchImpl(base_dataset, query_dataset, config, bitset, op_context); + }); +} + +template +Status +BruteForce::SearchWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, float* dis, + const Json& config, const BitsetView& bitset, milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> Status { + return BruteForceSearchWithBufImpl(base_dataset, query_dataset, ids, dis, config, bitset, op_context); + }); +} + +template +Status +BruteForce::SearchOnChunkWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, int64_t* ids, + float* dis, const Json& config, const BitsetView& bitset, + milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> Status { + return BruteForceSearchOnChunkWithBufImpl(base_dataset, query_dataset, ids, dis, config, bitset, + op_context); + }); +} + +template +expected +BruteForce::RangeSearch(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> expected { + return BruteForceRangeSearchImpl(base_dataset, query_dataset, config, bitset, op_context); + }); +} + +Status +BruteForce::SearchSparseWithBuf(const DataSetPtr base_dataset, const DataSetPtr query_dataset, sparse::label_t* labels, + float* distances, const Json& config, const BitsetView& bitset, + milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> Status { + return BruteForceSearchSparseWithBufImpl(base_dataset, query_dataset, labels, distances, config, bitset, + op_context); + }); +} + +expected +BruteForce::SearchSparse(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> expected { + return BruteForceSearchSparseImpl(base_dataset, query_dataset, config, bitset, op_context); + }); +} + +template +expected> +BruteForce::AnnIterator(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, bool use_knowhere_search_pool, + milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> expected> { + return BruteForceAnnIteratorImpl(base_dataset, query_dataset, config, bitset, + use_knowhere_search_pool, op_context); + }); +} + +template +expected> +BruteForce::AnnIteratorOnChunk(const DataSetPtr base_dataset, const DataSetPtr query_dataset, const Json& config, + const BitsetView& bitset, bool use_knowhere_search_pool, + milvus::OpContext* op_context) noexcept { + return GuardedCall([&]() -> expected> { + return BruteForceAnnIteratorOnChunkImpl(base_dataset, query_dataset, config, bitset, + use_knowhere_search_pool, op_context); + }); +} + template knowhere::expected knowhere::BruteForce::Search(const knowhere::DataSetPtr base_dataset, const knowhere::DataSetPtr query_dataset, const knowhere::Json& config, @@ -1855,5 +1955,9 @@ knowhere::BruteForce::AnnIterator(const knowhere::DataSetPtr bas const knowhere::DataSetPtr query_dataset, const knowhere::Json& config, const knowhere::BitsetView& bitset, bool use_knowhere_search_pool, milvus::OpContext* op_context); +template knowhere::expected> +knowhere::BruteForce::AnnIterator>( + const knowhere::DataSetPtr base_dataset, const knowhere::DataSetPtr query_dataset, const knowhere::Json& config, + const knowhere::BitsetView& bitset, bool use_knowhere_search_pool, milvus::OpContext* op_context); } // namespace knowhere diff --git a/src/index/index.cc b/src/index/index.cc index d1f076b17..901f4ce39 100644 --- a/src/index/index.cc +++ b/src/index/index.cc @@ -39,370 +39,396 @@ LoadConfig(BaseConfig* cfg, const Json& json, knowhere::PARAM_TYPE param_type, c #ifdef KNOWHERE_WITH_CARDINAL template inline const std::shared_ptr -Index::BuildAsync(const DataSetPtr dataset, const Json& json, const std::chrono::seconds timeout) { - auto pool = ThreadPool::GetGlobalBuildThreadPool(); - auto interrupt = std::make_shared(timeout); - interrupt->Set(pool->push([this, dataset, json, interrupt]() { - auto cfg = this->node->CreateConfig(); - RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Build")); +Index::BuildAsync(const DataSetPtr dataset, const Json& json, const std::chrono::seconds timeout) noexcept { + return GuardedCall([&]() -> std::shared_ptr { + auto pool = ThreadPool::GetGlobalBuildThreadPool(); + auto interrupt = std::make_shared(timeout); + interrupt->Set(pool->push([this, dataset, json, interrupt]() { + return GuardedCall([&]() -> Status { + auto cfg = this->node->CreateConfig(); + RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Build")); #if defined(NOT_COMPILE_FOR_SWIG) && !defined(KNOWHERE_WITH_LIGHT) - TimeRecorder rc("BuildAsync index ", 2); - auto res = this->node->BulidAsyncEmbListIfNeed(dataset, std::move(cfg), interrupt.get()); - auto time = rc.ElapseFromBegin("done"); - time *= 0.000001; // convert to s - knowhere_build_latency.Observe(time); + TimeRecorder rc("BuildAsync index ", 2); + auto res = this->node->BulidAsyncEmbListIfNeed(dataset, std::move(cfg), interrupt.get()); + auto time = rc.ElapseFromBegin("done"); + time *= 0.000001; // convert to s + knowhere_build_latency.Observe(time); #else - auto res = this->node->BulidAsyncEmbListIfNeed(dataset, std::move(cfg), Interrupt.get()); + auto res = this->node->BulidAsyncEmbListIfNeed(dataset, std::move(cfg), Interrupt.get()); #endif - return res; - })); - return interrupt; + return res; + }); + })); + return interrupt; + }); } #else template inline const std::shared_ptr -Index::BuildAsync(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) { - auto pool = ThreadPool::GetGlobalBuildThreadPool(); - auto interrupt = std::make_shared(); - interrupt->Set(pool->push([this, dataset, json]() { return this->Build(dataset, json); })); - return interrupt; +Index::BuildAsync(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) noexcept { + return GuardedCall([&]() -> std::shared_ptr { + auto pool = ThreadPool::GetGlobalBuildThreadPool(); + auto interrupt = std::make_shared(); + interrupt->Set(pool->push([this, dataset, json, use_knowhere_build_pool]() { + return this->Build(dataset, json, use_knowhere_build_pool); + })); + return interrupt; + }); } #endif template inline Status -Index::Build(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) { - auto cfg = this->node->CreateConfig(); - RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Build")); +Index::Build(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) noexcept { + return GuardedCall([&]() -> Status { + auto cfg = this->node->CreateConfig(); + RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Build")); #if defined(NOT_COMPILE_FOR_SWIG) && !defined(KNOWHERE_WITH_LIGHT) - TimeRecorder rc("Build index", 2); - auto res = this->node->BuildEmbListIfNeed(dataset, std::move(cfg), use_knowhere_build_pool); - auto time = rc.ElapseFromBegin("done"); - time *= 0.000001; // convert to s - knowhere_build_latency.Observe(time); + TimeRecorder rc("Build index", 2); + auto res = this->node->BuildEmbListIfNeed(dataset, std::move(cfg), use_knowhere_build_pool); + auto time = rc.ElapseFromBegin("done"); + time *= 0.000001; // convert to s + knowhere_build_latency.Observe(time); #else - auto res = this->node->BuildEmbListIfNeed(dataset, std::move(cfg), use_knowhere_build_pool); + auto res = this->node->BuildEmbListIfNeed(dataset, std::move(cfg), use_knowhere_build_pool); #endif - return res; + return res; + }); } template inline Status -Index::Train(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) { - bool is_emb_list = dataset->Get(knowhere::meta::EMB_LIST_OFFSET) != nullptr; - if (is_emb_list) { - // should use Index::Build instead. - LOG_KNOWHERE_WARNING_ << "EmbList should use Index::Build instead."; - return Status::emb_list_inner_error; - } - auto cfg = this->node->CreateConfig(); - std::string msg; - RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Train", &msg)); - return this->node->Train(dataset, std::move(cfg), use_knowhere_build_pool); +Index::Train(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) noexcept { + return GuardedCall([&]() -> Status { + bool is_emb_list = dataset->Get(knowhere::meta::EMB_LIST_OFFSET) != nullptr; + if (is_emb_list) { + // should use Index::Build instead. + LOG_KNOWHERE_WARNING_ << "EmbList should use Index::Build instead."; + return Status::emb_list_inner_error; + } + auto cfg = this->node->CreateConfig(); + std::string msg; + RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Train", &msg)); + return this->node->Train(dataset, std::move(cfg), use_knowhere_build_pool); + }); } template inline Status -Index::Add(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) { - auto cfg = this->node->CreateConfig(); - std::string msg; - RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Add", &msg)); - return this->node->AddEmbListIfNeed(dataset, std::move(cfg), use_knowhere_build_pool); +Index::Add(const DataSetPtr dataset, const Json& json, bool use_knowhere_build_pool) noexcept { + return GuardedCall([&]() -> Status { + auto cfg = this->node->CreateConfig(); + std::string msg; + RETURN_IF_ERROR(LoadConfig(cfg.get(), json, knowhere::TRAIN, "Add", &msg)); + return this->node->AddEmbListIfNeed(dataset, std::move(cfg), use_knowhere_build_pool); + }); } template inline expected Index::Search(const DataSetPtr dataset, const Json& json, const BitsetView& bitset_, - milvus::OpContext* op_context) const { - auto cfg = this->node->CreateConfig(); - std::string msg; - const Status load_status = LoadConfig(cfg.get(), json, knowhere::SEARCH, "Search", &msg); - if (load_status != Status::success) { - return expected::Err(load_status, msg); - } - // when index is immutable, bitset size should always equal to data count in index - // when index is mutable, it could happen that data count larger than bitset size, see - // https://github.com/zilliztech/knowhere/issues/70 - // so something must be wrong at caller side when passed bitset size larger than data count - if (bitset_.size() > (size_t)this->Count()) { - msg = fmt::format("bitset size should be <= data count, but we get bitset size: {}, data count: {}", - bitset_.size(), this->Count()); - LOG_KNOWHERE_ERROR_ << msg; - return expected::Err(Status::invalid_args, msg); - } - - BitsetView bitset; - if (bitset_.count() == 0) { - // traverse bitset to get the filtered out num - auto filtered_out_num = bitset_.get_filtered_out_num_(); - bitset = BitsetView(bitset_.data(), bitset_.size(), filtered_out_num); - } else { - // if bitset has filtered out num, use it - bitset = bitset_; - } + milvus::OpContext* op_context) const noexcept { + return GuardedCall([&]() -> expected { + auto cfg = this->node->CreateConfig(); + std::string msg; + const Status load_status = LoadConfig(cfg.get(), json, knowhere::SEARCH, "Search", &msg); + if (load_status != Status::success) { + return expected::Err(load_status, msg); + } + // when index is immutable, bitset size should always equal to data count in index + // when index is mutable, it could happen that data count larger than bitset size, see + // https://github.com/zilliztech/knowhere/issues/70 + // so something must be wrong at caller side when passed bitset size larger than data count + if (bitset_.size() > (size_t)this->Count()) { + msg = fmt::format("bitset size should be <= data count, but we get bitset size: {}, data count: {}", + bitset_.size(), this->Count()); + LOG_KNOWHERE_ERROR_ << msg; + return expected::Err(Status::invalid_args, msg); + } + + BitsetView bitset; + if (bitset_.count() == 0) { + // traverse bitset to get the filtered out num + auto filtered_out_num = bitset_.get_filtered_out_num_(); + bitset = BitsetView(bitset_.data(), bitset_.size(), filtered_out_num); + } else { + // if bitset has filtered out num, use it + bitset = bitset_; + } #if defined(NOT_COMPILE_FOR_SWIG) && !defined(KNOWHERE_WITH_LIGHT) - const BaseConfig& b_cfg = static_cast(*cfg); - // LCOV_EXCL_START - std::shared_ptr span = nullptr; - if (b_cfg.trace_id.has_value()) { - auto trace_id_str = tracer::GetIDFromHexStr(b_cfg.trace_id.value()); - auto span_id_str = tracer::GetIDFromHexStr(b_cfg.span_id.value()); - auto ctx = tracer::TraceContext{(uint8_t*)trace_id_str.c_str(), (uint8_t*)span_id_str.c_str(), - (uint8_t)b_cfg.trace_flags.value()}; - span = tracer::StartSpan("knowhere search", &ctx); - span->SetAttribute(meta::METRIC_TYPE, b_cfg.metric_type.value()); - span->SetAttribute(meta::TOPK, b_cfg.k.value()); - span->SetAttribute(meta::ROWS, Count()); - span->SetAttribute(meta::DIM, Dim()); - span->SetAttribute(meta::NQ, dataset->GetRows()); - } - // LCOV_EXCL_STOP - - TimeRecorder rc("Search"); - bool has_trace_id = b_cfg.trace_id.has_value(); - auto k = cfg->k.value(); - auto res = this->node->SearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); - auto time = rc.ElapseFromBegin("done"); - time *= 0.001; // convert to ms - knowhere_search_latency.Observe(time); - knowhere_search_topk.Observe(k); - - // LCOV_EXCL_START - if (has_trace_id) { - span->End(); - } - // LCOV_EXCL_STOP + const BaseConfig& b_cfg = static_cast(*cfg); + // LCOV_EXCL_START + std::shared_ptr span = nullptr; + if (b_cfg.trace_id.has_value()) { + auto trace_id_str = tracer::GetIDFromHexStr(b_cfg.trace_id.value()); + auto span_id_str = tracer::GetIDFromHexStr(b_cfg.span_id.value()); + auto ctx = tracer::TraceContext{(uint8_t*)trace_id_str.c_str(), (uint8_t*)span_id_str.c_str(), + (uint8_t)b_cfg.trace_flags.value()}; + span = tracer::StartSpan("knowhere search", &ctx); + span->SetAttribute(meta::METRIC_TYPE, b_cfg.metric_type.value()); + span->SetAttribute(meta::TOPK, b_cfg.k.value()); + span->SetAttribute(meta::ROWS, Count()); + span->SetAttribute(meta::DIM, Dim()); + span->SetAttribute(meta::NQ, dataset->GetRows()); + } + // LCOV_EXCL_STOP + + TimeRecorder rc("Search"); + bool has_trace_id = b_cfg.trace_id.has_value(); + auto k = cfg->k.value(); + auto res = this->node->SearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); + auto time = rc.ElapseFromBegin("done"); + time *= 0.001; // convert to ms + knowhere_search_latency.Observe(time); + knowhere_search_topk.Observe(k); + + // LCOV_EXCL_START + if (has_trace_id) { + span->End(); + } + // LCOV_EXCL_STOP #else - auto res = this->node->SearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); + auto res = this->node->SearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); #endif - return res; + return res; + }); } template inline expected>> Index::AnnIterator(const DataSetPtr dataset, const Json& json, const BitsetView& bitset_, - bool use_knowhere_search_pool, milvus::OpContext* op_context) const { - auto cfg = this->node->CreateConfig(); - std::string msg; - Status status = LoadConfig(cfg.get(), json, knowhere::ITERATOR, "Iterator", &msg); - if (status != Status::success) { - return expected>>::Err(status, msg); - } - // when index is immutable, bitset size should always equal to data count in index - // when index is mutable, it could happen that data count larger than bitset size, see - // https://github.com/zilliztech/knowhere/issues/70 - // so something must be wrong at caller side when passed bitset size larger than data count - if (bitset_.size() > (size_t)this->Count()) { - msg = fmt::format("bitset size should be <= data count, but we get bitset size: {}, data count: {}", - bitset_.size(), this->Count()); - LOG_KNOWHERE_ERROR_ << msg; - return expected>>::Err(Status::invalid_args, msg); - } - - const auto bitset = BitsetView(bitset_.data(), bitset_.size(), bitset_.get_filtered_out_num_()); + bool use_knowhere_search_pool, milvus::OpContext* op_context) const noexcept { + return GuardedCall([&]() -> expected>> { + auto cfg = this->node->CreateConfig(); + std::string msg; + Status status = LoadConfig(cfg.get(), json, knowhere::ITERATOR, "Iterator", &msg); + if (status != Status::success) { + return expected>>::Err(status, msg); + } + // when index is immutable, bitset size should always equal to data count in index + // when index is mutable, it could happen that data count larger than bitset size, see + // https://github.com/zilliztech/knowhere/issues/70 + // so something must be wrong at caller side when passed bitset size larger than data count + if (bitset_.size() > (size_t)this->Count()) { + msg = fmt::format("bitset size should be <= data count, but we get bitset size: {}, data count: {}", + bitset_.size(), this->Count()); + LOG_KNOWHERE_ERROR_ << msg; + return expected>>::Err(Status::invalid_args, msg); + } + + const auto bitset = BitsetView(bitset_.data(), bitset_.size(), bitset_.get_filtered_out_num_()); #if defined(NOT_COMPILE_FOR_SWIG) && !defined(KNOWHERE_WITH_LIGHT) - // note that this time includes only the initial search phase of iterator. - TimeRecorder rc("AnnIterator"); - auto res = - this->node->AnnIteratorEmbListIfNeed(dataset, std::move(cfg), bitset, use_knowhere_search_pool, op_context); - auto time = rc.ElapseFromBegin("done"); - time *= 0.001; // convert to ms - knowhere_search_latency.Observe(time); + // note that this time includes only the initial search phase of iterator. + TimeRecorder rc("AnnIterator"); + auto res = + this->node->AnnIteratorEmbListIfNeed(dataset, std::move(cfg), bitset, use_knowhere_search_pool, op_context); + auto time = rc.ElapseFromBegin("done"); + time *= 0.001; // convert to ms + knowhere_search_latency.Observe(time); #else - auto res = - this->node->AnnIteratorEmbListIfNeed(dataset, std::move(cfg), bitset, use_knowhere_search_pool, op_context); + auto res = + this->node->AnnIteratorEmbListIfNeed(dataset, std::move(cfg), bitset, use_knowhere_search_pool, op_context); #endif - return res; + return res; + }); } template inline expected Index::RangeSearch(const DataSetPtr dataset, const Json& json, const BitsetView& bitset_, - milvus::OpContext* op_context) const { - auto cfg = this->node->CreateConfig(); - std::string msg; - auto status = LoadConfig(cfg.get(), json, knowhere::RANGE_SEARCH, "RangeSearch", &msg); - if (status != Status::success) { - return expected::Err(status, std::move(msg)); - } - // when index is immutable, bitset size should always equal to data count in index - // when index is mutable, it could happen that data count larger than bitset size, see - // https://github.com/zilliztech/knowhere/issues/70 - // so something must be wrong at caller side when passed bitset size larger than data count - if (bitset_.size() > (size_t)this->Count()) { - msg = fmt::format("bitset size should be <= data count, but we get bitset size: {}, data count: {}", - bitset_.size(), this->Count()); - LOG_KNOWHERE_ERROR_ << msg; - return expected::Err(Status::invalid_args, msg); - } - - const auto bitset = BitsetView(bitset_.data(), bitset_.size(), bitset_.get_filtered_out_num_()); + milvus::OpContext* op_context) const noexcept { + return GuardedCall([&]() -> expected { + auto cfg = this->node->CreateConfig(); + std::string msg; + auto status = LoadConfig(cfg.get(), json, knowhere::RANGE_SEARCH, "RangeSearch", &msg); + if (status != Status::success) { + return expected::Err(status, std::move(msg)); + } + // when index is immutable, bitset size should always equal to data count in index + // when index is mutable, it could happen that data count larger than bitset size, see + // https://github.com/zilliztech/knowhere/issues/70 + // so something must be wrong at caller side when passed bitset size larger than data count + if (bitset_.size() > (size_t)this->Count()) { + msg = fmt::format("bitset size should be <= data count, but we get bitset size: {}, data count: {}", + bitset_.size(), this->Count()); + LOG_KNOWHERE_ERROR_ << msg; + return expected::Err(Status::invalid_args, msg); + } + + const auto bitset = BitsetView(bitset_.data(), bitset_.size(), bitset_.get_filtered_out_num_()); #if defined(NOT_COMPILE_FOR_SWIG) && !defined(KNOWHERE_WITH_LIGHT) - const BaseConfig& b_cfg = static_cast(*cfg); - // LCOV_EXCL_START - std::shared_ptr span = nullptr; - if (b_cfg.trace_id.has_value()) { - auto trace_id_str = tracer::GetIDFromHexStr(b_cfg.trace_id.value()); - auto span_id_str = tracer::GetIDFromHexStr(b_cfg.span_id.value()); - auto ctx = tracer::TraceContext{(uint8_t*)trace_id_str.c_str(), (uint8_t*)span_id_str.c_str(), - (uint8_t)b_cfg.trace_flags.value()}; - span = tracer::StartSpan("knowhere range search", &ctx); - span->SetAttribute(meta::METRIC_TYPE, b_cfg.metric_type.value()); - span->SetAttribute(meta::RADIUS, b_cfg.radius.value()); - if (b_cfg.range_filter.value() != defaultRangeFilter) { - span->SetAttribute(meta::RANGE_FILTER, b_cfg.range_filter.value()); + const BaseConfig& b_cfg = static_cast(*cfg); + // LCOV_EXCL_START + std::shared_ptr span = nullptr; + if (b_cfg.trace_id.has_value()) { + auto trace_id_str = tracer::GetIDFromHexStr(b_cfg.trace_id.value()); + auto span_id_str = tracer::GetIDFromHexStr(b_cfg.span_id.value()); + auto ctx = tracer::TraceContext{(uint8_t*)trace_id_str.c_str(), (uint8_t*)span_id_str.c_str(), + (uint8_t)b_cfg.trace_flags.value()}; + span = tracer::StartSpan("knowhere range search", &ctx); + span->SetAttribute(meta::METRIC_TYPE, b_cfg.metric_type.value()); + span->SetAttribute(meta::RADIUS, b_cfg.radius.value()); + if (b_cfg.range_filter.value() != defaultRangeFilter) { + span->SetAttribute(meta::RANGE_FILTER, b_cfg.range_filter.value()); + } + span->SetAttribute(meta::ROWS, Count()); + span->SetAttribute(meta::DIM, Dim()); + span->SetAttribute(meta::NQ, dataset->GetRows()); } - span->SetAttribute(meta::ROWS, Count()); - span->SetAttribute(meta::DIM, Dim()); - span->SetAttribute(meta::NQ, dataset->GetRows()); - } - // LCOV_EXCL_STOP - - TimeRecorder rc("Range Search"); - bool has_trace_id = b_cfg.trace_id.has_value(); - auto res = this->node->RangeSearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); - auto time = rc.ElapseFromBegin("done"); - time *= 0.001; // convert to ms - knowhere_range_search_latency.Observe(time); - - // LCOV_EXCL_START - if (has_trace_id) { - span->End(); - } - // LCOV_EXCL_STOP + // LCOV_EXCL_STOP + + TimeRecorder rc("Range Search"); + bool has_trace_id = b_cfg.trace_id.has_value(); + auto res = this->node->RangeSearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); + auto time = rc.ElapseFromBegin("done"); + time *= 0.001; // convert to ms + knowhere_range_search_latency.Observe(time); + + // LCOV_EXCL_START + if (has_trace_id) { + span->End(); + } + // LCOV_EXCL_STOP #else - auto res = this->node->RangeSearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); + auto res = this->node->RangeSearchEmbListIfNeed(dataset, std::move(cfg), bitset, op_context); #endif - return res; + return res; + }); } template inline expected -Index::GetVectorByIds(const DataSetPtr dataset, milvus::OpContext* op_context) const { - return this->node->GetVectorByIds(dataset, op_context); +Index::GetVectorByIds(const DataSetPtr dataset, milvus::OpContext* op_context) const noexcept { + return GuardedCall([&]() { return this->node->GetVectorByIds(dataset, op_context); }); } template inline bool -Index::HasRawData(const std::string& metric_type) const { - return this->node->HasRawData(metric_type); +Index::HasRawData(const std::string& metric_type) const noexcept { + return GuardedCall([&]() { return this->node->HasRawData(metric_type); }); } template inline bool -Index::IsAdditionalScalarSupported(bool is_mv_only) const { - return this->node->IsAdditionalScalarSupported(is_mv_only); +Index::IsAdditionalScalarSupported(bool is_mv_only) const noexcept { + return GuardedCall([&]() { return this->node->IsAdditionalScalarSupported(is_mv_only); }); } template inline expected -Index::GetIndexMeta(const Json& json) const { - auto cfg = this->node->CreateConfig(); - std::string msg; - auto status = LoadConfig(cfg.get(), json, knowhere::FEDER, "GetIndexMeta", &msg); - if (status != Status::success) { - return expected::Err(status, msg); - } - return this->node->GetIndexMeta(std::move(cfg)); +Index::GetIndexMeta(const Json& json) const noexcept { + return GuardedCall([&]() -> expected { + auto cfg = this->node->CreateConfig(); + std::string msg; + auto status = LoadConfig(cfg.get(), json, knowhere::FEDER, "GetIndexMeta", &msg); + if (status != Status::success) { + return expected::Err(status, msg); + } + return this->node->GetIndexMeta(std::move(cfg)); + }); } template inline Status -Index::Serialize(BinarySet& binset) const { - return this->node->SerializeEmbListIfNeed(binset); +Index::Serialize(BinarySet& binset) const noexcept { + return GuardedCall([&]() { return this->node->SerializeEmbListIfNeed(binset); }); } template inline Status -Index::Deserialize(const BinarySet& binset, const Json& json) { - Json json_(json); - auto cfg = this->node->CreateConfig(); - { - auto res = Config::FormatAndCheck(*cfg, json_); - LOG_KNOWHERE_DEBUG_ << "Deserialize config dump: " << json_.dump(); +Index::Deserialize(const BinarySet& binset, const Json& json) noexcept { + return GuardedCall([&]() -> Status { + Json json_(json); + auto cfg = this->node->CreateConfig(); + { + auto res = Config::FormatAndCheck(*cfg, json_); + LOG_KNOWHERE_DEBUG_ << "Deserialize config dump: " << json_.dump(); + if (res != Status::success) { + return res; + } + } + auto res = Config::Load(*cfg, json_, knowhere::DESERIALIZE); if (res != Status::success) { return res; } - } - auto res = Config::Load(*cfg, json_, knowhere::DESERIALIZE); - if (res != Status::success) { - return res; - } #if defined(NOT_COMPILE_FOR_SWIG) && !defined(KNOWHERE_WITH_LIGHT) - TimeRecorder rc("Load index", 2); - res = this->node->DeserializeEmbListIfNeed(binset, std::move(cfg)); - auto time = rc.ElapseFromBegin("done"); - time *= 0.001; // convert to ms - knowhere_load_latency.Observe(time); + TimeRecorder rc("Load index", 2); + res = this->node->DeserializeEmbListIfNeed(binset, std::move(cfg)); + auto time = rc.ElapseFromBegin("done"); + time *= 0.001; // convert to ms + knowhere_load_latency.Observe(time); #else - res = this->node->DeserializeEmbListIfNeed(binset, std::move(cfg)); + res = this->node->DeserializeEmbListIfNeed(binset, std::move(cfg)); #endif - return res; + return res; + }); } template inline Status -Index::DeserializeFromFile(const std::string& filename, const Json& json) { - Json json_(json); - auto cfg = this->node->CreateConfig(); - { - auto res = Config::FormatAndCheck(*cfg, json_); - LOG_KNOWHERE_DEBUG_ << "DeserializeFromFile config dump: " << json_.dump(); +Index::DeserializeFromFile(const std::string& filename, const Json& json) noexcept { + return GuardedCall([&]() -> Status { + Json json_(json); + auto cfg = this->node->CreateConfig(); + { + auto res = Config::FormatAndCheck(*cfg, json_); + LOG_KNOWHERE_DEBUG_ << "DeserializeFromFile config dump: " << json_.dump(); + if (res != Status::success) { + return res; + } + } + auto res = Config::Load(*cfg, json_, knowhere::DESERIALIZE_FROM_FILE); if (res != Status::success) { return res; } - } - auto res = Config::Load(*cfg, json_, knowhere::DESERIALIZE_FROM_FILE); - if (res != Status::success) { - return res; - } #if defined(NOT_COMPILE_FOR_SWIG) && !defined(KNOWHERE_WITH_LIGHT) - TimeRecorder rc("Load index from file", 2); - res = this->node->DeserializeFromFileIfNeed(filename, std::move(cfg)); - auto time = rc.ElapseFromBegin("done"); - time *= 0.001; // convert to ms - knowhere_load_latency.Observe(time); + TimeRecorder rc("Load index from file", 2); + res = this->node->DeserializeFromFileIfNeed(filename, std::move(cfg)); + auto time = rc.ElapseFromBegin("done"); + time *= 0.001; // convert to ms + knowhere_load_latency.Observe(time); #else - res = this->node->DeserializeFromFileIfNeed(filename, std::move(cfg)); + res = this->node->DeserializeFromFileIfNeed(filename, std::move(cfg)); #endif - return res; + return res; + }); } template inline int64_t -Index::Dim() const { - return this->node->Dim(); +Index::Dim() const noexcept { + return GuardedCall([&]() { return this->node->Dim(); }); } template inline int64_t -Index::Size() const { - return this->node->Size(); +Index::Size() const noexcept { + return GuardedCall([&]() { return this->node->Size(); }); } template inline int64_t -Index::Count() const { - return this->node->Count(); +Index::Count() const noexcept { + return GuardedCall([&]() { return this->node->Count(); }); } template inline std::string -Index::Type() const { - return this->node->Type(); +Index::Type() const noexcept { + return GuardedCall([&]() { return this->node->Type(); }); } template inline bool -Index::LoadIndexWithStream() const { - return this->node->LoadIndexWithStream(); +Index::LoadIndexWithStream() const noexcept { + return GuardedCall([&]() { return this->node->LoadIndexWithStream(); }); } template class Index; diff --git a/src/index/index_factory.cc b/src/index/index_factory.cc index 12422a4b5..06fd349ed 100644 --- a/src/index/index_factory.cc +++ b/src/index/index_factory.cc @@ -43,29 +43,31 @@ checkGpuAvailable(const std::string& name) { template expected> -IndexFactory::Create(const std::string& name, const int32_t& version, const Object& object) { - static_assert(KnowhereDataTypeCheck::value == true); - auto& func_mapping_ = MapInstance(); - auto key = GetKey(name); - if (func_mapping_.find(key) == func_mapping_.end()) { - LOG_KNOWHERE_ERROR_ << "failed to find index " << key << " in factory"; - return expected>::Err(Status::invalid_index_error, "index not supported"); - } - LOG_KNOWHERE_INFO_ << "use key " << key << " to create knowhere index " << name << " with version " << version; - auto fun_map_v = (FunMapValue>*)(func_mapping_[key].get()); +IndexFactory::Create(const std::string& name, const int32_t& version, const Object& object) noexcept { + return GuardedCall([&]() -> expected> { + static_assert(KnowhereDataTypeCheck::value == true); + auto& func_mapping_ = MapInstance(); + auto key = GetKey(name); + if (func_mapping_.find(key) == func_mapping_.end()) { + LOG_KNOWHERE_ERROR_ << "failed to find index " << key << " in factory"; + return expected>::Err(Status::invalid_index_error, "index not supported"); + } + LOG_KNOWHERE_INFO_ << "use key " << key << " to create knowhere index " << name << " with version " << version; + auto fun_map_v = static_cast>*>(func_mapping_[key].get()); #ifdef KNOWHERE_WITH_CUVS - if (!checkGpuAvailable(name)) { - return expected>::Err(Status::cuda_runtime_error, "gpu not available"); - } + if (!checkGpuAvailable(name)) { + return expected>::Err(Status::cuda_runtime_error, "gpu not available"); + } #endif - if (name == knowhere::IndexEnum::INDEX_FAISS_SCANN && !faiss::support_pq_fast_scan) { - LOG_KNOWHERE_ERROR_ << "SCANN index is not supported on the current CPU model"; - return expected>::Err(Status::invalid_index_error, - "SCANN index is not supported on the current CPU model"); - } + if (name == knowhere::IndexEnum::INDEX_FAISS_SCANN && !faiss::support_pq_fast_scan) { + LOG_KNOWHERE_ERROR_ << "SCANN index is not supported on the current CPU model"; + return expected>::Err(Status::invalid_index_error, + "SCANN index is not supported on the current CPU model"); + } - return fun_map_v->fun_value(version, object); + return fun_map_v->fun_value(version, object); + }); } template @@ -89,7 +91,7 @@ IndexFactory::Register(const std::string& name, std::function(c } IndexFactory& -IndexFactory::Instance() { +IndexFactory::Instance() noexcept { static IndexFactory factory; return factory; } @@ -109,20 +111,22 @@ IndexFactory::FeatureMapInstance() { } IndexFactory::GlobalIndexTable& -IndexFactory::StaticIndexTableInstance() { +IndexFactory::StaticIndexTableInstance() noexcept { static GlobalIndexTable static_index_table; return static_index_table; } bool -IndexFactory::FeatureCheck(const std::string& name, uint64_t feature) const { - auto& feature_mapping_ = IndexFactory::FeatureMapInstance(); - assert(feature_mapping_.find(name) != feature_mapping_.end()); - return (feature_mapping_[name] & feature) == feature; +IndexFactory::FeatureCheck(const std::string& name, uint64_t feature) const noexcept { + return GuardedCall([&]() { + auto& feature_mapping_ = IndexFactory::FeatureMapInstance(); + assert(feature_mapping_.find(name) != feature_mapping_.end()); + return (feature_mapping_[name] & feature) == feature; + }); } const std::map& -IndexFactory::GetIndexFeatures() { +IndexFactory::GetIndexFeatures() noexcept { return FeatureMapInstance(); } diff --git a/src/index/index_static.cc b/src/index/index_static.cc index f3d66ccda..f5f96c87d 100644 --- a/src/index/index_static.cc +++ b/src/index/index_static.cc @@ -20,6 +20,13 @@ namespace knowhere { inline Status LoadStaticConfig(BaseConfig* cfg, const Json& json, knowhere::PARAM_TYPE param_type, const std::string& method, std::string* const msg = nullptr) { + if (cfg == nullptr) { + if (msg != nullptr) { + *msg = "failed to create config"; + } + LOG_KNOWHERE_ERROR_ << method << " failed to create config"; + return Status::knowhere_inner_error; + } Json json_(json); auto res = Config::FormatAndCheck(*cfg, json_, msg); LOG_KNOWHERE_DEBUG_ << method << " config dump: " << json_.dump(); @@ -36,31 +43,35 @@ IndexStaticFaced::Instance() { template std::unique_ptr -IndexStaticFaced::CreateConfig(const IndexType& indexType, const IndexVersion& version) { - if (Instance().staticCreateConfigMap.find(indexType) != Instance().staticCreateConfigMap.end()) { - return Instance().staticCreateConfigMap[indexType](); - } - LOG_KNOWHERE_WARNING_ << "unhandled create config for indexType: " << indexType; - return std::make_unique(); +IndexStaticFaced::CreateConfig(const IndexType& indexType, const IndexVersion& version) noexcept { + return GuardedCall([&]() -> std::unique_ptr { + if (Instance().staticCreateConfigMap.find(indexType) != Instance().staticCreateConfigMap.end()) { + return Instance().staticCreateConfigMap[indexType](); + } + LOG_KNOWHERE_WARNING_ << "unhandled create config for indexType: " << indexType; + return std::make_unique(); + }); } template knowhere::Status IndexStaticFaced::ConfigCheck(const IndexType& indexType, const IndexVersion& version, const Json& params, - std::string& msg) { - auto cfg = IndexStaticFaced::CreateConfig(indexType, version); - - const Status status = LoadStaticConfig(cfg.get(), params, knowhere::PARAM_TYPE::TRAIN, "ConfigCheck", &msg); - if (status != Status::success) { - LOG_KNOWHERE_ERROR_ << "Load Config failed, msg = " << msg; - return status; - } - - if (Instance().staticConfigCheckMap.find(indexType) != Instance().staticConfigCheckMap.end()) { - return Instance().staticConfigCheckMap[indexType](*cfg, knowhere::PARAM_TYPE::TRAIN, msg); - } - - return knowhere::Status::success; + std::string& msg) noexcept { + return GuardedCall([&]() -> Status { + auto cfg = IndexStaticFaced::CreateConfig(indexType, version); + + const Status status = LoadStaticConfig(cfg.get(), params, knowhere::PARAM_TYPE::TRAIN, "ConfigCheck", &msg); + if (status != Status::success) { + LOG_KNOWHERE_ERROR_ << "Load Config failed, msg = " << msg; + return status; + } + + if (Instance().staticConfigCheckMap.find(indexType) != Instance().staticConfigCheckMap.end()) { + return Instance().staticConfigCheckMap[indexType](*cfg, knowhere::PARAM_TYPE::TRAIN, msg); + } + + return knowhere::Status::success; + }); } template @@ -68,21 +79,25 @@ expected IndexStaticFaced::EstimateLoadResource(const knowhere::IndexType& indexType, const knowhere::IndexVersion& version, const uint64_t file_size_in_bytes, const int64_t num_rows, - const int64_t dim, const knowhere::Json& params) { - auto cfg = IndexStaticFaced::CreateConfig(indexType, version); - - std::string msg; - const Status status = LoadStaticConfig(cfg.get(), params, knowhere::STATIC, "EstimateLoadResource", &msg); - if (status != Status::success) { - LOG_KNOWHERE_ERROR_ << "Load Config failed, msg = " << msg; - return expected::Err(status, msg); - } - - if (Instance().staticEstimateLoadResourceMap.find(indexType) != Instance().staticEstimateLoadResourceMap.end()) { - return Instance().staticEstimateLoadResourceMap[indexType](file_size_in_bytes, num_rows, dim, *cfg, version); - } - - return InternalEstimateLoadResource(file_size_in_bytes, num_rows, dim, *cfg, version); + const int64_t dim, const knowhere::Json& params) noexcept { + return GuardedCall([&]() -> expected { + auto cfg = IndexStaticFaced::CreateConfig(indexType, version); + + std::string msg; + const Status status = LoadStaticConfig(cfg.get(), params, knowhere::STATIC, "EstimateLoadResource", &msg); + if (status != Status::success) { + LOG_KNOWHERE_ERROR_ << "Load Config failed, msg = " << msg; + return expected::Err(status, msg); + } + + if (Instance().staticEstimateLoadResourceMap.find(indexType) != + Instance().staticEstimateLoadResourceMap.end()) { + return Instance().staticEstimateLoadResourceMap[indexType](file_size_in_bytes, num_rows, dim, *cfg, + version); + } + + return InternalEstimateLoadResource(file_size_in_bytes, num_rows, dim, *cfg, version); + }); } template @@ -103,32 +118,36 @@ IndexStaticFaced::InternalEstimateLoadResource(const uint64_t file_siz template bool -IndexStaticFaced::HasRawData(const IndexType& indexType, const IndexVersion& version, const Json& params) { - auto cfg = IndexStaticFaced::CreateConfig(indexType, version); - std::string msg; - const Status status = LoadStaticConfig(cfg.get(), params, knowhere::STATIC, "HasRawData", &msg); - - if (status != Status::success) { - LOG_KNOWHERE_ERROR_ << "Load Config failed, msg = " << msg; - return false; - } - - if (Instance().staticHasRawDataMap.find(indexType) != Instance().staticHasRawDataMap.end()) { - return Instance().staticHasRawDataMap[indexType](*cfg, version); - } - - static std::set has_raw_data_index_set = { - IndexEnum::INDEX_FAISS_BIN_IDMAP, IndexEnum::INDEX_FAISS_BIN_IVFFLAT, IndexEnum::INDEX_FAISS_IVFFLAT, - IndexEnum::INDEX_FAISS_IVFFLAT_CC}; - - static std::set has_raw_data_index_alias_set = {"IVFBIN", "BINFLAT", "IVFFLAT", "IVFFLATCC"}; - - if (has_raw_data_index_set.find(indexType) != has_raw_data_index_set.end() || - has_raw_data_index_alias_set.find(indexType) != has_raw_data_index_alias_set.end()) { - return true; - } - - return InternalStaticHasRawData(*cfg, version); +IndexStaticFaced::HasRawData(const IndexType& indexType, const IndexVersion& version, + const Json& params) noexcept { + return GuardedCall([&]() { + auto cfg = IndexStaticFaced::CreateConfig(indexType, version); + std::string msg; + const Status status = LoadStaticConfig(cfg.get(), params, knowhere::STATIC, "HasRawData", &msg); + + if (status != Status::success) { + LOG_KNOWHERE_ERROR_ << "Load Config failed, msg = " << msg; + return false; + } + + if (Instance().staticHasRawDataMap.find(indexType) != Instance().staticHasRawDataMap.end()) { + return Instance().staticHasRawDataMap[indexType](*cfg, version); + } + + static std::set has_raw_data_index_set = { + IndexEnum::INDEX_FAISS_BIN_IDMAP, IndexEnum::INDEX_FAISS_BIN_IVFFLAT, IndexEnum::INDEX_FAISS_IVFFLAT, + IndexEnum::INDEX_FAISS_IVFFLAT_CC}; + + static std::set has_raw_data_index_alias_set = {"IVFBIN", "BINFLAT", "IVFFLAT", + "IVFFLATCC"}; + + if (has_raw_data_index_set.find(indexType) != has_raw_data_index_set.end() || + has_raw_data_index_alias_set.find(indexType) != has_raw_data_index_alias_set.end()) { + return true; + } + + return InternalStaticHasRawData(*cfg, version); + }); } template diff --git a/tests/ut/test_diskann.cc b/tests/ut/test_diskann.cc index 8cac733b2..f7176b557 100644 --- a/tests/ut/test_diskann.cc +++ b/tests/ut/test_diskann.cc @@ -838,7 +838,8 @@ TEST_CASE("Test_AiSAQ_dynamic_cache", "[diskann]") { read_page_cache_size += 4096) { knn_json["pq_read_page_cache_size"] = read_page_cache_size; auto start = std::chrono::high_resolution_clock::now(); - diskann.Search(query_ds, knn_json, nullptr); + auto loop_results = diskann.Search(query_ds, knn_json, nullptr); + REQUIRE(loop_results.has_value()); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); std::cout << "*********************** Run time: " << duration.count() << " ms" diff --git a/tests/ut/test_error_code.cc b/tests/ut/test_error_code.cc new file mode 100644 index 000000000..0e6482cd5 --- /dev/null +++ b/tests/ut/test_error_code.cc @@ -0,0 +1,171 @@ +// Copyright (C) 2019-2023 Zilliz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See +// the License for the specific language governing permissions and limitations under the License. + +#include "catch2/catch_test_macros.hpp" +#include "knowhere/comp/brute_force.h" +#include "knowhere/index/index.h" +#include "knowhere/index/index_static.h" + +namespace { + +class ThrowingIndexNode : public knowhere::IndexNode { + public: + knowhere::Status + Train(const knowhere::DataSetPtr, std::shared_ptr, bool) override { + throw std::runtime_error("boom train"); + } + + knowhere::Status + Add(const knowhere::DataSetPtr, std::shared_ptr, bool) override { + throw std::runtime_error("boom add"); + } + + knowhere::expected + Search(const knowhere::DataSetPtr, std::unique_ptr, const knowhere::BitsetView&, + milvus::OpContext*) const override { + throw std::runtime_error("boom search"); + } + + knowhere::expected + RangeSearch(const knowhere::DataSetPtr, std::unique_ptr, const knowhere::BitsetView&, + milvus::OpContext*) const override { + throw std::runtime_error("boom range search"); + } + + knowhere::expected + GetVectorByIds(const knowhere::DataSetPtr, milvus::OpContext*) const override { + throw std::runtime_error("boom get vector"); + } + + bool + HasRawData(const std::string&) const override { + throw std::runtime_error("boom raw data"); + } + + knowhere::expected + GetIndexMeta(std::unique_ptr) const override { + throw std::runtime_error("boom meta"); + } + + knowhere::Status + Serialize(knowhere::BinarySet&) const override { + throw std::runtime_error("boom serialize"); + } + + knowhere::Status + Deserialize(const knowhere::BinarySet&, std::shared_ptr) override { + throw std::runtime_error("boom deserialize"); + } + + knowhere::Status + DeserializeFromFile(const std::string&, std::shared_ptr) override { + throw std::runtime_error("boom deserialize file"); + } + + std::unique_ptr + CreateConfig() const override { + return std::make_unique(); + } + + int64_t + Dim() const override { + throw std::runtime_error("boom dim"); + } + + int64_t + Size() const override { + throw std::runtime_error("boom size"); + } + + int64_t + Count() const override { + throw std::runtime_error("boom count"); + } + + std::string + Type() const override { + throw std::runtime_error("boom type"); + } +}; + +knowhere::Index +CreateThrowingIndex() { + return knowhere::Index::Create(); +} + +class ThrowingStaticIndexNode { + public: + static std::unique_ptr + StaticCreateConfig() { + throw std::runtime_error("boom static create config"); + } +}; + +} // namespace + +TEST_CASE("Status category separates input and inner errors", "[error_code]") { + STATIC_REQUIRE(knowhere::StatusCategoryOf(knowhere::Status::invalid_args) == knowhere::StatusCategory::input_error); + STATIC_REQUIRE(knowhere::StatusCategoryOf(knowhere::Status::invalid_param_in_json) == + knowhere::StatusCategory::input_error); + STATIC_REQUIRE(knowhere::StatusCategoryOf(knowhere::Status::faiss_inner_error) == + knowhere::StatusCategory::inner_error); + STATIC_REQUIRE(knowhere::StatusCategoryOf(knowhere::Status::internal_error) == + knowhere::StatusCategory::inner_error); + STATIC_REQUIRE(knowhere::StatusCategoryOf(knowhere::Status::knowhere_inner_error) == + knowhere::StatusCategory::inner_error); + + REQUIRE(knowhere::IsInputError(knowhere::Status::invalid_metric_type)); + REQUIRE_FALSE(knowhere::IsInputError(knowhere::Status::faiss_inner_error)); + REQUIRE(knowhere::IsInnerError(knowhere::Status::brute_force_inner_error)); + REQUIRE_FALSE(knowhere::IsInnerError(knowhere::Status::invalid_value_in_json)); +} + +TEST_CASE("Index facade APIs are noexcept and convert exceptions to error codes", "[error_code]") { + auto index = CreateThrowingIndex(); + auto ds = std::make_shared(); + knowhere::BinarySet binset; + + STATIC_REQUIRE(noexcept(index.GetVectorByIds(ds))); + STATIC_REQUIRE(noexcept(index.Serialize(binset))); + STATIC_REQUIRE(noexcept(index.Count())); + STATIC_REQUIRE(noexcept(index.Type())); + STATIC_REQUIRE( + noexcept(knowhere::BruteForce::Search(ds, ds, knowhere::Json{}, knowhere::BitsetView{}))); + STATIC_REQUIRE(noexcept(knowhere::BruteForce::SearchWithBuf( + ds, ds, static_cast(nullptr), static_cast(nullptr), knowhere::Json{}, + knowhere::BitsetView{}))); + + const auto get_vector_result = index.GetVectorByIds(ds); + REQUIRE(get_vector_result.error() == knowhere::Status::knowhere_inner_error); + REQUIRE(std::string(get_vector_result.what()).find("boom get vector") != std::string::npos); + + REQUIRE(index.Serialize(binset) == knowhere::Status::knowhere_inner_error); + REQUIRE(index.Count() == 0); + REQUIRE(index.Type().empty()); +} + +TEST_CASE("Index static facade APIs handle config creation failures", "[error_code]") { + const knowhere::IndexType index_type = "THROWING_STATIC_CONFIG"; + constexpr knowhere::IndexVersion version = 0; + knowhere::IndexStaticFaced::Instance().RegisterStaticFunc(index_type); + + std::string msg; + REQUIRE(knowhere::IndexStaticFaced::ConfigCheck(index_type, version, knowhere::Json{}, msg) == + knowhere::Status::knowhere_inner_error); + REQUIRE(msg == "failed to create config"); + + auto resource = knowhere::IndexStaticFaced::EstimateLoadResource(index_type, version, 1024, 10, 4, + knowhere::Json{}); + REQUIRE(resource.error() == knowhere::Status::knowhere_inner_error); + REQUIRE(resource.what() == "failed to create config"); + + REQUIRE_FALSE(knowhere::IndexStaticFaced::HasRawData(index_type, version, knowhere::Json{})); +}