Repository navigation
An embedding wider than 2000 dims gets a halfvec index that picks the candidates, and they are still scored and ordered at full precision - #43
Merged
Conversation
… candidates, and they are still scored and ordered at full precision Signed-off-by: Bogdan Szabo <bogdan@re-cinq.com>
gedaiu
added a commit
to re-cinq/lore
that referenced
this pull request
Oct 27, 2026
…s database (#3727) Utopia was slow because its database sat at its 4-CPU limit: every vector search scanned the whole table. Utopia only indexed embeddings up to 2000 dims, and ours are 3072. Name lookups took up to 35 s. This pins `0.1.0-re.25` (digest `sha256:abab8151…`), which adds re-cinq/utopia#43. Embeddings wider than 2000 dims get a halfvec HNSW index that only picks the candidates, which are still scored and ordered at full precision. There is no new migration. After the rollout, the next write of each kind queues its index build (CONCURRENTLY, one at a time). The chunks index (617k rows) can take a while, and searches keep the old path until it is valid. It also raises the Utopia database from 4Gi to 8Gi, with `shared_buffers` 1GB → 2GB and `effective_cache_size` 3GB → 6GB, so the indexes stay in memory. Changing `shared_buffers` restarts the single CNPG instance, so expect a short Utopia outage at apply. Related: re-cinq/utopia#43 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every vector search on a knowledge base with 3072-dim embeddings (text-embedding-3-large) scans the whole table. pgvector's HNSW holds
vectorup to 2000 dims, sovector_indexnever built an index for these, and the comment there said halfvec would wait until someone needs it. We need it: in our deploymentchunks,entities.profile_embeddingandname_vectorsare all 3072 dims with no HNSW index. Name lookups run up to 35 s, readingname_vectors(3.5 GB) from disk, and the database sits at its CPU limit.Above 2000 dims the build job now makes the same partial expression index on
col::halfvec(N)withhalfvec_cosine_ops(halfvec goes to 4000). The index only picks the candidates. Each search selects its distance withvector_index::distance, which stayscol::vector(N), whileORDER BYuses the newvector_index::index_order, which matches the index expression. The outerRESORTthat already exists then orders the candidates by full-precision distance. Half precision can therefore only affect which rows make the candidate list, never a score or the order returned. At 2000 dims or fewerindex_orderequalsdistance, so the SQL is unchanged there.The three nearest-neighbour searches (
documents::vector_search,name_vectors::nearest, entity-profile neighbours inresolution.rs) useindex_order.name_vectors::nearestalso now runs in a transaction withrelaxed_order. It never set it, which was harmless without an index, but with one a small base sharing the table would come back short (rule 3).a_wide_embedding_is_ranked_at_full_precisionbuilds a 2001-dim index, checks it is a halfvec one, forces the planner onto it and confirms throughidx_scanthat the search used it. It places two chunks whose distances tie in half precision and differ in full precision, with the farther one holding the smaller id. Scoring the candidates with the half-precision expression puts that one first and fails the test. A small base beside the big one still fills its limit.On a deployment that already holds 3072-dim rows, the next write of each kind queues its build. The builds are
CONCURRENTLYand serial, so searches keep their current (slow) path until each index is valid.🤖 Generated with Claude Code