# RuVector-PGlite Implementation Plan ## 🎯 Executive Summary Create **@ruvector/pglite** - a lightweight WASM-based vector database extension for [PGlite](https://github.com/electric-sql/pglite) that brings ruvector's vector capabilities to browsers, edge environments, and serverless platforms. **Target**: ~500KB-1MB WASM bundle (vs full PostgreSQL extension) **Use Cases**: Browser vector search, edge computing, serverless, local-first apps ## 📊 Current State Analysis ### ruvector-postgres (Existing) - **Framework**: pgrx 0.12 (Rust → PostgreSQL extension) - **Build**: `cdylib` → native .so/.dylib/.dll - **Size**: Full PostgreSQL extension (~10-20MB) - **Features**: 53+ SQL functions, SIMD, GNN, SPARQL, Hyperbolic embeddings - **Target**: PostgreSQL 14-17 ### PGlite (Target Platform) - **Size**: 3MB gzipped WASM - **PostgreSQL**: v16.3 compiled to WASM - **Extensions**: Supports dynamic loading (pgvector confirmed working) - **Platforms**: Browser, Node.js, Bun, Deno - **Limitation**: Single-user/connection ## 🏗️ Architecture Design ### Three-Tier Strategy ``` ┌─────────────────────────────────────────────────────────┐ │ ruvector-core (NEW) │ │ - Shared vector types and operations │ │ - Platform-agnostic (no_std compatible) │ │ - Used by both postgres and pglite variants │ └─────────────────────────────────────────────────────────┘ ▲ ▲ │ │ ┌─────────────┴─────────┐ ┌────────────┴──────────────┐ │ ruvector-postgres │ │ ruvector-pglite (NEW) │ │ - Full features │ │ - Lightweight subset │ │ - pgrx framework │ │ - WASM target │ │ - Native compilation │ │ - pgrx + wasm32 │ │ - 53+ functions │ │ - Essential functions │ └───────────────────────┘ └───────────────────────────┘ ``` ### Feature Comparison Matrix | Component | ruvector-postgres | ruvector-pglite | |-----------|-------------------|-----------------| | **Vector Types** | | `vector` (f32) | ✅ | ✅ | | `halfvec` (f16) | ✅ | ✅ | | `binaryvec` | ✅ | ✅ | | `sparsevec` | ✅ | ✅ (simplified) | | `productvec` | ✅ | ❌ | | `scalarvec` | ✅ | ❌ | | **Distance Metrics** | | L2 (Euclidean) | ✅ | ✅ | | Cosine | ✅ | ✅ | | Inner Product | ✅ | ✅ | | L1 (Manhattan) | ✅ | ✅ | | Hamming | ✅ | ✅ | | Jaccard | ✅ | ❌ | | **Indexing** | | HNSW | ✅ Full | ✅ Lite (M=8, ef=32) | | IVFFlat | ✅ | ❌ | | Flat (brute-force) | ✅ | ✅ | | **Quantization** | | Binary | ✅ | ✅ | | Scalar (SQ8) | ✅ | ✅ | | Product (PQ) | ✅ | ❌ | | **SIMD** | | AVX-512 | ✅ | ❌ (WASM SIMD only) | | AVX2 | ✅ | ❌ | | NEON | ✅ | ❌ | | WASM SIMD | ❌ | ✅ | | **Advanced Features** | | GNN (GCN/GraphSage) | ✅ | ❌ | | SPARQL/Cypher | ✅ | ❌ | | ReasoningBank | ✅ | ❌ | | Hyperbolic space | ✅ | ❌ | | Attention mechanisms | ✅ | ❌ | | Routing (Tiny Dancer) | ✅ | ❌ | | **Target Size** | 10-20MB | 500KB-1MB | ## 🛠️ Implementation Phases ### Phase 1: Core Extraction (Week 1) **Goal**: Create `ruvector-core` with shared types ```rust // crates/ruvector-core/ ├── Cargo.toml // no_std compatible ├── src/ │ ├── lib.rs │ ├── types/ │ │ ├── vector.rs // f32 vector │ │ ├── halfvec.rs // f16 vector │ │ ├── binary.rs // binary vector │ │ └── sparse.rs // sparse vector (COO format) │ ├── distance/ │ │ ├── euclidean.rs │ │ ├── cosine.rs │ │ ├── inner.rs │ │ ├── hamming.rs │ │ └── traits.rs │ ├── quantization/ │ │ ├── binary.rs │ │ └── scalar.rs │ └── simd/ │ ├── wasm.rs // WASM SIMD intrinsics │ └── dispatch.rs // Runtime dispatch ``` **Key Changes**: - Extract from `ruvector-postgres/src/types/*` - Make `no_std` compatible (with `alloc` feature) - No PostgreSQL dependencies - WASM SIMD support via `core::arch::wasm32` ### Phase 2: PGlite Extension (Week 2) **Goal**: Create minimal PostgreSQL extension for WASM ```rust // crates/ruvector-pglite/ ├── Cargo.toml // target: wasm32-unknown-unknown ├── src/ │ ├── lib.rs // pgrx initialization │ ├── types.rs // PostgreSQL type wrappers │ ├── distance.rs // Distance SQL functions │ ├── operators.rs // <->, <=>, <#> operators │ ├── index/ │ │ ├── hnsw_lite.rs // Simplified HNSW (M=8) │ │ └── flat.rs // Brute-force fallback │ └── quantization.rs // Binary + Scalar only ``` **Cargo.toml Configuration**: ```toml [package] name = "ruvector-pglite" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] ruvector-core = { path = "../ruvector-core", default-features = false } pgrx = { version = "0.12", default-features = false } half = { version = "2.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["alloc"] } [profile.release] opt-level = "z" # Optimize for size lto = true # Link-time optimization codegen-units = 1 # Single codegen unit panic = "abort" # No unwinding strip = true # Strip symbols [package.metadata.pgrx] pg16 = "pg16" # Match PGlite's PostgreSQL version ``` **Build Configuration** (`.cargo/config.toml`): ```toml [target.wasm32-unknown-unknown] rustflags = [ "-C", "target-feature=+simd128", # Enable WASM SIMD "-C", "opt-level=z", # Size optimization ] ``` ### Phase 3: WASM Build Pipeline (Week 2) **Goal**: Automated WASM compilation ```bash # scripts/build-pglite.sh #!/bin/bash set -e echo "Building ruvector-pglite for WASM..." # Install wasm32 target rustup target add wasm32-unknown-unknown # Build with pgrx for wasm32 cd crates/ruvector-pglite cargo pgrx package --target wasm32-unknown-unknown --pg-version 16 # Output: target/wasm32-unknown-unknown/release/ruvector_pglite.wasm # Optimize with wasm-opt (from binaryen) wasm-opt -Oz \ target/wasm32-unknown-unknown/release/ruvector_pglite.wasm \ -o ../../npm/packages/pglite/dist/ruvector.wasm echo "✅ WASM build complete: $(du -h ../../npm/packages/pglite/dist/ruvector.wasm)" ``` **GitHub Actions** (`.github/workflows/build-pglite.yml`): ```yaml name: Build PGlite WASM Extension on: push: tags: ['pglite-v*'] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown - name: Install pgrx run: cargo install --locked cargo-pgrx@0.12 - name: Install binaryen (wasm-opt) run: sudo apt-get install -y binaryen - name: Build WASM run: ./scripts/build-pglite.sh - name: Upload artifact uses: actions/upload-artifact@v4 with: name: ruvector-pglite-wasm path: npm/packages/pglite/dist/ruvector.wasm ``` ### Phase 4: NPM Package (Week 3) **Goal**: TypeScript wrapper for PGlite ```typescript // npm/packages/pglite/ ├── package.json ├── tsconfig.json ├── src/ │ ├── index.ts // Main export │ ├── types.ts // TypeScript types │ ├── loader.ts // WASM loader │ └── extension.ts // PGlite extension wrapper ├── dist/ │ ├── ruvector.wasm // Built artifact │ ├── index.js // Compiled JS │ └── index.d.ts // Type definitions └── examples/ ├── browser.html ├── node.js └── deno.ts ``` **package.json**: ```json { "name": "@ruvector/pglite", "version": "0.1.0", "description": "Lightweight vector database for PGlite (WASM)", "main": "dist/index.js", "types": "dist/index.d.ts", "files": ["dist"], "keywords": ["pglite", "vector", "wasm", "embeddings", "similarity"], "peerDependencies": { "@electric-sql/pglite": "^0.2.0" }, "devDependencies": { "@electric-sql/pglite": "^0.2.0", "typescript": "^5.0.0" } } ``` **Extension Wrapper** (`src/extension.ts`): ```typescript import type { Extension } from '@electric-sql/pglite'; // WASM binary embedded import wasmBinary from '../dist/ruvector.wasm'; export const ruvector: Extension = { name: 'ruvector', setup: async (pg, context) => { // Load WASM extension const wasmModule = await WebAssembly.instantiate(wasmBinary); // Register with PGlite await pg.exec(` CREATE EXTENSION IF NOT EXISTS ruvector CASCADE; `); console.log('✅ RuVector extension loaded'); } }; ``` **Usage Example** (`examples/browser.html`): ```html
Check browser console for results
``` ### Phase 5: Testing & Optimization (Week 4) **Test Suite**: ```rust // crates/ruvector-pglite/tests/integration.rs #[cfg(test)] mod tests { use pgrx::pg_test; #[pg_test] fn test_vector_creation() { let vec = Spi::get_one::