fenecdb

Embedded Rust

fenec-core is a plain Rust library with no dependencies. Parsing is fenec-ql; everything else is the engine.

use fenec_core::prelude::*;
use fenec_ql::parse;

let mut db = fenec_core::fs::open("data.fenec")?;
for stmt in parse("get docs near embed $1 limit 5")? {
    let r = db.execute_with(&stmt, &[Value::Vector(embedding)])?;
}

Reads and writes take different locks

Database::query takes a shared lock and execute_with the exclusive one. There are no transactions: BEGIN and COMMIT parse and do nothing.

One process per file. Two processes opening the same file and writing it corrupt it. There is no lock file and no advisory locking — the single-writer rule is yours to keep.

Durability is the caller's job

File writes are buffered. If the process crashes before sync is called, the last writes are lost. In embedded use nothing calls it for you — fenec-pg has the --sync policy and a shutdown hook precisely because the library does not.

The same applies to the HNSW graph. It is derived data and is written only during snapshot, compact and checkpoint, never on the write path. Without a graph in the file it is rebuilt on open: 10.2 s at 100 000 × 128, against 110 ms with it.

Measuring the footprint

Database::memory_bytes() sums the segment bytes, offset indexes, vector arenas and graph links from counters. It is not RSS — allocator slack, HNSW build buffers, upper-level neighbour allocations and the 2–3× transient peaks all sit outside it. Measured ratios are in Limits.

Features

FeatureWhat it brings
std-fsBuffered file I/O — fenec_core::fs. Off in wasm builds.
threadsThe parallel HNSW build path, compiled out of wasm32 entirely.

now() errors on wasm32-unknown-unknown, which has no clock; time is passed in as a parameter there.

Errors

Error in fenec-core/src/error.rs is the single error type across the whole engine: allocation-free variants, no Box. fenec-pg maps it onto PostgreSQL SQLSTATE codes.

Plugins

The registry takes after PostgreSQL's extension model. Scalar functions, write hooks in the shape of triggers, and transport adapters can all be registered.

db.install_plugin(&PgPlugin)?;   // pg_version(), pg_typeof(), to_pgvector()

Module map

storeSegments and the offset index
engineDatabase, Collection, replay, snapshot, compact, checkpoint
vectorHNSW and the distance kernels
queryStatement and plan execution
schema value codec jsonTypes, the binary codec, the JSON parser
timeCalendar arithmetic, integer only
changesThe change ring behind subscriptions
pluginThe registry
fsBuffered file I/O, behind std-fs