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
28 changes: 22 additions & 6 deletions src/db/postgres/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,14 +465,30 @@ export class PostgresKnowledgeDB implements IKnowledgeStore {
this.pgvectorReady = false;
return;
}
// Check whether the HNSW index already exists. CREATE INDEX IF NOT EXISTS
// still requires table ownership, so it can fail with "must be owner of
// table" on managed Postgres where the schema was bootstrapped by a
// different role (e.g. Cloud SQL with a separate admin user). When the
// index is already there from a prior run, treat that as success and
// keep ANN enabled instead of degrading to Path B.
const hnswExists = await this.sql`
SELECT 1 FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'i'
AND n.nspname = current_schema()
AND c.relname = 'idx_entry_embedding_halfvec_hnsw'
`;

try {
await this.sql`DROP INDEX IF EXISTS idx_entry_embedding_vec_hnsw`;
await this.sql.unsafe(`
CREATE INDEX IF NOT EXISTS idx_entry_embedding_halfvec_hnsw
ON knowledge_entry
USING hnsw ((embedding_vec::halfvec(${dims})) halfvec_cosine_ops)
WITH (m = 16, ef_construction = 64)
`);
if (hnswExists.length === 0) {
await this.sql.unsafe(`
CREATE INDEX IF NOT EXISTS idx_entry_embedding_halfvec_hnsw
ON knowledge_entry
USING hnsw ((embedding_vec::halfvec(${dims})) halfvec_cosine_ops)
WITH (m = 16, ef_construction = 64)
`);
}
} catch (err) {
logger.warn(
`[pg-db] ANN index creation failed — ANN search disabled. Will retry on next startup. Error: ${err instanceof Error ? err.message : String(err)}`,
Expand Down
36 changes: 25 additions & 11 deletions src/db/postgres/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,17 +514,31 @@ export const PG_MIGRATIONS: Array<{
// Replace legacy vector HNSW index with halfvec ANN index when supported.
await sql`DROP INDEX IF EXISTS idx_entry_embedding_vec_hnsw`;
if (dims <= 4000) {
try {
await sql.unsafe(`
CREATE INDEX IF NOT EXISTS idx_entry_embedding_halfvec_hnsw
ON knowledge_entry
USING hnsw ((embedding_vec::halfvec(${dims})) halfvec_cosine_ops)
WITH (m = 16, ef_construction = 64)
`);
} catch (err) {
logger.warn(
`[pg-db] Migration v17: ANN index creation skipped. ANN search disabled for this store. Error: ${err instanceof Error ? err.message : String(err)}`,
);
// Skip CREATE INDEX if the halfvec HNSW index is already present.
// CREATE INDEX IF NOT EXISTS still requires table ownership and
// fails with "must be owner of table" on managed Postgres where
// the schema was bootstrapped by a separate admin user. When the
// index exists from a prior run, treat that as success.
const hnswExists = await sql`
SELECT 1 FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'i'
AND n.nspname = current_schema()
AND c.relname = 'idx_entry_embedding_halfvec_hnsw'
`;
if (hnswExists.length === 0) {
try {
await sql.unsafe(`
CREATE INDEX IF NOT EXISTS idx_entry_embedding_halfvec_hnsw
ON knowledge_entry
USING hnsw ((embedding_vec::halfvec(${dims})) halfvec_cosine_ops)
WITH (m = 16, ef_construction = 64)
`);
} catch (err) {
logger.warn(
`[pg-db] Migration v17: ANN index creation skipped. ANN search disabled for this store. Error: ${err instanceof Error ? err.message : String(err)}`,
);
}
}
} else {
logger.warn(
Expand Down
Loading