fix(server): Async index builder, part 1#6504
Conversation
|
augment review |
🤖 Augment PR SummarySummary: Begins refactoring search indexing to support asynchronous index builds (per #5752) while preserving current behavior by running the new builder synchronously. Changes:
🤖 Was this summary useful? React with 👍 or 👎 |
|
|
||
| MainLoopFb(prime_table.get(), op_args.db_cntx); | ||
|
|
||
| fiber_.Detach(); // Detach self to be safely deletable |
There was a problem hiding this comment.
Calling fiber_.Detach() from inside the running fiber can drop the last reference to the active FiberInterface while it’s executing (the fb2 implementation expects the final release/resume to happen from another fiber). This looks like it can lead to UB/crash during fiber teardown.
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Its safe, a fiber keeps itself alive until its execution finishes. It can be detached from the very start and even from within itself
| TraverseAllMatching(*base_, op_args, cb); | ||
| // This PR is limited to using the builder synchronously | ||
| while (builder_) | ||
| util::ThisFiber::SleepFor(100us); |
There was a problem hiding this comment.
Currently there's no way for the indexer to fail, it will be handled in the future
|
|
||
| MainLoopFb(prime_table.get(), op_args.db_cntx); | ||
|
|
||
| fiber_.Detach(); // Detach self to be safely deletable |
There was a problem hiding this comment.
Calling on_complete() (which currently deletes the IndexBuilder via builder_.reset()) from inside the worker fiber is a fragile self-destruction pattern: any future access to members after this point would become UB. It may be safer if completion is signaled back to the caller fiber/thread rather than deleting the builder from within itself.
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
I agree its hacky but it's simple. No context really owns the index, but to join the final fiber we have to run it in a suspendable context (read fiber). With ProactorBase::me()->Dispatch({}) to create another fiber to stop a fiber?
|
|
||
| TraverseAllMatching(*base_, op_args, cb); | ||
| // This PR is limited to using the builder synchronously | ||
| while (builder_) |
There was a problem hiding this comment.
Rebuild() now waits in a loop until builder_ is reset by the worker fiber; if the worker fiber exits early or on_complete is skipped, this becomes an infinite wait on the shard fiber. Consider adding a guard/failure path so rebuild can’t block forever on completion.
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Pull request overview
Introduces an IndexBuilder abstraction intended to support asynchronous index construction (per #5752), while keeping current behavior functionally synchronous.
Changes:
- Added
search::IndexBuilder(fiber-based) to traverse the shard table and add matching documents to an index, yielding periodically. - Wired
ShardDocIndex::Rebuild()to useIndexBuilderand wait for completion (sync usage in this PR). - Added plumbing for ownership/forward-declarations (
ShardDocIndexdestructor +builder_member) and updated search CMake target.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/server/search/index_builder.h | Declares new IndexBuilder helper and its fiber-based entrypoints. |
| src/server/search/index_builder.cc | Implements traversal loop with periodic yields and completion callback. |
| src/server/search/doc_index.cc | Switches Rebuild() to use IndexBuilder and adds a synchronous wait loop. |
| src/server/search/doc_index.h | Adds forward declaration + builder_ member and explicit ctor/dtor. |
| src/server/search/doc_index_fallback.cc | Adds ShardDocIndex dtor in non-search build. |
| src/server/search/CMakeLists.txt | Adds index_builder.cc to dfly_search_server when WITH_SEARCH is enabled. |
| ShardDocIndex::~ShardDocIndex() { | ||
| } |
There was a problem hiding this comment.
ShardDocIndex now owns std::unique_ptr<search::IndexBuilder> builder_, but this fallback TU defines ShardDocIndex::~ShardDocIndex() without including the full IndexBuilder definition. That will fail to compile because std::unique_ptr<T> requires T to be complete when the containing class destructor is instantiated. Include server/search/index_builder.h here, or conditionally exclude builder_ from ShardDocIndex when WITH_SEARCH is off.
| void IndexBuilder::MainLoopFb(dfly::DbTable* table, DbContext db_cntx) { | ||
| const auto doc_index = index_->GetInfo().base_index; | ||
|
|
||
| auto cb = [this, doc_index, db_cntx, scratch = std::string{}](PrimeTable::iterator it) mutable { | ||
| PrimeValue& pv = it->second; | ||
| std::string_view key = it->first.GetSlice(&scratch); | ||
|
|
||
| if (doc_index.Matches(key, pv.ObjType())) | ||
| index_->AddDoc(key, db_cntx, pv); | ||
| }; | ||
|
|
||
| PrimeTable::Cursor cursor; | ||
| do { | ||
| cursor = table->prime.Traverse(cursor, cb); | ||
| if (base::CycleClock::ToUsec(util::ThisFiber::GetRunningTimeCycles()) > 500) | ||
| util::ThisFiber::Yield(); | ||
| } while (cursor); |
There was a problem hiding this comment.
IndexBuilder::MainLoopFb duplicates the traversal/yielding logic that already exists as TraverseAllMatching in doc_index.cc. Having two implementations increases the risk they diverge (e.g., yield thresholds, match conditions). Consider factoring the traversal into a shared helper and reuse it here, keeping IndexBuilder focused on async orchestration.
There was a problem hiding this comment.
yes, the goal is to replace it
|
@dranikpg As I understand it, we run index building using all threads. It could be inefficient because, in most cases, even a quarter of threads could utilize the whole memory bandwidth. Have you checked how long should sleep every fiber to avoid CPU resource wasting? |
I only changed classic (sharded) search for now that has to be thread local |
Relevant to #5752