Skip to content

Commit 7efe521

Browse files
committed
feat(QdrantVectorStore): enhance error handling and logging in collection management
1 parent 2053d27 commit 7efe521

File tree

2 files changed

+68
-60
lines changed

2 files changed

+68
-60
lines changed

src/services/code-index/vector-store/__tests__/qdrant-client.test.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,19 +201,23 @@ describe("QdrantVectorStore", () => {
201201
expect(mockQdrantClientInstance.createPayloadIndex).toHaveBeenCalledTimes(5)
202202
;(console.warn as jest.Mock).mockRestore() // Restore console.warn
203203
})
204-
it("should re-throw error from getCollection if it is not a 404 error", async () => {
204+
it("should log warning for non-404 errors but still create collection", async () => {
205205
const genericError = new Error("Generic Qdrant Error")
206206
mockQdrantClientInstance.getCollection.mockRejectedValue(genericError)
207-
jest.spyOn(console, "error").mockImplementation(() => {}) // Suppress console.error
207+
jest.spyOn(console, "warn").mockImplementation(() => {}) // Suppress console.warn
208208

209-
await expect(vectorStore.initialize()).rejects.toThrow(genericError)
209+
const result = await vectorStore.initialize()
210210

211+
expect(result).toBe(true) // Collection was created
211212
expect(mockQdrantClientInstance.getCollection).toHaveBeenCalledTimes(1)
212-
expect(mockQdrantClientInstance.createCollection).not.toHaveBeenCalled()
213+
expect(mockQdrantClientInstance.createCollection).toHaveBeenCalledTimes(1)
213214
expect(mockQdrantClientInstance.deleteCollection).not.toHaveBeenCalled()
214-
expect(mockQdrantClientInstance.createPayloadIndex).not.toHaveBeenCalled()
215-
expect(console.error).toHaveBeenCalledTimes(2) // Once in the try/catch for getCollection, once in the outer try/catch
216-
;(console.error as jest.Mock).mockRestore()
215+
expect(mockQdrantClientInstance.createPayloadIndex).toHaveBeenCalledTimes(5)
216+
expect(console.warn).toHaveBeenCalledWith(
217+
expect.stringContaining(`Warning during getCollectionInfo for "${expectedCollectionName}"`),
218+
genericError.message,
219+
)
220+
;(console.warn as jest.Mock).mockRestore()
217221
})
218222
it("should re-throw error from createCollection when no collection initially exists", async () => {
219223
mockQdrantClientInstance.getCollection.mockRejectedValue({
@@ -260,7 +264,7 @@ describe("QdrantVectorStore", () => {
260264
for (let i = 0; i <= 4; i++) {
261265
expect(console.warn).toHaveBeenCalledWith(
262266
expect.stringContaining(`Could not create payload index for pathSegments.${i}`),
263-
indexError,
267+
indexError.message,
264268
)
265269
}
266270

@@ -322,17 +326,20 @@ describe("QdrantVectorStore", () => {
322326
expect(mockQdrantClientInstance.getCollection).toHaveBeenCalledWith(expectedCollectionName)
323327
})
324328

325-
it("should return false and log error for non-404 errors", async () => {
329+
it("should return false and log warning for non-404 errors", async () => {
326330
const genericError = new Error("Network error")
327331
mockQdrantClientInstance.getCollection.mockRejectedValue(genericError)
328-
jest.spyOn(console, "error").mockImplementation(() => {})
332+
jest.spyOn(console, "warn").mockImplementation(() => {})
329333

330334
const result = await vectorStore.collectionExists()
331335

332336
expect(result).toBe(false)
333337
expect(mockQdrantClientInstance.getCollection).toHaveBeenCalledTimes(1)
334-
expect(console.error).toHaveBeenCalledWith("Error checking collection existence:", genericError)
335-
;(console.error as jest.Mock).mockRestore()
338+
expect(console.warn).toHaveBeenCalledWith(
339+
expect.stringContaining(`Warning during getCollectionInfo for "${expectedCollectionName}"`),
340+
genericError.message,
341+
)
342+
;(console.warn as jest.Mock).mockRestore()
336343
})
337344
describe("collectionExists", () => {
338345
// Test scenarios for collectionExists will go here

src/services/code-index/vector-store/qdrant-client.ts

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { QdrantClient } from "@qdrant/js-client-rest"
1+
import { QdrantClient, Schemas } from "@qdrant/js-client-rest"
22
import { createHash } from "crypto"
33
import * as path from "path"
44
import { getWorkspacePath } from "../../../utils/path"
@@ -37,30 +37,51 @@ export class QdrantVectorStore implements IVectorStore {
3737
this.collectionName = `ws-${hash.substring(0, 16)}`
3838
}
3939

40+
private async getCollectionInfo(): Promise<Schemas["CollectionInfo"] | null> {
41+
try {
42+
const collectionInfo = await this.client.getCollection(this.collectionName)
43+
return collectionInfo // Success
44+
} catch (error: any) {
45+
// Log a warning if the error is not a typical 404 "Not Found"
46+
if (error?.response?.status !== 404) {
47+
console.warn(
48+
`[QdrantVectorStore] Warning during getCollectionInfo for "${this.collectionName}". Collection may not exist or another error occurred:`,
49+
error?.message || error, // Log error message or the error itself
50+
)
51+
}
52+
return null // Treat any error as "collection info not retrievable"
53+
}
54+
}
55+
4056
/**
4157
* Initializes the vector store
4258
* @returns Promise resolving to boolean indicating if a new collection was created
4359
*/
4460
async initialize(): Promise<boolean> {
61+
let created = false
4562
try {
46-
let created = false
63+
const collectionInfo = await this.getCollectionInfo()
4764

48-
try {
49-
// Directly attempt to fetch the specific collection
50-
const collectionInfo = await this.client.getCollection(this.collectionName)
51-
52-
// Collection exists - check if vector size matches
65+
if (collectionInfo === null) {
66+
// Collection info not retrieved (assume not found or inaccessible), create it
67+
await this.client.createCollection(this.collectionName, {
68+
vectors: {
69+
size: this.vectorSize,
70+
distance: this.DISTANCE_METRIC,
71+
},
72+
})
73+
created = true
74+
} else {
75+
// Collection exists, check vector size
5376
const existingVectorSize = collectionInfo.config?.params?.vectors?.size
54-
5577
if (existingVectorSize === this.vectorSize) {
56-
// Collection exists and has correct vector size
57-
created = false
78+
created = false // Exists and correct
5879
} else {
59-
// Collection exists but has wrong vector size - recreate it
80+
// Exists but wrong vector size, recreate
6081
console.warn(
6182
`[QdrantVectorStore] Collection ${this.collectionName} exists with vector size ${existingVectorSize}, but expected ${this.vectorSize}. Recreating collection.`,
6283
)
63-
await this.client.deleteCollection(this.collectionName)
84+
await this.client.deleteCollection(this.collectionName) // Known to exist
6485
await this.client.createCollection(this.collectionName, {
6586
vectors: {
6687
size: this.vectorSize,
@@ -69,42 +90,31 @@ export class QdrantVectorStore implements IVectorStore {
6990
})
7091
created = true
7192
}
72-
} catch (error: any) {
73-
// Check if this is a "Not Found" error (collection doesn't exist)
74-
if (error?.response?.status === 404) {
75-
// Collection doesn't exist - create it
76-
await this.client.createCollection(this.collectionName, {
77-
vectors: {
78-
size: this.vectorSize,
79-
distance: this.DISTANCE_METRIC,
80-
},
81-
})
82-
created = true
83-
} else {
84-
// Other error - log and re-throw
85-
console.error(`[QdrantVectorStore] Error checking collection ${this.collectionName}:`, error)
86-
throw error
87-
}
8893
}
8994

90-
// Create payload indexes for pathSegments up to depth 5
95+
// Create payload indexes
9196
for (let i = 0; i <= 4; i++) {
9297
try {
9398
await this.client.createPayloadIndex(this.collectionName, {
9499
field_name: `pathSegments.${i}`,
95100
field_schema: "keyword",
96101
})
97-
} catch (indexError) {
98-
console.warn(
99-
`[QdrantVectorStore] Could not create payload index for pathSegments.${i} on ${this.collectionName}. It might already exist or there was an issue.`,
100-
indexError,
101-
)
102+
} catch (indexError: any) {
103+
const errorMessage = (indexError?.message || "").toLowerCase()
104+
if (!errorMessage.includes("already exists")) {
105+
console.warn(
106+
`[QdrantVectorStore] Could not create payload index for pathSegments.${i} on ${this.collectionName}. Details:`,
107+
indexError?.message || indexError,
108+
)
109+
}
102110
}
103111
}
104-
105112
return created
106-
} catch (error) {
107-
console.error("Failed to initialize Qdrant collection:", error)
113+
} catch (error: any) {
114+
console.error(
115+
`[QdrantVectorStore] Failed to initialize Qdrant collection "${this.collectionName}":`,
116+
error?.message || error,
117+
)
108118
throw error
109119
}
110120
}
@@ -295,16 +305,7 @@ export class QdrantVectorStore implements IVectorStore {
295305
* @returns Promise resolving to boolean indicating if the collection exists
296306
*/
297307
async collectionExists(): Promise<boolean> {
298-
try {
299-
// Prefer direct API call if supported
300-
await this.client.getCollection(this.collectionName)
301-
return true
302-
} catch (error: any) {
303-
if (error?.response?.status === 404) {
304-
return false
305-
}
306-
console.error("Error checking collection existence:", error)
307-
return false
308-
}
308+
const collectionInfo = await this.getCollectionInfo()
309+
return collectionInfo !== null
309310
}
310311
}

0 commit comments

Comments
 (0)