Files
wifi-densepose/docs/research/RUVECTOR_PGLITE_IMPLEMENTATION_PLAN.md
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

15 KiB

RuVector-PGlite Implementation Plan

🎯 Executive Summary

Create @ruvector/pglite - a lightweight WASM-based vector database extension for 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

// 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

// 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:

[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):

[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

# 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):

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

// 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:

{
  "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):

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):

<!DOCTYPE html>
<html>
<head>
  <script type="module">
    import { PGlite } from 'https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist/index.js';
    import { ruvector } from 'https://cdn.jsdelivr.net/npm/@ruvector/pglite/dist/index.js';

    const db = await PGlite.create({
      extensions: { ruvector }
    });

    // Create table with vector column
    await db.exec(`
      CREATE TABLE embeddings (
        id SERIAL PRIMARY KEY,
        content TEXT,
        embedding vector(384)
      );

      CREATE INDEX ON embeddings USING hnsw (embedding vector_cosine_ops);
    `);

    // Insert vectors
    const embedding = Array(384).fill(0).map(() => Math.random());
    await db.query(
      'INSERT INTO embeddings (content, embedding) VALUES ($1, $2)',
      ['Sample text', JSON.stringify(embedding)]
    );

    // Similarity search
    const results = await db.query(`
      SELECT content, embedding <=> $1 AS distance
      FROM embeddings
      ORDER BY distance
      LIMIT 5
    `, [JSON.stringify(embedding)]);

    console.log('Search results:', results.rows);
  </script>
</head>
<body>
  <h1>RuVector PGlite Demo</h1>
  <p>Check browser console for results</p>
</body>
</html>

Phase 5: Testing & Optimization (Week 4)

Test Suite:

// 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::<Vec<f32>>(
            "SELECT '[1,2,3]'::vector"
        ).unwrap();
        assert_eq!(vec.len(), 3);
    }

    #[pg_test]
    fn test_cosine_distance() {
        let dist = Spi::get_one::<f32>(
            "SELECT '[1,0,0]'::vector <=> '[0,1,0]'::vector"
        ).unwrap();
        assert!((dist - 1.0).abs() < 0.001);
    }

    #[pg_test]
    fn test_hnsw_index() {
        Spi::run("
            CREATE TABLE items (id int, vec vector(3));
            INSERT INTO items VALUES (1, '[1,0,0]'), (2, '[0,1,0]');
            CREATE INDEX ON items USING hnsw (vec vector_cosine_ops);
        ").unwrap();
    }
}

Size Optimization Checklist:

  • Use opt-level = "z" in release profile
  • Enable LTO (Link-Time Optimization)
  • Strip debug symbols
  • Run wasm-opt -Oz
  • Minimize dependencies (use default-features = false)
  • Avoid large data structures in binary
  • Use lazy initialization for indexes

📈 Success Metrics

Metric Target Measurement
WASM size < 1MB du -h ruvector.wasm
Load time (browser) < 500ms Performance API
Query latency (1k vectors) < 10ms Benchmark suite
Memory usage < 50MB for 100k vectors Chrome DevTools
Compatibility Chrome 91+, Firefox 89+, Safari 16.4+ Manual testing

🚀 Deployment Strategy

Publishing Flow

  1. Build WASM: ./scripts/build-pglite.sh
  2. Run tests: cargo test -p ruvector-pglite
  3. Optimize: wasm-opt -Oz
  4. Publish npm: npm publish --access public
  5. Tag release: git tag pglite-v0.1.0 && git push --tags

CDN Distribution

// Via unpkg
import { ruvector } from 'https://unpkg.com/@ruvector/pglite@latest/dist/index.js';

// Via jsDelivr
import { ruvector } from 'https://cdn.jsdelivr.net/npm/@ruvector/pglite@latest/dist/index.js';

// Via esm.sh
import { ruvector } from 'https://esm.sh/@ruvector/pglite@latest';

🎯 Use Cases Enabled

  1. In-Browser Semantic Search

    • Chat interfaces with local embedding search
    • Document search without server round-trips
    • Privacy-first search (data never leaves browser)
  2. Edge Computing

    • Cloudflare Workers with vector search
    • Deno Deploy with similarity matching
    • Vercel Edge Functions with embeddings
  3. Desktop Apps

    • Electron apps with local vector DB
    • Tauri apps with Rust + WASM synergy
    • VS Code extensions with semantic code search
  4. Mobile Apps

    • React Native with local vector search
    • Capacitor/Ionic with PGlite
    • Expo apps with offline-first embeddings
  5. Development/Testing

    • No Docker required for local dev
    • Fast test suites with in-memory DB
    • Prototype vector apps in CodeSandbox/StackBlitz

🔄 Maintenance Plan

  • Weekly: Monitor PGlite releases for compatibility
  • Monthly: Sync features from ruvector-postgres
  • Quarterly: Performance audits and size optimization
  • Yearly: Major version alignment with PostgreSQL

📚 Documentation Plan

  1. README.md: Quick start, installation, examples
  2. API.md: Full API reference for all functions
  3. PERFORMANCE.md: Benchmarks and optimization tips
  4. MIGRATION.md: Guide for pgvector users
  5. CONTRIBUTING.md: How to contribute to pglite variant

🤝 Collaboration Opportunities

  • PGlite Team: Coordinate on extension API improvements
  • pgvector Team: Ensure SQL compatibility
  • Transformers.js: Integration examples for embeddings
  • LangChain: Add @ruvector/pglite as vector store option

⚠️ Risks & Mitigations

Risk Impact Mitigation
WASM size bloat High Aggressive optimization, feature gating
pgrx WASM support gaps Medium Fallback to manual FFI if needed
PGlite breaking changes Medium Pin to stable versions, monitor releases
Performance vs native Low Clear documentation of tradeoffs

📅 Timeline

  • Week 1: Core extraction, architecture setup
  • Week 2: PGlite extension, WASM build pipeline
  • Week 3: NPM package, TypeScript wrapper, examples
  • Week 4: Testing, optimization, documentation
  • Week 5: Beta release, community feedback
  • Week 6: v1.0 launch

Next Steps: Ready to proceed? I can start with Phase 1 (Core Extraction) immediately.