Skip to content

Commit 4b6db6b

Browse files
committed
feat: optimize vector table size
1 parent a88a952 commit 4b6db6b

File tree

4 files changed

+82
-33
lines changed

4 files changed

+82
-33
lines changed

src/services/code-index/config-manager.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class CodeIndexConfigManager {
1313
private codebaseIndexEnabled: boolean = true
1414
private embedderProvider: EmbedderProvider = "openai"
1515
private vectorStoreProvider: "local" | "qdrant" = "qdrant"
16-
private localVectorStoreDirectoryPlaceholder?: string
16+
private localVectorStoreDirectory?: string
1717
private modelId?: string
1818
private modelDimension?: number
1919
private openAiOptions?: ApiHandlerOptions
@@ -83,7 +83,7 @@ export class CodeIndexConfigManager {
8383
// Update instance variables with configuration
8484
this.codebaseIndexEnabled = codebaseIndexEnabled ?? true
8585
this.vectorStoreProvider = codebaseIndexVectorStoreProvider ?? "qdrant"
86-
this.localVectorStoreDirectoryPlaceholder = codebaseIndexLocalVectorStoreDirectory
86+
this.localVectorStoreDirectory = codebaseIndexLocalVectorStoreDirectory
8787
this.qdrantUrl = codebaseIndexQdrantUrl
8888
this.qdrantApiKey = qdrantApiKey ?? ""
8989
this.searchMinScore = codebaseIndexSearchMinScore
@@ -165,7 +165,7 @@ export class CodeIndexConfigManager {
165165
configured: this.isConfigured(),
166166
embedderProvider: this.embedderProvider,
167167
vectorStoreProvider: this.vectorStoreProvider,
168-
localVectorStoreDirectoryPlaceholder: this.localVectorStoreDirectoryPlaceholder,
168+
localVectorStoreDirectory: this.localVectorStoreDirectory,
169169
modelId: this.modelId,
170170
modelDimension: this.modelDimension,
171171
openAiKey: this.openAiOptions?.openAiNativeApiKey ?? "",
@@ -272,7 +272,7 @@ export class CodeIndexConfigManager {
272272
const prevQdrantUrl = prev?.qdrantUrl ?? ""
273273
const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
274274
const prevVectorStoreProvider = prev?.vectorStoreProvider ?? "qdrant"
275-
const prevLocalDbPath = prev?.localVectorStoreDirectoryPlaceholder ?? ""
275+
const prevLocalDbPath = prev?.localVectorStoreDirectory ?? ""
276276

277277
// 1. Transition from disabled/unconfigured to enabled/configured
278278
if ((!prevEnabled || !prevConfigured) && this.codebaseIndexEnabled && nowConfigured) {
@@ -306,10 +306,7 @@ export class CodeIndexConfigManager {
306306
}
307307

308308
// Local DB path change (only affects local vector store)
309-
if (
310-
this.vectorStoreProvider === "local" &&
311-
prevLocalDbPath !== (this.localVectorStoreDirectoryPlaceholder ?? "")
312-
) {
309+
if (this.vectorStoreProvider === "local" && prevLocalDbPath !== (this.localVectorStoreDirectory ?? "")) {
313310
return true
314311
}
315312

@@ -323,7 +320,7 @@ export class CodeIndexConfigManager {
323320
const currentMistralApiKey = this.mistralOptions?.apiKey ?? ""
324321
const currentQdrantUrl = this.qdrantUrl ?? ""
325322
const currentQdrantApiKey = this.qdrantApiKey ?? ""
326-
const currentLocalDbPath = this.localVectorStoreDirectoryPlaceholder ?? ""
323+
const currentLocalDbPath = this.localVectorStoreDirectory ?? ""
327324

328325
if (prevOpenAiKey !== currentOpenAiKey) {
329326
return true
@@ -404,7 +401,7 @@ export class CodeIndexConfigManager {
404401
isConfigured: this.isConfigured(),
405402
embedderProvider: this.embedderProvider,
406403
vectorStoreProvider: this.vectorStoreProvider ?? "qdrant",
407-
localVectorStoreDirectoryPlaceholder: this.localVectorStoreDirectoryPlaceholder,
404+
localVectorStoreDirectoryPlaceholder: this.localVectorStoreDirectory,
408405
modelId: this.modelId,
409406
modelDimension: this.modelDimension,
410407
openAiOptions: this.openAiOptions,
@@ -502,6 +499,6 @@ export class CodeIndexConfigManager {
502499
* Gets the current local database path for vector storage
503500
*/
504501
public get currentLocalDbPath(): string | undefined {
505-
return this.localVectorStoreDirectoryPlaceholder
502+
return this.localVectorStoreDirectory
506503
}
507504
}

src/services/code-index/interfaces/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export type PreviousConfigSnapshot = {
3030
configured: boolean
3131
embedderProvider: EmbedderProvider
3232
vectorStoreProvider?: "local" | "qdrant"
33-
localVectorStoreDirectoryPlaceholder?: string
33+
localVectorStoreDirectory?: string
3434
modelId?: string
3535
modelDimension?: number // Generic dimension property
3636
openAiKey?: string

src/services/code-index/vector-store/__tests__/local-vector-store.spec.ts

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// npm vitest run src/services/code-index/vector-store/__tests__/local-vector-store.spec.ts
1+
// npx vitest run services/code-index/vector-store/__tests__/local-vector-store.spec.ts
22

33
/**
44
* Comprehensive tests for LocalVectorStore.
@@ -9,38 +9,67 @@ import { describe, it, beforeEach, afterEach, expect, vi } from "vitest"
99
import { LocalVectorStore } from "../local-vector-store"
1010
import { LanceDBManager } from "../../../lancedb-manager"
1111
import { Payload } from "../../interfaces"
12+
import * as path from "path"
1213
const fs = require("fs")
13-
14-
const mockDb = {
15-
tableNames: vi.fn(),
16-
openTable: vi.fn(),
17-
createTable: vi.fn(),
18-
dropTable: vi.fn(),
19-
connect: vi.fn(),
14+
const mockTable = {
15+
delete: vi.fn().mockResolvedValue(undefined),
16+
add: vi.fn().mockResolvedValue(undefined),
17+
query: vi.fn().mockReturnThis(),
18+
where: vi.fn().mockReturnThis(),
19+
toArray: vi.fn().mockResolvedValue([]),
20+
vectorSearch: vi.fn().mockReturnThis(),
21+
limit: vi.fn().mockReturnThis(),
22+
refineFactor: vi.fn().mockReturnThis(),
23+
postfilter: vi.fn().mockReturnThis(),
24+
openTable: vi.fn().mockResolvedValue(undefined),
25+
search: vi.fn().mockReturnThis(),
26+
name: "vector",
27+
isOpen: true,
2028
close: vi.fn(),
29+
display: vi.fn(),
30+
schema: {},
31+
count: vi.fn(),
32+
get: vi.fn(),
33+
create: vi.fn(),
34+
drop: vi.fn(),
35+
insert: vi.fn(),
36+
update: vi.fn(),
37+
find: vi.fn(),
38+
remove: vi.fn(),
39+
createIndex: vi.fn(),
40+
dropIndex: vi.fn(),
41+
indexes: [],
42+
columns: [],
43+
primaryKey: "id",
44+
metadata: {},
45+
batch: vi.fn(),
46+
distanceRange: vi.fn().mockReturnThis(),
2147
}
22-
const mockTable = {
23-
query: vi.fn(),
24-
add: vi.fn(),
25-
delete: vi.fn(),
26-
search: vi.fn(),
48+
const mockDb = {
49+
openTable: vi.fn().mockResolvedValue(mockTable),
50+
createTable: vi.fn().mockResolvedValue(mockTable),
51+
dropTable: vi.fn().mockResolvedValue(undefined),
52+
tableNames: vi.fn().mockResolvedValue(["vector", "metadata"]),
53+
close: vi.fn().mockResolvedValue(undefined),
54+
isOpen: true,
55+
display: vi.fn(),
56+
createEmptyTable: vi.fn(),
57+
dropAllTables: vi.fn(),
2758
}
59+
2860
const mockLanceDBModule = {
29-
connect: vi.fn(() => Promise.resolve(mockDb)),
61+
connect: vi.fn().mockResolvedValue(mockDb),
3062
}
3163
const mockLanceDBManager = {
3264
ensureLanceDBAvailable: vi.fn(),
3365
getNodeModulesPath: vi.fn(() => "mock_node_modules"),
3466
}
3567

3668
vi.mock("@lancedb/lancedb", () => mockLanceDBModule)
37-
vi.mock("../../lancedb-manager", () => ({
38-
LanceDBManager: vi.fn(() => mockLanceDBManager),
39-
}))
4069

41-
const workspacePath = "/mock/workspace"
42-
const vectorSize = 3
43-
const dbDirectory = "/mock/db"
70+
const workspacePath = path.join("mock", "workspace")
71+
const vectorSize = 768
72+
const dbDirectory = path.join("mock", "db")
4473
let store: LocalVectorStore
4574

4675
describe("LocalVectorStore", () => {

src/services/code-index/vector-store/local-vector-store.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,11 @@ export class LocalVectorStore implements IVectorStore {
213213
await this._dropTableIfExists(db, this.metadataTableName)
214214
await this._createVectorTable(db)
215215
await this._createMetadataTable(db)
216+
this.optimizeTable()
217+
216218
return true
217219
}
218-
220+
this.optimizeTable()
219221
return false
220222
} catch (error) {
221223
console.error(`[LocalVectorStore] Failed to initialize:`, error)
@@ -395,6 +397,9 @@ export class LocalVectorStore implements IVectorStore {
395397
} catch (metadataError) {
396398
console.warn("Failed to clear metadata table:", metadataError)
397399
}
400+
401+
// Run optimization to clean up disk space after clearing
402+
await this.optimizeTable()
398403
} catch (error) {
399404
console.error("Failed to clear collection:", error)
400405
throw error
@@ -420,4 +425,22 @@ export class LocalVectorStore implements IVectorStore {
420425
this.db = null
421426
}
422427
}
428+
429+
/**
430+
* Optimizes the table to reduce disk space usage and improve performance.
431+
* This method performs compaction, pruning of old versions, and index optimization.
432+
* Should be called periodically to prevent unbounded disk space growth.
433+
*/
434+
async optimizeTable(): Promise<void> {
435+
try {
436+
const table = await this.getTable()
437+
438+
await table.optimize({
439+
cleanupOlderThan: new Date(),
440+
deleteUnverified: false,
441+
})
442+
} catch (error) {
443+
console.error("[LocalVectorStore] Failed to optimize table:", error)
444+
}
445+
}
423446
}

0 commit comments

Comments
 (0)