|
| 1 | +# Deprecate sqlite-diskann and Migrate Assets to sqlite-vec |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Benchmarks show sqlite-diskann has prohibitive performance issues (2,268x slower builds, 74x larger indexes than sqlite-vec, similar QPS). Gracefully deprecate the npm package, archive the repository, and migrate valuable benchmark infrastructure and research findings to sqlite-vec. |
| 6 | + |
| 7 | +## Current Phase |
| 8 | + |
| 9 | +- [ ] Research & Planning |
| 10 | +- [ ] Test Design |
| 11 | +- [ ] Implementation Design |
| 12 | +- [ ] Test-First Development |
| 13 | +- [ ] Implementation |
| 14 | +- [ ] Integration |
| 15 | +- [ ] Cleanup & Documentation |
| 16 | +- [ ] Final Review |
| 17 | + |
| 18 | +## Required Reading |
| 19 | + |
| 20 | +- `benchmarks/src/harness.ts` - Multi-library comparison framework |
| 21 | +- `benchmarks/src/runners/usearch-runner.ts` - USearch integration |
| 22 | +- `experiments/README.md` - Experiment methodology |
| 23 | +- `experiments/experiment-005-100k-recall.md` - Post-mortem findings |
| 24 | +- `package.json` - npm package configuration |
| 25 | +- `../sqlite-vec/` - Target repository for migration |
| 26 | + |
| 27 | +## Description |
| 28 | + |
| 29 | +**Problem:** sqlite-diskann is not viable compared to alternatives: |
| 30 | +- Build time: 3626s vs 1.6s (sqlite-vec) = 2,268x slower |
| 31 | +- Index size: 7.5 GB vs 100 MB (sqlite-vec) = 74x larger |
| 32 | +- QPS: ~46 (same as sqlite-vec's brute force) |
| 33 | +- Only advantage: recall is good (93-100%), but not enough to offset costs |
| 34 | + |
| 35 | +**Constraints:** |
| 36 | +- Package already published to npm (v0.1.2, 2 versions total) |
| 37 | +- Users may have installed it (minimize disruption) |
| 38 | +- Valuable assets should be preserved (benchmark infrastructure, research) |
| 39 | +- sqlite-vec has minimal benchmark infrastructure (just Rust micro-benchmarks) |
| 40 | + |
| 41 | +**Success Criteria:** |
| 42 | +- [ ] New repo `node-vector-bench` created with benchmark infrastructure |
| 43 | +- [ ] npm package deprecated with clear explanation and guidance |
| 44 | +- [ ] Post-mortem narrative explains what happened and why |
| 45 | +- [ ] Experiment methodology preserved in new repo |
| 46 | +- [ ] sqlite-diskann repo archived with deprecation notice |
| 47 | +- [ ] All links work, no orphaned docs |
| 48 | + |
| 49 | +## Tribal Knowledge |
| 50 | + |
| 51 | +**npm Package Details:** |
| 52 | +- Package: `@photostructure/sqlite-diskann` |
| 53 | +- Current version: 0.1.2 |
| 54 | +- Published: 5 days ago by GitHub Actions |
| 55 | +- Downloads: Unknown (likely minimal given recent publish) |
| 56 | +- Includes: prebuilds/, dist/, TypeScript bindings |
| 57 | + |
| 58 | +**Benchmark Infrastructure:** |
| 59 | +- Self-contained in `benchmarks/` subdirectory with own package.json |
| 60 | +- Multi-library runner supporting diskann, vec, usearch |
| 61 | +- USearch integration provides HNSW comparison baseline |
| 62 | +- Profile-based configuration (JSON) |
| 63 | +- Ground truth computation and caching |
| 64 | +- Statistical metrics (recall@k, percentile latencies, QPS) |
| 65 | +- Multiple reporters (console table, JSON, markdown) |
| 66 | +- Dataset generation utilities |
| 67 | + |
| 68 | +**Experiment Methodology:** |
| 69 | +- Template-driven documentation (`experiments/template.md`) |
| 70 | +- Hypothesis-driven testing approach |
| 71 | +- Structured experiment index |
| 72 | +- Guidelines document (`experiments/README.md`) |
| 73 | +- Valuable for any performance engineering work |
| 74 | + |
| 75 | +**sqlite-vec Current State:** |
| 76 | +- Has `benchmarks/` directory but only Rust micro-benchmarks |
| 77 | +- Empty README.md in benchmarks/ |
| 78 | +- No multi-library comparison infrastructure |
| 79 | +- Would benefit from this benchmark harness |
| 80 | + |
| 81 | +**Key Findings to Preserve:** |
| 82 | +- Block size limitations (4KB → 2-3 edges max for 256D) |
| 83 | +- Graph fragmentation at scale |
| 84 | +- BLOB I/O overhead dominates |
| 85 | +- Write performance bottlenecks (profiling data) |
| 86 | +- Why DiskANN's advantages don't materialize in SQLite |
| 87 | + |
| 88 | +## Solutions |
| 89 | + |
| 90 | +### Option 1: Standalone Benchmark Repo (Recommended) |
| 91 | + |
| 92 | +**Create new repo: `node-vector-bench`** |
| 93 | +- Implementation-agnostic vector search benchmarking framework |
| 94 | +- Multi-library runner supporting any vector library (diskann, vec, usearch, hnswlib, etc.) |
| 95 | +- Profile-based configuration, ground truth computation, statistical reporting |
| 96 | +- Experiment methodology and templates |
| 97 | +- Not tied to any specific implementation |
| 98 | + |
| 99 | +**Migrate from sqlite-diskann:** |
| 100 | +- Benchmark harness (`benchmarks/src/`, `benchmarks/package.json`) |
| 101 | +- USearch runner (as reference competitor) |
| 102 | +- Add sqlite-vec runner (migrate from existing) |
| 103 | +- Experiment methodology (`experiments/template.md`, `experiments/README.md`) |
| 104 | + |
| 105 | +**Deprecate sqlite-diskann:** |
| 106 | +- Publish v0.2.0 with deprecation notice and clear narrative of why it failed |
| 107 | +- Update package.json with `"deprecated"` field |
| 108 | +- Add prominent deprecation banner to README with link to post-mortem |
| 109 | +- Archive GitHub repo (read-only) |
| 110 | +- Keep public as learning resource |
| 111 | + |
| 112 | +**Pros:** |
| 113 | +- Preserves valuable benchmark work in neutral location |
| 114 | +- Benefits entire Node.js vector search ecosystem |
| 115 | +- Not tied to any vendor or implementation |
| 116 | +- sqlite-vec can use it without inheriting diskann baggage |
| 117 | +- Clear migration path for users |
| 118 | +- Post-mortem becomes valuable reference |
| 119 | + |
| 120 | +**Cons:** |
| 121 | +- Need to create new repo |
| 122 | +- Some work to make runners pluggable |
| 123 | +- Need to document runner interface |
| 124 | + |
| 125 | +**Status:** Recommended approach |
| 126 | + |
| 127 | +### Option 2: Migrate to sqlite-vec |
| 128 | + |
| 129 | +**Pros:** |
| 130 | +- Simpler - one destination |
| 131 | +- sqlite-vec gets better benchmarks |
| 132 | + |
| 133 | +**Cons:** |
| 134 | +- sqlite-vec already has its own benchmark infrastructure |
| 135 | +- Ties universal benchmark tool to specific implementation |
| 136 | +- Confusing to have diskann benchmarks in vec repo |
| 137 | + |
| 138 | +**Status:** Rejected - standalone is cleaner |
| 139 | + |
| 140 | +### Option 3: Minimal Deprecation |
| 141 | + |
| 142 | +**Just deprecate without preserving anything:** |
| 143 | + |
| 144 | +**Pros:** |
| 145 | +- Minimal effort |
| 146 | + |
| 147 | +**Cons:** |
| 148 | +- Loses valuable benchmark infrastructure |
| 149 | +- Loses experiment methodology |
| 150 | +- Wastes months of research findings |
| 151 | + |
| 152 | +**Status:** Rejected - too much value left on table |
| 153 | + |
| 154 | +## Tasks |
| 155 | + |
| 156 | +### Phase 1: Create node-vector-bench repo |
| 157 | +- [ ] Create new GitHub repo: `node-vector-bench` |
| 158 | +- [ ] Initialize with README explaining purpose |
| 159 | +- [ ] Set up package.json structure |
| 160 | +- [ ] Define runner interface |
| 161 | + |
| 162 | +### Phase 2: Migrate benchmark infrastructure |
| 163 | +- [ ] Copy benchmark harness (`benchmarks/src/`) |
| 164 | +- [ ] Copy USearch runner |
| 165 | +- [ ] Copy benchmark profiles |
| 166 | +- [ ] Copy dataset utilities |
| 167 | +- [ ] Update imports for new structure |
| 168 | +- [ ] Test with USearch runner |
| 169 | + |
| 170 | +### Phase 3: Add sqlite-vec runner |
| 171 | +- [ ] Copy sqlite-vec runner from sqlite-diskann |
| 172 | +- [ ] Test sqlite-vec runner in new repo |
| 173 | +- [ ] Verify benchmarks work end-to-end |
| 174 | + |
| 175 | +### Phase 4: Migrate experiment methodology |
| 176 | +- [ ] Copy experiment template and README |
| 177 | +- [ ] Update paths in experiment docs |
| 178 | +- [ ] Link to node-vector-bench from experiments |
| 179 | + |
| 180 | +### Phase 5: Deprecate sqlite-diskann npm package |
| 181 | +- [ ] Update package.json to v0.2.0 |
| 182 | +- [ ] Add `"deprecated"` field with message |
| 183 | +- [ ] Update README with deprecation banner |
| 184 | +- [ ] Add post-mortem narrative to README |
| 185 | +- [ ] Publish final version to npm |
| 186 | + |
| 187 | +### Phase 6: Archive sqlite-diskann repo |
| 188 | +- [ ] Add deprecation notice to README |
| 189 | +- [ ] Link to node-vector-bench |
| 190 | +- [ ] Link to post-mortem section |
| 191 | +- [ ] Update repo description |
| 192 | +- [ ] Archive repository on GitHub |
| 193 | + |
| 194 | +**Verification:** |
| 195 | + |
| 196 | +```bash |
| 197 | +# Test benchmark harness in sqlite-vec |
| 198 | +cd ../sqlite-vec/benchmarks |
| 199 | +npm install |
| 200 | +npm run bench -- profiles/quick.json |
| 201 | + |
| 202 | +# Verify npm deprecation |
| 203 | +npm view @photostructure/sqlite-diskann |
| 204 | + |
| 205 | +# Check archived repo status |
| 206 | +# (Manual GitHub UI check) |
| 207 | +``` |
| 208 | + |
| 209 | +## Post-mortem narrative |
| 210 | + |
| 211 | +Draft for README deprecation notice and standalone document. |
| 212 | + |
| 213 | +### Why we abandoned sqlite-diskann |
| 214 | + |
| 215 | +We extracted the DiskANN implementation from libSQL into a standalone SQLite extension (Feb 9-10). The code worked, but benchmarks were bad. We spent the next week figuring out why and trying to fix it. |
| 216 | + |
| 217 | +**The recall problem.** At 10k vectors, recall was 97%. At 100k, it dropped to 0-1%. We tuned `searchListSize`, `pruning_alpha`, and `MIN_DEGREE` with no effect. Turns out the default 4KB block size only fits 2-3 edges per node for 256D vectors (`(4096 - 1040) / 1040 = 2.9`). The graph fragmented into disconnected components. See `_done/20260210-diskann-recall-fix-investigation.md`. |
| 218 | + |
| 219 | +**Fixing recall broke everything else.** Switching to auto-calculated 40KB blocks (Feb 11) restored recall to 98%. But 40KB per node means 7.5 GB indexes for 100k vectors (sqlite-vec uses 100 MB). Build time: 3626 seconds vs 1.6 seconds. See `experiment-005-100k-recall.md`. |
| 220 | + |
| 221 | +**We tried to optimize build performance.** None of it worked well enough: |
| 222 | + |
| 223 | +- **Parallel construction** (`_todo/20260210-parallel-graph-construction.md`): SQLite only allows one writer. Graph mutations can't be parallelized. |
| 224 | +- **BLOB caching** (`_todo/20260211-build-speed-optimization.md`): LRU cache with refcounting. 37% faster (707s to 445s for 25k). Still 1,667x slower than brute force. |
| 225 | +- **Batch insert API** (`_todo/20260211-serial-batch-insert.md`): Persistent cache across inserts, amortized SAVEPOINT overhead. 10-20% gain. We stopped benchmarking at that point. |
| 226 | +- **Lazy back-edges** (`_todo/20260212-lazy-back-edges.md`): Defer edge updates, batch repair at commit. Helped throughput but hurt recall at small scale. Complex code for marginal gains. |
| 227 | +- **Parameter sweeps** (`_done/20260212-max-neighbors-sweep.md`, experiments 002b-004): Tested `max_neighbors` from 24 to 64 and `searchListSize` from 100 to 500. Recall improved, build time didn't. |
| 228 | + |
| 229 | +**The problem is architectural.** DiskANN assumes direct memory control: mmap the graph, traverse it cheaply. SQLite's BLOB I/O adds a 40KB read per graph hop. Each insert touches ~200 nodes, so that's 16 MB of I/O per insert. At 100k inserts, the cumulative BLOB I/O is around 800 TB. No amount of caching or batching fixes that. |
| 230 | + |
| 231 | +**Final numbers (Feb 15):** |
| 232 | + |
| 233 | +- Build: 3626s vs 1.6s (sqlite-vec), 2,268x slower |
| 234 | +- Index: 7.5 GB vs 100 MB, 74x larger |
| 235 | +- QPS: ~46, same as sqlite-vec brute force at 100k |
| 236 | +- Recall: 98% (the one metric that worked) |
| 237 | + |
| 238 | +**Use `@photostructure/sqlite-vec` instead.** For datasets under 100k, brute force builds in 1.6 seconds with perfect recall. For datasets over 1M, use an external index like USearch with mmap. |
| 239 | + |
| 240 | +See also: `_todo/` and `_done/` directories for full TPPs, `experiments/` for benchmark data. |
| 241 | + |
| 242 | +## Notes |
| 243 | + |
| 244 | +**Session 2026-02-15:** |
| 245 | +- User showed benchmark results: diskann loses on all metrics except recall |
| 246 | +- Build: 3626s vs 1.6s (sqlite-vec) |
| 247 | +- Index: 7.5GB vs 100MB (sqlite-vec) |
| 248 | +- QPS: ~46 (both libraries similar) |
| 249 | +- User requested help with deprecation and migration |
| 250 | +- Decision: Create standalone `node-vector-bench` repo (not migrate to sqlite-vec) |
| 251 | +- Reason: sqlite-vec has own benchmarks, standalone benefits whole ecosystem |
| 252 | +- Wrote post-mortem narrative summarizing optimization journey |
| 253 | +- Catalogued TPPs showing what was tried and why it failed |
| 254 | +- Catalogued assets: benchmark harness is valuable, experiment methodology is valuable |
| 255 | +- sqlite-vec has minimal benchmark infrastructure currently |
0 commit comments