Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ finalized in place with a date — no renaming/migration step needed.

- **`POST /repos` adopts an index that is already on disk instead of clearing and re-embedding it.** Registering a repo (`codesearch index add`, the git-hook path, or a script adding many repos at once) unconditionally ran the destructive force reindex, so a repo that already had a valid `.codesearch.db` — for example one indexed directly before being registered — lost its index and had to re-embed every file, serialized behind the process-wide job gate. The local `codesearch index add` path already reused an existing index; the serve path now matches it. A new index (no `metadata.json`) and an explicit model change still take the force-rebuild path, because changing the embedding model invalidates every stored vector.

- **Adopting an index on `POST /repos` no longer rebuilds the HNSW graph a second time.** The adoption path's incremental refresh already rebuilds the index once under the job gate; the add-repo task still re-ran a full graph build afterwards, doubling the work (serialized behind the job gate) for an identical result. The extra build now runs only on the force-rebuild path, which builds no index internally.

## [1.6.0] - 2026-10-18

### Fixed
Expand Down
55 changes: 32 additions & 23 deletions src/serve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6002,38 +6002,47 @@ async fn add_repo_handler(
return;
}

// Build vector index from freshly indexed data.
// Build vector index from freshly indexed data — force path only.
// On the adopt path `perform_incremental_refresh_with_stores` already
// rebuilt the HNSW once after its last batch (or left the existing
// valid graph untouched when nothing changed), so a second build here
// would redo the whole graph per adopted registration for an
// identical result.
// build_index() is CPU-heavy — offload to the blocking pool, under the
// same process-wide job permit as every other heavy pass. The wait is
// cancellation-aware: a repo removed while queued behind another build
// must not keep its stores (and a queue position) parked.
let _job_permit = match stores.acquire_job_permit_or_cancel(&token_for_task).await {
JobAcquire::Ready(permit) => permit,
JobAcquire::Cancelled => {
if !adopt_existing {
let _job_permit = match stores.acquire_job_permit_or_cancel(&token_for_task).await {
JobAcquire::Ready(permit) => permit,
JobAcquire::Cancelled => {
state_bg.end_indexing(&alias_bg, IndexingOwner::Reindex);
return;
}
};
// Re-check liveness after the wait: a removal that landed while the
// task was parked must not resurrect the alias's vector index.
if !state_bg.is_alias_live(&alias_bg, &token_for_task) {
state_bg.end_indexing(&alias_bg, IndexingOwner::Reindex);
return;
}
};
// Re-check liveness after the wait: a removal that landed while the
// task was parked must not resurrect the alias's vector index.
if !state_bg.is_alias_live(&alias_bg, &token_for_task) {
state_bg.end_indexing(&alias_bg, IndexingOwner::Reindex);
return;
}
{
let vector_store = Arc::clone(&stores.vector_store);
let alias_bi = alias_bg.clone();
match tokio::task::spawn_blocking(move || {
let mut vstore = vector_store.blocking_write();
vstore.build_index()
})
.await
{
Ok(Ok(())) => {}
Ok(Err(e)) => {
tracing::warn!("Failed to build vector index for '{}': {}", alias_bi, e)
let vector_store = Arc::clone(&stores.vector_store);
let alias_bi = alias_bg.clone();
match tokio::task::spawn_blocking(move || {
let mut vstore = vector_store.blocking_write();
vstore.build_index()
})
.await
{
Ok(Ok(())) => {}
Ok(Err(e)) => {
tracing::warn!("Failed to build vector index for '{}': {}", alias_bi, e)
}
Err(e) => {
tracing::warn!("build_index task panicked for '{}': {:?}", alias_bi, e)
}
}
Err(e) => tracing::warn!("build_index task panicked for '{}': {:?}", alias_bi, e),
}
}

Expand Down
Loading