-
Notifications
You must be signed in to change notification settings - Fork 96
perf(hgraph): skip mutex acquisition on immutable index search #2154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -82,10 +82,13 @@ HGraph::KnnSearch(const DatasetPtr& query, | |||||||||||||||||||||||||||||||||||||
| fmt::format("ef_search({}) must in range[1, {}]", params.ef_search, ef_search_threshold)); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| std::shared_lock<std::shared_mutex> force_remove_rlock; | ||||||||||||||||||||||||||||||||||||||
| if (this->support_force_remove()) { | ||||||||||||||||||||||||||||||||||||||
| force_remove_rlock = std::shared_lock<std::shared_mutex>(this->force_remove_mutex_); | ||||||||||||||||||||||||||||||||||||||
| std::shared_lock<std::shared_mutex> shared_lock; | ||||||||||||||||||||||||||||||||||||||
| if (!this->immutable_.load(std::memory_order_acquire)) { | ||||||||||||||||||||||||||||||||||||||
| if (this->support_force_remove()) { | ||||||||||||||||||||||||||||||||||||||
| force_remove_rlock = std::shared_lock<std::shared_mutex>(this->force_remove_mutex_); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| shared_lock = std::shared_lock<std::shared_mutex>(this->global_mutex_); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
84
to
91
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the suggestion. We intentionally use default-construct + conditional move-assign because |
||||||||||||||||||||||||||||||||||||||
| std::shared_lock shared_lock(this->global_mutex_); | ||||||||||||||||||||||||||||||||||||||
| k = std::min(k, GetNumElements()); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| FilterPtr ft = this->create_search_filter(filter, params.use_extra_info_filter); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -357,10 +360,13 @@ HGraph::RangeSearch(const DatasetPtr& query, | |||||||||||||||||||||||||||||||||||||
| this->validate_range_args(query, radius, limited_size); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| std::shared_lock<std::shared_mutex> force_remove_rlock; | ||||||||||||||||||||||||||||||||||||||
| if (this->support_force_remove()) { | ||||||||||||||||||||||||||||||||||||||
| force_remove_rlock = std::shared_lock<std::shared_mutex>(this->force_remove_mutex_); | ||||||||||||||||||||||||||||||||||||||
| std::shared_lock<std::shared_mutex> shared_lock; | ||||||||||||||||||||||||||||||||||||||
| if (!this->immutable_.load(std::memory_order_acquire)) { | ||||||||||||||||||||||||||||||||||||||
| if (this->support_force_remove()) { | ||||||||||||||||||||||||||||||||||||||
| force_remove_rlock = std::shared_lock<std::shared_mutex>(this->force_remove_mutex_); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| shared_lock = std::shared_lock<std::shared_mutex>(this->global_mutex_); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
362
to
369
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the suggestion. We intentionally use default-construct + conditional move-assign because |
||||||||||||||||||||||||||||||||||||||
| std::shared_lock shared_lock(this->global_mutex_); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| InnerSearchParam search_param; | ||||||||||||||||||||||||||||||||||||||
| search_param.ep = this->entry_point_id_; | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -455,10 +461,13 @@ HGraph::SearchWithRequest(const SearchRequest& request) const { | |||||||||||||||||||||||||||||||||||||
| fmt::format("ef_search({}) must in range[1, {}]", params.ef_search, ef_search_threshold)); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| std::shared_lock<std::shared_mutex> force_remove_rlock; | ||||||||||||||||||||||||||||||||||||||
| if (this->support_force_remove()) { | ||||||||||||||||||||||||||||||||||||||
| force_remove_rlock = std::shared_lock<std::shared_mutex>(this->force_remove_mutex_); | ||||||||||||||||||||||||||||||||||||||
| std::shared_lock<std::shared_mutex> shared_lock; | ||||||||||||||||||||||||||||||||||||||
| if (!this->immutable_.load(std::memory_order_acquire)) { | ||||||||||||||||||||||||||||||||||||||
| if (this->support_force_remove()) { | ||||||||||||||||||||||||||||||||||||||
| force_remove_rlock = std::shared_lock<std::shared_mutex>(this->force_remove_mutex_); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| shared_lock = std::shared_lock<std::shared_mutex>(this->global_mutex_); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
463
to
470
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the suggestion. We intentionally use default-construct + conditional move-assign because |
||||||||||||||||||||||||||||||||||||||
| std::shared_lock shared_lock(this->global_mutex_); | ||||||||||||||||||||||||||||||||||||||
| k = std::min(k, GetNumElements()); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Setup reasoning context if expected labels are provided. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While acquiring
add_mutex_inSetImmutable()is intended to prevent a TOCTOU race withTune(), it is currently insufficient becauseCHECK_IMMUTABLE_INDEXis checked outside of any lock inIndexImpl::Tune().If
Tune()passes the check beforeSetImmutable()runs, it will block onadd_mutex_insideHGraph::Tune(). OnceSetImmutable()completes and releases the locks,Tune()will proceed to modify the index even though it has been made immutable. Since search threads skip acquiringglobal_mutex_whenimmutable_istrue, this will lead to concurrent reads and writes onbasic_flatten_codes_and other member variables, causing a severe data race and undefined behavior.To completely resolve this,
HGraph::Tune()must re-checkimmutable_after acquiringadd_mutex_:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching this! Already addressed in commit f895451 — Tune() now re-checks
immutable_after acquiringadd_mutex_: