Repository navigation
perf(index): add an ANN index so vector search stops scanning every embedding #173
Description
Summary
SPEC.md:537 lists embeddings_* (embedding) — pgvector HNSW as part of the
index contract. No ANN index DDL exists anywhere in the repository, so every
vector query is an exact sequential scan of the whole embedding table.
This was raised while auditing the data/index plane and appears to be untracked,
checked two ways: search/issues?q=repo:bytefolk/mem hnsw OR ivfflat OR "approximate nearest" returns total_count: 0 with
incomplete_results: false, and a local regex for the same three terms over all
172 open+closed issue and PR titles and bodies returns zero hits.
Source reproduction
On main @ 7a194f1eba4167d54bd46cf84cdbe86e00532319:
server/internal/db/migrations/0001_init.sql:109-110— the only
embeddings_textindex isidx_embeddings_text_file (file_id), with the
commentHNSW index will be added by worker once we settle on a model dimension.- The stated blocker no longer holds. Dimensions are pinned in code:
server/internal/search/search.go:62-64and
server/internal/indexer/indexer.go:112-116declare
textEmbeddingSchemaDim = 768,visualEmbeddingSchemaDim = 512,
faceEmbeddingSchemaDim = 512, andvector(768)/vector(512)are fixed
column types. server/internal/db/migrations/0019_versioned_index_generations.sql:267
defers again for the versioned table (Future ANN indexes must be route/dimension-specific expression or partition …).- Full scans:
server/internal/search/search.go:666,674,705,711,
server/internal/relator/relator.go:205,211,272,278. server/internal/face/face.go:14acknowledges the ceiling in a comment:
thousands of faces. For larger corpora swap in pgvector HNSW + offline …
No new dependency is required: docker-compose.yml:9 and
docker-compose.test.yml:7 already run pgvector/pgvector:pg16 (test compose
pins it by digest), and HNSW has shipped in pgvector since 0.5.0.
Scope boundary
In scope: one migration adding ANN indexes plus the verification that queries
actually use them. Out of scope: changing distance function, model, chunking,
retrieval ranking, or the generation lifecycle (that is a separate report).
Deliberately not decided here: whether index_generation_vectors gets an index
in the same change. It is empty today, so building an index on it proves nothing
about the planner; it may be better to add it when the generation executor
exists.
Acceptance
-
embeddings_text,embeddings_visual, andembeddings_facehave ANN
indexes with the operators their queries use (cosine,<=>). - A regression check proves the planner takes them:
EXPLAINfor the
text and visual query shapes shows an index scan rather than a seq scan on
a populated corpus, recorded in the PR validation ledger. - Building the index on a populated table does not break ingest: the
migration is verified against a corpus containing all three dimensions and
the failure mode of an inconsistent-dimension table is stated. -
SPEC.md:537and the two deferral comments no longer contradict the DDL. - Recall measured before and after is reported, or the issue link of the
harness that will measure it is recorded if recall cannot be measured yet.
Evidence level
E2 — source-level. The absence of ANN DDL and the exact-scan queries were
verified by reading every .sql, .go, and .py file in the tree; the planner
behavior and the recall effect were not measured, and no database of
meaningful size was run against.
Proposed triage
Applied on filing, per docs/maintainers/triage.md and the precedent in #135:
type:feature, area:server, evidence:e2-source, status:needs-triage.
Left to a maintainer: priority, and any change to the set below.
This is the limiting factor only once a workspace holds tens of thousands of
vectors, and the current corpus sizes in this repository's own fixtures are far
below that.
Raised from the data/index audit on 2026-09-08.
Activity
docs(index): add HNSW validation script and ledger (bytefolk#173)
perf(migrations): add HNSW ANN indexes for embedding tables
chore(migrations): remove stale HNSW deferral comment from 0001_init
chore(migrations): clarify ANN deferral scope in 0019
fix(migrations): run HNSW index creation outside transaction
xiaocui-big commentedon Sep 17, 2026
PR submitted: #213
What the PR does
New migration
0024_add_hnsw_ann_indexes.sql— adds HNSW indexes on all three embedding tables:idx_embeddings_text_embedding_hnswonembeddings_text(768-d,vector_cosine_ops)idx_embeddings_visual_embedding_hnswonembeddings_visual(512-d,vector_cosine_ops)idx_embeddings_face_embedding_hnswonembeddings_face(512-d,vector_cosine_ops)CREATE INDEX CONCURRENTLY(with gooseNO TRANSACTION) to avoid blocking writes on populated tables0001_init.sql— removed the stale deferral comment about HNSW waiting on model dimensions0019_versioned_index_generations.sql— scoped the ANN guidance toindex_generation_vectorsexplicitly; noted that legacy tables now have HNSW indexes via migration 0024Not in scope (per the issue's boundary)
index_generation_vectors— deliberately not indexed (empty table, no generation executor yet)benchmarks/recallharness exists but needs a populated corpus)Still needs
chore(verify): bump EXPECTED_MIGRATION_HEAD to 24
chore(changelog): add HNSW ANN index entry under [Unreleased]
waterbro-8 commentedon Sep 17, 2026
Open implementation PR: #213 (
perf(migrations): add HNSW ANN indexes for embedding tables).mainstill has the deferral comment on0001_init.sqland no HNSW DDL.I am not opening a duplicate. Review/CI on #213 is the remaining path. No merge or close.
waterbro-8 commentedon Sep 18, 2026
Implementation of the remaining acceptance (0025 HNSW + text continuation/fallback + planner/semantics tests): #219
#213 was the duplicate 0024 attempt and stays closed. #197 is the earlier HOLD on the DISTINCT ON planner; 219 rewrites the shipping text path instead of leaving that HOLD in place.
fix(test): restore time import in HNSW migration regression
fix(index): isolate text ANN in a CTE so HNSW can be planned
fix(ci): do not feed pgx URIs to psql in the HNSW gate
fix(ci): restore executable bit on scripts/verify.sh
waterbro-8 commentedon Sep 18, 2026
#197 rebase 进展(#173 保持 open)
平台组 Review:#197 (comment)
已按建议处理:
95af9fe),冲突解除。fix(index): publish honest HNSW verification (successor to #180) #197runTextANN的DISTINCT ON (f.id) ORDER BY f.id, embedding <=> $1用不上 pgvector HNSW(缺 nearest-per-group 原语)。不为变绿放宽。还差什么才能合 #197
95af9fe上 Approve。waterbro-8 是 last pusher,不会自批自合。planner 后续(本 issue 继续挂)
平台组建议的两阶段查询 + 动态 OVERSAMPLE + 有界 fallback + eligible-set 先过滤,需要真实语料 recall,独立交付。#219 目前叠了 0025 DDL 和 continuation,等 #197 合入后再 rebase,并删掉重复迁移。
waterbro-8 commentedon Sep 18, 2026
#197 rebase 后 CI 已绿:head
95af9fe,21/21 pass(Go + PostgreSQL integration 含在内)。仍不能合:需要 CODEOWNER Approve(已 request @Bindy-lbb)。合入后本 issue 继续 open,只关闭“索引基础”,planner 集成仍挂在这里。
PR:#197 (comment)
perf(index): add cosine HNSW indexes and text continuation (#173) (#219)