Skip to content

Vector index bloat: no compress_neighbors/max_neighbors tuning - 542 MB shard for ~656 memories (2048-dim embeddings) #277

Description

@shdowzh888

Environment

  • opencode-mem: 2.22.2 (issue likely affects all versions up to 2.25.0)
  • OS: Windows 11
  • Embedding: remote OpenAI-compatible API (doubao-embedding-vision), 2048 dims

Symptom

With only ~656 memories in a project shard, the shard DB grew to 542 MB. dbstat shows the two DiskANN shadow tables account for 98% of the file:

object size
memories_vec_idx_shadow 267.4 MB
memories_tags_vec_idx_shadow 266.5 MB
memories (all actual text + metadata) 11.0 MB

That is ~850 KB of index overhead per memory, while the raw vectors are only 2048 × 4 B ≈ 8 KB each.

Root cause

initShardDb() creates both vector indexes with 'metric=cosine' only:

CREATE INDEX ... ON memories (libsql_vector_idx(vector, 'metric=cosine'))

libSQL defaults are aggressive at high dimensionality:

  • max_neighbors = 3·√D~137 neighbors per node at D=2048
  • compress_neighbors unset → neighbor vectors stored as uncompressed F32

This matches the documented space formula N · (Storage(T1) + M · Storage(T2)) in the Turso docs.

Fix verified locally

Dropping and re-creating the same-named indexes with tuned parameters, then VACUUM, shrinks the whole shard from 542.5 MB → 73.3 MB (~7.4×) with search quality intact:

DROP INDEX IF EXISTS memories_vec_idx;
DROP INDEX IF EXISTS memories_tags_vec_idx;
CREATE INDEX memories_vec_idx ON memories (libsql_vector_idx(vector,
  'metric=cosine', 'compress_neighbors=float8', 'max_neighbors=20'));
CREATE INDEX memories_tags_vec_idx ON memories (libsql_vector_idx(tags_vector,
  'metric=cosine', 'compress_neighbors=float8', 'max_neighbors=20'));
VACUUM;

Post-rebuild verification: row counts unchanged; vector_top_k smoke test returns correct semantic hits; multi-topic recall quality confirmed unaffected. This matches Kin's published case study (The space complexity of vector indexes in LibSQL, 8x reduction with identical search results on small datasets).

Note: because retrieval goes through vector_top_k('<index-name>', ...), rebuilding with the same index name requires no plugin code changes — the plugin picks up the tuned index transparently.

Suggestion

  1. Add 'compress_neighbors=float8', 'max_neighbors=20' to both index definitions in initShardDb() — optionally exposed as config keys (e.g. indexMaxNeighbors, indexNeighborCompression) for advanced users.
  2. Consider documenting libSQL's native REINDEX <idx> as the maintenance path for existing bloated shards (it rebuilds from base table data).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions