Pure OCaml B-tree implementation for persistent storage
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(btree,sqlite): add in-memory pager for ~60x faster fuzz tests

Add Pager.mem for purely in-memory B-tree storage (no file backing)
and Sqlite.in_memory constructor. Fuzz tests now run in 0.5s vs ~30s
by eliminating per-iteration Eio_main.run and file I/O overhead.

Also syncs ocaml-claude-skills upstream changes (plugin restructure)
and adds fuzz skill with updated Crowbar API patterns.

+40 -17
+5 -1
lib/btree.mli
··· 182 182 type t 183 183 184 184 val v : page_size:int -> Eio.File.rw_ty Eio.Resource.t -> t 185 - (** [v ~page_size file] creates a pager with the given page size. *) 185 + (** [v ~page_size file] creates a pager backed by [file]. *) 186 + 187 + val mem : page_size:int -> unit -> t 188 + (** [mem ~page_size ()] creates a purely in-memory pager with no file backing. 189 + All pages are stored in memory only. [sync] is a no-op. *) 186 190 187 191 val page_size : t -> int 188 192 (** [page_size t] returns the page size. *)
+30 -15
lib/pager.ml
··· 6 6 (** Page cache and file I/O for B-tree storage. *) 7 7 8 8 type t = { 9 - file : Eio.File.rw_ty Eio.Resource.t; 9 + file : Eio.File.rw_ty Eio.Resource.t option; 10 10 page_size : int; 11 11 mutable page_count : int; 12 12 cache : (int, string) Hashtbl.t; ··· 18 18 let file_size = Optint.Int63.to_int stat.size in 19 19 let page_count = if file_size = 0 then 0 else file_size / page_size in 20 20 { 21 - file; 21 + file = Some file; 22 22 page_size; 23 23 page_count; 24 24 cache = Hashtbl.create 64; 25 25 dirty = Hashtbl.create 16; 26 26 } 27 27 28 + let mem ~page_size () = 29 + { 30 + file = None; 31 + page_size; 32 + page_count = 0; 33 + cache = Hashtbl.create 64; 34 + dirty = Hashtbl.create 16; 35 + } 36 + 28 37 let page_size t = t.page_size 29 38 let page_count t = t.page_count 30 39 ··· 36 45 | None -> ( 37 46 match Hashtbl.find_opt t.cache page_num with 38 47 | Some data -> data 39 - | None -> 40 - let offset = Optint.Int63.of_int ((page_num - 1) * t.page_size) in 41 - let buf = Cstruct.create t.page_size in 42 - Eio.File.pread_exact t.file ~file_offset:offset [ buf ]; 43 - let data = Cstruct.to_string buf in 44 - Hashtbl.replace t.cache page_num data; 45 - data) 48 + | None -> ( 49 + match t.file with 50 + | None -> Fmt.failwith "Page %d not in memory" page_num 51 + | Some file -> 52 + let offset = Optint.Int63.of_int ((page_num - 1) * t.page_size) in 53 + let buf = Cstruct.create t.page_size in 54 + Eio.File.pread_exact file ~file_offset:offset [ buf ]; 55 + let data = Cstruct.to_string buf in 56 + Hashtbl.replace t.cache page_num data; 57 + data)) 46 58 47 59 let write t page_num data = 48 60 if String.length data <> t.page_size then failwith "Invalid page size"; ··· 57 69 t.page_count 58 70 59 71 let sync t = 60 - Hashtbl.iter 61 - (fun page_num data -> 62 - let offset = Optint.Int63.of_int ((page_num - 1) * t.page_size) in 63 - let buf = Cstruct.of_string data in 64 - Eio.File.pwrite_all t.file ~file_offset:offset [ buf ]) 65 - t.dirty; 72 + (match t.file with 73 + | None -> () 74 + | Some file -> 75 + Hashtbl.iter 76 + (fun page_num data -> 77 + let offset = Optint.Int63.of_int ((page_num - 1) * t.page_size) in 78 + let buf = Cstruct.of_string data in 79 + Eio.File.pwrite_all file ~file_offset:offset [ buf ]) 80 + t.dirty); 66 81 Hashtbl.clear t.dirty
+5 -1
lib/pager.mli
··· 9 9 (** A pager manages page-level I/O with caching. *) 10 10 11 11 val v : page_size:int -> Eio.File.rw_ty Eio.Resource.t -> t 12 - (** [v ~page_size file] creates a pager with the given page size. *) 12 + (** [v ~page_size file] creates a pager backed by [file]. *) 13 + 14 + val mem : page_size:int -> unit -> t 15 + (** [mem ~page_size ()] creates a purely in-memory pager with no file backing. 16 + All pages are stored in memory only. [sync] is a no-op. *) 13 17 14 18 val page_size : t -> int 15 19 (** [page_size t] returns the page size. *)