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
| Feature | What it brings |
|---|---|
std-fs | Buffered file I/O — fenec_core::fs. Off in wasm builds. |
| threads | The 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
store | Segments and the offset index |
engine | Database, Collection, replay, snapshot, compact, checkpoint |
vector | HNSW and the distance kernels |
query | Statement and plan execution |
schema value codec json | Types, the binary codec, the JSON parser |
time | Calendar arithmetic, integer only |
changes | The change ring behind subscriptions |
plugin | The registry |
fs | Buffered file I/O, behind std-fs |