Phase 1 — Embedded Library
The implementation map for the in-process embeddable library: libengine + the frozen C ABI + the @twilldb/bun wrapper, backed by LocalFileStorage over a stable Storage seam.
Overview
This document maps the implemented code to the Phase 1 deliverables, exit criteria, and Definition of Done in the roadmap, and records the deliberate scope decisions.
What shipped
| Deliverable (spec 13 §Phase 1) | Where |
|---|---|
libengine.a / libengine.so (+.dylib/.dll) static & dynamic libs | crates/engine (crate-type = ["cdylib","staticlib","rlib"]) |
engine.h — the stable C ABI (engine_open/exec/query/prepare/begin/commit/…) | crates/engine/include/engine.h, implemented in crates/engine/src/ffi.rs |
@twilldb/bun thin TypeScript wrapper over bun:ffi | clients/bun/ (src/ffi.ts raw bindings, src/index.ts typed API) |
LocalFileStorage — first concrete Storage impl | crates/storage/src/local.rs |
| Local cache / in-process working set (the SHOULD) | the engine's in-process MVCC store (crates/engine/src/store.rs) is the buffer; durability is the WAL |
Exit criteria → evidence
- Open a
file://DB from Bun via FFI; DDL + DML + queries; correct results across a process restart.clients/bun/test/embedded.test.ts(persists across reopen) andcrates/engine/tests/engine.rs(persists_across_restart). State is rebuilt purely by replaying the durable WAL (crates/engine/src/db.rs::replay). - Basic correctness gate + MVCC snapshot isolation (a reader sees a stable snapshot across a concurrent committed write).
engine.rs::mvcc_snapshot_isolationandembedded.test.ts(MVCC snapshot isolation across two handles). Visibility rules:store.rs::RowVersion. @twilldb/bundemonstrated end-to-end with no native build step beyond the prebuilt library.clients/bun/examples/notes.ts.- Storage seam conformance (durability-after-ack, monotonic LSN, snapshot reads, fencing, crash hooks, batch reads, branch creation, retention). C1–C8 in
crates/storage/src/conformance.rs, run againstLocalFileStorageincrates/storage/tests/conformance.rs, plus a torn-trailing-frame recovery test (the in-process analog of kill -9 mid-append). - No panics across FFI; misuse is defined, not UB. Every export is wrapped in
catch_unwind→ENGINE_ERR_INTERNAL; null handles →ENGINE_ERR_MISUSE(crates/engine/tests/ffi.rs).
Architecture decisions (and why)
- The
Storagetrait isasyncfrom Phase 1. Spec 13 requires the trait to stay signature-stable once Phase 2 adds the network-boundObjectStoragebackend. Making it async now avoids a later breaking change;LocalFileStorageresolves synchronously and the engine drives it with a tiny dependency-freeblock_on(crates/storage/src/lib.rs). The C ABI stays synchronous (engine_commitblocks until durable). - Two buildable additions to the source trait, both forward-compatible with
ObjectStorage:scan_wal(recovery read — the engine replays the durable log on open) andput_page(the page store's write path; the source trait names only the read pathget_page). They share one monotonic LSN counter. - WAL-centric engine. Durability and recovery go through the WAL; the working set lives in the in-process store (the buffer the cache spec formalizes). The page read API (
get_page/get_pages) is implemented and conformance-tested in the storage layer and becomes the cold-read path whenObjectStoragelands in Phase 2 (where cold reads pay a network round-trip and the cache is mandatory). - Single writer per database, snapshot isolation. Store mutation serializes through a write lane; readers capture a snapshot LSN and never block. A first-committer / first-toucher-wins check (
exec.rs::check_no_conflict) keeps SI correct — extended to concurrent in-flight writers, whose pending versions coexist tagged by anowner(store.rs). - Group commit (the W1 lever). The lane is released before the durable append, so transactions that are ready to commit coalesce into a single
append_walvia a leader/follower coordinator (group_commit.rs) — amortizing onefsync/CAS round-trip across the batch, never acking before the batch is durable. Each member publishes at its own commit LSN from the one contiguous LSN range the append returns. Proven by Benchmark Experiment 2 (the plateau clears the Exp-1 ceiling) and gated bytests/group_commit.rs. - Crash safety.
LocalFileStoragewrites CRC-checked, length-prefixed frames andfsyncs before returning the commit LSN — never ack-before-durable. On reopen, a torn trailing frame is detected and truncated; the WAL replay then rebuilds state, so every acked commit survives and no half-state is replayed.
Deliberate Phase-1 limitations (documented, not accidental)
- Branching (
engine_branch) is reserved, not implemented. The roadmap explicitly forbids folding a later phase's concern into Phase 1; the ABI symbol is frozen here but returns NULL with an explanatoryengine_last_error(copy-on-write branching is Phase 4). - DDL runs in autocommit only.
CREATE/DROP TABLEinside an explicit transaction returnsENGINE_ERR_TXN. (Row DML is fully transactional.) - SQL subset. A focused hand-written parser supports
CREATE/DROP TABLE,INSERT,SELECT(projection,WHERE,ORDER BY,LIMIT,COUNT/SUM/MIN/MAX/AVG),UPDATE,DELETE, andBEGIN/COMMIT/ROLLBACK. Joins, GROUP BY, subqueries, andDISTINCTare out of scope for Phase 1 and rejected withENGINE_ERR_SQL. s3:///r2://schemes are rejected with a clear "Phase-2 backend" error; unknown schemes are rejected outright (no silent default).- In-process database sharing. Multiple handles to the same
file://URL in one process share MVCC state via a registry (so the snapshot-isolation guarantee holds across handles). Cross-process concurrent writers are not a Phase 1 target (single-writer-per-DB; fencing is conformance-tested).
Running
cargo test # storage conformance + engine correctness + FFI
cargo build -p twill-engine --release # target/release/libengine.{a,so}
cd clients/bun && bun test # embedded end-to-end
Related
ENGEngine CoreRust library: parser → plan → executor, MVCC via LSN-stamped versions, WAL generation, C ABI.
STORStorage InterfaceThe narrow
Storage trait frozen here and satisfied first by LocalFileStorage.
BUNBun IntegrationThe bun:ffi binding over engine.h and the @twilldb/bun wrapper shipped in Phase 1.
ROADRoadmap & Build SequenceThe phased plan whose Phase 1 deliverables and Definition of Done this map implements.