13 KiB
Critical Gaps in RuVector-PGlite Implementation Plan
๐จ Major Architectural Flaws Discovered
After researching actual PGlite extension development, the original implementation plan has critical flaws that must be addressed.
โ What Was WRONG in the Original Plan
1. pgrx Does NOT Support WASM Compilation
Original Assumption: Use pgrx with wasm32-unknown-unknown target
# โ THIS DOESN'T WORK
[lib]
crate-type = ["cdylib"]
[target.wasm32-unknown-unknown]
# pgrx is not designed for WASM target
Reality:
- pgrx is designed to build native PostgreSQL extensions (.so, .dylib, .dll)
- pgrx is used to build extensions that run WebAssembly (via Extism), not extensions compiled to WebAssembly
- No evidence of pgrx supporting wasm32 as a compilation target
Sources:
- pgrx WebAssembly research
- No wasm32 target in pgrx documentation
2. Wrong Build Toolchain
Original Plan: cargo pgrx package --target wasm32
Reality: PGlite extensions require:
# โ
CORRECT: Emscripten toolchain
emcc -o extension.wasm extension.c \
-I$POSTGRES_INCLUDE \
-s MAIN_MODULE=1 \
-s ASYNCIFY
Required Tools:
- โ Emscripten SDK (emsdk)
- โ PostgreSQL headers for WASM
- โ
Tar packaging for
.tar.gzbundles - โ NOT cargo pgrx
3. Misunderstood Extension Structure
Original Plan: Build a standalone .wasm file
Reality: PGlite extensions are .tar.gz tarballs containing:
vector.tar.gz
โโโ extension/
โ โโโ vector.so.wasm # WASM compiled extension
โ โโโ vector.control # Extension metadata
โ โโโ vector--*.sql # SQL install scripts
โ โโโ data/ # Any data files
Actual pgvector Implementation:
// packages/pglite/src/vector/index.ts
const setup = async (_pg: PGliteInterface, emscriptenOpts: any) => {
return {
emscriptenOpts,
bundlePath: new URL('../../release/vector.tar.gz', import.meta.url),
}
}
export const vector = { name: 'pgvector', setup }
Source: PGlite vector extension source
4. Missing Build Process Details
What Was Missing:
- How to clone PGlite with submodules
- How to add extension to
postgres-pglite/pglite/Makefile - How to build within PGlite's build system
- Emscripten compilation flags (MAIN_MODULE, ASYNCIFY)
- Tarball packaging steps
Actual Process (source):
# 1. Clone PGlite
git clone --recurse-submodules git@github.com:electric-sql/pglite.git
cd pglite && pnpm i
# 2. Add extension as submodule
cd postgres-pglite/pglite
git submodule add <extension_url>
# 3. Register in Makefile
echo "SUBDIRS += ruvector" >> Makefile
# 4. Build (creates .tar.gz)
pnpm build:all
# Output: packages/pglite/release/ruvector.tar.gz
5. No Rust-Specific Guidance
Gap: How to write a Rust extension that compiles with Emscripten?
Missing Details:
- Rust โ C FFI interface layer
#[no_mangle]exports for PostgreSQL API- Memory management (Emscripten vs Rust allocator)
- Build script for
emcc+rustc
Possible Approaches:
Option A: Pure C Extension
// ruvector_pglite.c
#include "postgres.h"
#include "fmgr.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(vector_cosine_distance);
Datum vector_cosine_distance(PG_FUNCTION_ARGS) {
// Call Rust library via FFI
float32 result = rust_cosine_distance(...);
PG_RETURN_FLOAT4(result);
}
Then compile:
emcc -o ruvector.wasm ruvector_pglite.c libruvector_core.a \
-I$PG_INCLUDE -s MAIN_MODULE=1
Option B: Rust with C Wrapper
// ruvector_core/src/ffi.rs
#[no_mangle]
pub extern "C" fn rust_cosine_distance(
a: *const f32,
b: *const f32,
len: usize
) -> f32 {
// Safe Rust implementation
}
Then build:
# Build Rust to WASM staticlib
cargo build --target wasm32-unknown-emscripten --release
# Link with C wrapper
emcc -o ruvector.wasm wrapper.c libruvector_core.a \
-I$PG_INCLUDE -s MAIN_MODULE=1
6. Size Targets May Be Unrealistic
Original Target: 500KB-1MB WASM
Reality Check:
- pgvector (minimal, C-based): ~200KB compiled to WASM
- Full ruvector features (even stripped): likely 2-5MB
- Rust std library adds ~100-300KB
- PostgreSQL runtime overhead: varies
Revised Targets:
- Minimal (types + distances): ~500KB-1MB โ
- With HNSW index: ~1-2MB
- With quantization: ~2-3MB
- Full features: 5-10MB (defeats purpose)
7. No TypeScript Plugin API Consideration
Missing Alternative: PGlite's custom plugin API
Instead of a PostgreSQL extension, could build a TypeScript plugin that provides vector operations via PGlite's namespace API:
// Hybrid approach: TypeScript + WASM compute kernel
import { Extension } from '@electric-sql/pglite'
import init, { cosineDistance } from './ruvector_core.wasm'
const setup = async (pg: PGliteInterface) => {
await init() // Initialize WASM
return {
namespaceObj: {
vector: {
cosineDistance: (a: Float32Array, b: Float32Array) =>
cosineDistance(a, b), // WASM function
// Other vector operations...
}
}
}
}
export const ruvector = { name: 'ruvector', setup }
Usage:
const db = await PGlite.create({ extensions: { ruvector } })
// Use via JavaScript API (not SQL)
const dist = db.ruvector.vector.cosineDistance(vec1, vec2)
Pros:
- โ No Emscripten/PostgreSQL build complexity
- โ Direct WASM (no PostgreSQL FFI overhead)
- โ Easier to build and maintain
- โ Can still use Rust โ wasm-bindgen
Cons:
- โ Not SQL-compatible (no
SELECT ... ORDER BY embedding <=> $1) - โ Can't use PostgreSQL indexes
- โ Not a drop-in pgvector replacement
โ What's ACTUALLY Needed
Corrected Architecture Options
Option 1: Full PostgreSQL Extension (Complex but SQL-compatible)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ruvector-core (Rust library) โ
โ - Vector types, distances, HNSW โ
โ - Compiles to: libruvector_core.a โ
โ - Target: wasm32-unknown-emscripten โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โฒ
โ C FFI
โ
โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ruvector_pglite_wrapper.c โ
โ - PostgreSQL extension entry points โ
โ - PG_FUNCTION_INFO_V1 macros โ
โ - Calls Rust via FFI โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Emscripten
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ruvector.tar.gz โ
โ โโโ ruvector.so.wasm โ
โ โโโ ruvector.control โ
โ โโโ ruvector--0.1.0.sql โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ @ruvector/pglite (TypeScript) โ
โ - Extension loader โ
โ - Minimal wrapper (like pgvector) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Build Process:
- Fork PGlite repo
- Add ruvector as submodule in
postgres-pglite/pglite/ - Create Makefile with Emscripten rules
- Build Rust core to WASM staticlib
- Link with C wrapper
- Package to .tar.gz
- Create TypeScript loader
Pros: โ Full SQL compatibility, โ PostgreSQL indexes Cons: โ Complex build, โ Large size, โ Tight coupling to PGlite
Option 2: Hybrid TypeScript Plugin (Simpler, WASM-native)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ruvector-core (Rust library) โ
โ - Vector operations only โ
โ - No PostgreSQL dependencies โ
โ - wasm-bindgen for JS interop โ
โ - Target: wasm32-unknown-unknown โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ wasm-pack
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ruvector_core_bg.wasm + .js glue โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ @ruvector/pglite (TypeScript plugin) โ
โ - PGlite Extension interface โ
โ - Namespace API for vector ops โ
โ - SQL function wrappers (via exec) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Build Process:
# 1. Build Rust to WASM
cd ruvector-core
wasm-pack build --target web
# 2. Create TypeScript wrapper
cd ../npm/packages/pglite
pnpm build
# 3. Publish
pnpm publish
Pros: โ Simple build, โ Small size, โ Easy maintenance Cons: โ Limited SQL integration, โ No native indexes
Option 3: Minimal SQL Extension (Best Balance)
Start with Option 1 but with minimal features:
- โ Core vector type
- โ
Distance operators (
<->,<=>,<#>) - โ Skip HNSW (use flat scan)
- โ Skip quantization
- โ Skip advanced features
Target Size: ~200-500KB (comparable to pgvector)
๐ Revised Implementation Checklist
Prerequisites
- Clone PGlite repo with submodules
- Install Emscripten SDK (emsdk)
- Study pgvector's PGlite implementation
- Understand PGlite's build system
Development
- Create
ruvector-core(no PostgreSQL deps) - Add C FFI layer (
ffi.rswith#[no_mangle]) - Write C wrapper (
ruvector_wrapper.c) - Create extension control file (
ruvector.control) - Write SQL install script (
ruvector--0.1.0.sql)
Building
- Add as submodule to PGlite
- Configure Emscripten Makefile
- Build Rust to WASM staticlib
- Link with C wrapper using emcc
- Package to .tar.gz
Testing
- Write Vitest tests
- Test in browser environment
- Benchmark against pgvector
- Validate SQL compatibility
Publishing
- Create TypeScript loader
- Add to PGlite extensions catalog
- Publish to npm
- Write documentation
๐ฏ Recommended Path Forward
Start with Option 2 (TypeScript Plugin) for these reasons:
- Immediate Value: Can ship in 1-2 weeks vs 6+ weeks
- Learning Path: Understand PGlite before committing to Option 1
- Proof of Concept: Validate demand for ruvector-pglite
- Simpler: No Emscripten complexity
- Upgradeable: Can migrate to Option 1 later if SQL is critical
Then, if SQL compatibility is required, upgrade to Option 1 (Full Extension).
๐ Additional Research Needed
- Emscripten + Rust: Best practices for compiling Rust to WASM with emcc
- PGlite Build System: Deep dive into their Makefile and build scripts
- PostgreSQL C API: Required functions for minimal extension
- Memory Management: Emscripten's memory model vs Rust
- Size Optimization: Dead code elimination, LTO for WASM
Sources
- PGlite Extension Development Guide
- PGlite pgvector Implementation
- In-Browser Semantic Search with PGlite
- PGlite Extensions Catalog
- Bringing WebAssembly to PostgreSQL
- Compiling Postgres to WASM with PGlite
Next Step: Choose an implementation option and update the plan accordingly.