You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
craft: in-memory reference model + client-facing API surface (#166)
Add a HomeStore-free, in-process reference model of the CRAFT replication
protocol (the server side) so a client can be built and tested against it,
plus the client-facing CRAFT API it exposes.
Public API (home_blocks.hpp): per-replica CRAFT free functions over an opaque
volume_handle -- login / async_write / async_read / keep_alive -- each carrying
a client_hdr {term, commit_lsn, all_committed_lsn}. Addressing is byte-based
(addr/len aligned to lba_size, returned by login and enforced server-side);
sisl::sg_list is the one buffer type both ways (an empty buffer is a zero
write); reads fill the caller's buffer and return a sparse io_extent layout.
Commit is piggybacked via client_hdr.commit_lsn -- there is no standalone
commit verb -- and keep_alive is its term-fenced carrier. The legacy byte
block ops are marked [[deprecated]] (kept as reference).
Model (src/lib/craft/memory/): MemCraftReplica implements the internal
craft_replica backend (journal, per-LBA index, journal-tail overlay,
horizon-clamped sparse reads, term fencing, in-order commit apply);
MemTransport is the in-process network + cold path (fixed leader, login
orchestration) + fault hooks (replica down/up, sub-quorum).
create_memory_volume / make_memory_replica_set wrap each replica behind a
volume_handle (one handle == one replica device; no aggregate handle). The
model links no HomeStore -- its unit-test binary proves it -- and only the
create_memory_volume glue reuses volume (and thus links the engine).
Tests: 15 model unit tests (no HomeStore, no iomgr reactor; ops complete
inline) + 3 end-to-end tests through the public volume_handle API.
Docs: docs/craft/{api,rpcs,README}.md updated to the landed surface.
Also removes the obsolete disabled memory_backend stub.
Copy file name to clipboardExpand all lines: docs/craft/README.md
+6-2Lines changed: 6 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,14 +40,18 @@ synchronization, and recovery bookkeeping. Write data never flows through the RA
40
40
|**InternalLogin**| A RAFT log entry type. On apply, stores the new `client_token` and enforces single-writer exclusivity. |
41
41
|**Missing**| A dLSN slot that a replica knows about (from a peer or from the RAFT log) but has not yet received data for. |
42
42
|**Empty**| A dLSN proven never quorum-durable, declared by the leader; a permanent no-op the commit skips. |
43
-
|**all_zeros (zero write)**| A payload-free write naming just an LBA range (the WRITE_ZEROES / discard-to-zero op). Takes a dLSN and merges like any write; allocates nothing and unmaps its range on apply. |
43
+
|**zero write (all_zeros)**| A payload-free write naming just a byte range (WRITE_ZEROES / discard-to-zero), signaled by an **empty `sisl::sg_list`** (no `all_zeros` flag). Takes a dLSN and merges like any write; allocates nothing and unmaps its range on apply, reading back as a hole. |
44
44
|**hole**| A read sub-range with no data (never written, zero-written, or an all-zero region collapsed at read time). Returned as a marker, read as zeros; **not** the same as `Missing`. |
45
+
|**client_hdr**| The session + watermark fields stamped on every client IO: `{term, commit_lsn, all_committed_lsn}` (see the commit note below). |
46
+
|**lba_size**| The volume block size in bytes, returned by `login`; the client aligns every byte `addr`/`len` to it and presents the geometry to the filesystem. |
47
+
|**io_extent**| One sub-range of a read's sparse layout, in **bytes**: `{addr, len, hole}` — carries no bytes (they are in the caller's buffer). |
45
48
46
49
## Key design properties
47
50
48
51
-**Single writer**: only one client at a time owns a partition (enforced by `InternalLogin` RAFT entry).
49
52
-**Leaderless data path**: after login, the RAFT leader has no special role for writes or reads.
50
-
-**Client drives commit**: replicas apply to the index only when told (via `commit` / `keep_alive`), strictly in dLSN order at the contiguous frontier. Readability is per-write and does not wait: appended entries are served from the journal-tail overlay (no index write on the read path).
53
+
-**Client drives commit (piggybacked, no standalone verb)**: the client stamps its `commit_lsn` on every write / read / keep_alive (`client_hdr`); replicas apply to the index strictly in dLSN order at the contiguous frontier. `keep_alive` is the dedicated carrier (commit + watchdog reset, term-fenced). Readability is per-write and does not wait: appended entries are served from the journal-tail overlay (no index write on the read path).
54
+
-**Byte-based, one buffer type**: `addr`/`len` are byte offsets/lengths (block-aligned to `lba_size`); `sisl::sg_list` is the single caller-owned buffer both ways (an **empty** write buffer is a zero write). A read fills the caller's buffer in place and returns a sparse `io_extent` layout (data vs holes).
51
55
-**Client-routed reads**: reads are unicast, chosen by LBA-overlap against the client's per-replica Missing map (plus `Synced ≥ L`, the login dLSN). The read path never fetches from a peer; fetch is resync-only.
52
56
-**Merge key, not serialization**: overlapping writes need no ordering; highest dLSN per LBA wins on every replica.
53
57
-**Thin from the start**: a write may be `all_zeros` (WRITE_ZEROES) with no payload; reads return sparse results (data extents + holes), and the server collapses all-zero reads to holes, so reads and resync stay thin. A hole is not `Missing`.
0 commit comments