mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: prevent unnecessary re-indexing on VSCode window reload
- Added detailed logging to track index persistence behavior - Enhanced comments to clarify existing incremental scan logic - Added tests to verify incremental scan on window reload - The orchestrator already checks for existing indexed data via hasIndexedData() and performs incremental scans instead of full rebuilds when data exists Fixes #9186
This commit is contained in:
parent
69d4efc335
commit
3bb529eede
4 changed files with 137 additions and 6 deletions
|
|
@ -157,4 +157,102 @@ describe("CodeIndexOrchestrator - error path cleanup gating", () => {
|
|||
const lastCall = stateManager.setSystemState.mock.calls[stateManager.setSystemState.mock.calls.length - 1]
|
||||
expect(lastCall[0]).toBe("Error")
|
||||
})
|
||||
|
||||
it("should perform incremental scan when indexed data already exists (window reload scenario)", async () => {
|
||||
// Arrange: simulate window reload scenario where index already exists
|
||||
vectorStore.initialize.mockResolvedValue(false) // existing collection (not newly created)
|
||||
vectorStore.hasIndexedData.mockResolvedValue(true) // index data exists
|
||||
vectorStore.markIndexingIncomplete.mockResolvedValue(undefined)
|
||||
vectorStore.markIndexingComplete.mockResolvedValue(undefined)
|
||||
|
||||
// Mock scanner to return successful result for incremental scan
|
||||
scanner.scanDirectory.mockResolvedValue({
|
||||
stats: {
|
||||
filesProcessed: 5,
|
||||
blocksFound: 10,
|
||||
blocksIndexed: 10,
|
||||
},
|
||||
})
|
||||
|
||||
const orchestrator = new CodeIndexOrchestrator(
|
||||
configManager,
|
||||
stateManager,
|
||||
workspacePath,
|
||||
cacheManager,
|
||||
vectorStore,
|
||||
scanner,
|
||||
fileWatcher,
|
||||
)
|
||||
|
||||
// Act
|
||||
await orchestrator.startIndexing()
|
||||
|
||||
// Assert
|
||||
// Should check for existing data
|
||||
expect(vectorStore.hasIndexedData).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Should perform incremental scan (scanner called once)
|
||||
expect(scanner.scanDirectory).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Should NOT clear collection or cache (preserving existing index)
|
||||
expect(vectorStore.clearCollection).not.toHaveBeenCalled()
|
||||
expect(cacheManager.clearCacheFile).not.toHaveBeenCalled()
|
||||
|
||||
// Should mark indexing as incomplete at start and complete at end
|
||||
expect(vectorStore.markIndexingIncomplete).toHaveBeenCalledTimes(1)
|
||||
expect(vectorStore.markIndexingComplete).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Should end in Indexed state
|
||||
expect(stateManager.state).toBe("Indexed")
|
||||
})
|
||||
|
||||
it("should perform full scan when collection is newly created", async () => {
|
||||
// Arrange: new collection created
|
||||
vectorStore.initialize.mockResolvedValue(true) // new collection created
|
||||
vectorStore.hasIndexedData.mockResolvedValue(false) // no data yet
|
||||
vectorStore.markIndexingIncomplete.mockResolvedValue(undefined)
|
||||
vectorStore.markIndexingComplete.mockResolvedValue(undefined)
|
||||
|
||||
// Mock scanner to return successful result for full scan
|
||||
scanner.scanDirectory.mockResolvedValue({
|
||||
stats: {
|
||||
filesProcessed: 100,
|
||||
blocksFound: 500,
|
||||
blocksIndexed: 500,
|
||||
},
|
||||
})
|
||||
|
||||
// Clear cache when new collection is created
|
||||
cacheManager.clearCacheFile.mockResolvedValue(undefined)
|
||||
|
||||
const orchestrator = new CodeIndexOrchestrator(
|
||||
configManager,
|
||||
stateManager,
|
||||
workspacePath,
|
||||
cacheManager,
|
||||
vectorStore,
|
||||
scanner,
|
||||
fileWatcher,
|
||||
)
|
||||
|
||||
// Act
|
||||
await orchestrator.startIndexing()
|
||||
|
||||
// Assert
|
||||
// Should check for existing data
|
||||
expect(vectorStore.hasIndexedData).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Should clear cache since collection was newly created
|
||||
expect(cacheManager.clearCacheFile).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Should perform full scan
|
||||
expect(scanner.scanDirectory).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Should mark indexing as incomplete at start and complete at end
|
||||
expect(vectorStore.markIndexingIncomplete).toHaveBeenCalledTimes(1)
|
||||
expect(vectorStore.markIndexingComplete).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Should end in Indexed state
|
||||
expect(stateManager.state).toBe("Indexed")
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -154,11 +154,30 @@ export class CodeIndexManager {
|
|||
// 5. Handle Indexing Start/Restart
|
||||
// The enhanced vectorStore.initialize() in startIndexing() now handles dimension changes automatically
|
||||
// by detecting incompatible collections and recreating them, so we rely on that for dimension changes
|
||||
const shouldStartOrRestartIndexing =
|
||||
requiresRestart ||
|
||||
(needsServiceRecreation && (!this._orchestrator || this._orchestrator.state !== "Indexing"))
|
||||
|
||||
// Determine if we should start or restart indexing
|
||||
// We need to be careful here to avoid unnecessary re-indexing on window reload
|
||||
let shouldStartOrRestartIndexing = false
|
||||
|
||||
if (requiresRestart) {
|
||||
// Configuration changed in a way that requires restart (e.g., model change)
|
||||
shouldStartOrRestartIndexing = true
|
||||
} else if (needsServiceRecreation && this._orchestrator) {
|
||||
// Services were recreated but orchestrator exists
|
||||
// Only restart if not currently indexing
|
||||
shouldStartOrRestartIndexing =
|
||||
this._orchestrator.state !== "Indexing" && this._orchestrator.state !== "Indexed"
|
||||
} else if (needsServiceRecreation && !this._orchestrator) {
|
||||
// Services were recreated and orchestrator doesn't exist (e.g., after window reload)
|
||||
// This is the common case that was causing unnecessary re-indexing
|
||||
// The orchestrator's startIndexing() will check for existing data and do incremental scan if needed
|
||||
shouldStartOrRestartIndexing = true
|
||||
}
|
||||
|
||||
if (shouldStartOrRestartIndexing) {
|
||||
// Note: startIndexing() internally checks for existing indexed data via hasIndexedData()
|
||||
// and will perform an incremental scan instead of a full rebuild when data exists
|
||||
// This prevents unnecessary re-indexing when the window is reloaded
|
||||
this._orchestrator?.startIndexing() // This method is async, but we don't await it here
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,6 +141,10 @@ export class CodeIndexOrchestrator {
|
|||
// If it does, we can skip the full scan and just start the watcher
|
||||
const hasExistingData = await this.vectorStore.hasIndexedData()
|
||||
|
||||
console.log(
|
||||
`[CodeIndexOrchestrator] Checking existing data: hasExistingData=${hasExistingData}, collectionCreated=${collectionCreated}`,
|
||||
)
|
||||
|
||||
if (hasExistingData && !collectionCreated) {
|
||||
// Collection exists with data - run incremental scan to catch any new/changed files
|
||||
// This handles files added while workspace was closed or Qdrant was inactive
|
||||
|
|
@ -201,6 +205,9 @@ export class CodeIndexOrchestrator {
|
|||
this.stateManager.setSystemState("Indexed", t("embeddings:orchestrator.fileWatcherStarted"))
|
||||
} else {
|
||||
// No existing data or collection was just created - do a full scan
|
||||
console.log(
|
||||
`[CodeIndexOrchestrator] No existing data or collection was just created. Starting full workspace scan...`,
|
||||
)
|
||||
this.stateManager.setSystemState("Indexing", "Services ready. Starting workspace scan...")
|
||||
|
||||
// Mark as incomplete at the start of full scan
|
||||
|
|
|
|||
|
|
@ -588,10 +588,13 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
try {
|
||||
const collectionInfo = await this.getCollectionInfo()
|
||||
if (!collectionInfo) {
|
||||
console.log("[QdrantVectorStore] hasIndexedData: No collection info found")
|
||||
return false
|
||||
}
|
||||
// Check if the collection has any points indexed
|
||||
const pointsCount = collectionInfo.points_count ?? 0
|
||||
console.log(`[QdrantVectorStore] hasIndexedData: Collection has ${pointsCount} points`)
|
||||
|
||||
if (pointsCount === 0) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -605,17 +608,21 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
|
||||
// If marker exists, use it to determine completion status
|
||||
if (metadataPoints.length > 0) {
|
||||
return metadataPoints[0].payload?.indexing_complete === true
|
||||
const isComplete = metadataPoints[0].payload?.indexing_complete === true
|
||||
console.log(
|
||||
`[QdrantVectorStore] hasIndexedData: Found metadata marker, indexing_complete=${isComplete}`,
|
||||
)
|
||||
return isComplete
|
||||
}
|
||||
|
||||
// Backward compatibility: No marker exists (old index or pre-marker version)
|
||||
// Fall back to old logic - assume complete if collection has points
|
||||
console.log(
|
||||
"[QdrantVectorStore] No indexing metadata marker found. Using backward compatibility mode (checking points_count > 0).",
|
||||
`[QdrantVectorStore] hasIndexedData: No indexing metadata marker found. Using backward compatibility mode (returning ${pointsCount > 0} based on points_count)`,
|
||||
)
|
||||
return pointsCount > 0
|
||||
} catch (error) {
|
||||
console.warn("[QdrantVectorStore] Failed to check if collection has data:", error)
|
||||
console.warn("[QdrantVectorStore] hasIndexedData: Failed to check if collection has data:", error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue