My agentic slop goes here. Not intended for anyone else!
1(** File-backed persistent cache with streaming API and TTL support
2
3 Cacheio provides a robust file-backed caching system designed for high-performance
4 applications that need persistent storage with automatic expiry management.
5
6 {2 Key Features}
7
8 - {b SHA256-based key hashing}: Uniform distribution across subdirectories
9 - {b Maildir++-style naming}: Atomic operations with flag and TTL encoding
10 - {b Streaming API}: Built on Eio.Flow for efficient, non-blocking I/O
11 - {b Automatic expiry}: TTL-based cache expiration with manual control
12 - {b Thread-safe}: All operations are safe for concurrent access
13 - {b Chunk support}: Resume partial downloads with range-based chunks
14 - {b XDG integration}: Automatic directory management via XDG Base Directory spec
15
16 {2 Usage Example}
17
18 {[
19 let cache = Cacheio.create ~base_dir:cache_dir in
20
21 (* Store data with 1 hour TTL *)
22 Eio.Switch.run @@ fun sw ->
23 let source = Eio.Flow.string_source "Hello, World!" in
24 Cacheio.put cache ~key:"greeting" ~source ~ttl:(Some 3600.0) ();
25
26 (* Retrieve data *)
27 Eio.Switch.run @@ fun sw ->
28 match Cacheio.get cache ~key:"greeting" ~sw with
29 | Some source ->
30 let content = Eio.Buf_read.(parse_exn take_all) source ~max_size:Int.max_int in
31 print_endline content
32 | None -> print_endline "Not found"
33 ]}*)
34
35(** {1 Core Operations} *)
36
37(** Abstract type representing a cache instance *)
38type t
39
40(** Create a new cache at the specified base directory *)
41val create : base_dir:Eio.Fs.dir_ty Eio.Path.t -> t
42
43(** Create a new cache using an Xdge context *)
44val create_with_xdge : Xdge.t -> t
45
46(** Check if a key exists in the cache.
47 Keys are strings that will be automatically hashed for filesystem storage. *)
48val exists : t -> key:string -> bool
49
50(** Get a stream source for reading cached data *)
51val get : t -> key:string -> sw:Eio.Switch.t -> Eio.Flow.source_ty Eio.Resource.t option
52
53(** Cache flags management *)
54module Flags = Flags
55
56(** Store data from a stream source with optional TTL and flags *)
57val put : t -> key:string -> source:Eio.Flow.source_ty Eio.Resource.t ->
58 ?ttl:float option -> ?flags:Flags.t -> unit -> unit
59
60(** Delete a cache entry *)
61val delete : t -> key:string -> unit
62
63(** Get the size of a cache entry in bytes *)
64val size : t -> key:string -> int64 option
65
66(** Get the path to a cache entry (for direct file access if needed) *)
67val get_path : t -> key:string -> Eio.Fs.dir_ty Eio.Path.t option
68
69(** {1 Flag Management} *)
70
71(** Add a flag to an entry *)
72val add_flag : t -> key:string -> Flags.flag -> unit
73
74(** Remove a flag from an entry *)
75val remove_flag : t -> key:string -> Flags.flag -> unit
76
77(** Set specific flags on an entry (replaces all existing) *)
78val set_flags : t -> key:string -> Flags.t -> unit
79
80(** Get current flags for an entry *)
81val get_flags : t -> key:string -> Flags.t option
82
83(** {1 Cache Management} *)
84
85(** Cache entry information *)
86module Entry = Entry
87
88(** Cache statistics *)
89module Stats = Stats
90
91(** Scan the cache and return all entries *)
92val scan : t -> Entry.t list
93
94(** Get cache statistics *)
95val stats : t -> Stats.t
96
97(** Remove expired entries and return count of removed entries *)
98val expire : t -> int
99
100(** Clear all entries (except pinned ones) *)
101val clear : t -> unit
102
103(** Clear all temporary entries *)
104val clear_temporary : t -> unit
105
106(** {1 Chunk Support for Partial Downloads} *)
107
108(** Range management for partial downloads *)
109module Range = Range
110
111(** Chunk file management *)
112module Chunk = Chunk
113
114(** Store a chunk of data with a specific byte range *)
115val put_chunk : t -> key:string -> range:Range.t ->
116 source:Eio.Flow.source_ty Eio.Resource.t ->
117 ?ttl:float option -> ?flags:Flags.t -> unit -> unit
118
119(** Get data for a specific range, returns either complete file or chunks *)
120val get_range : t -> key:string -> range:Range.t -> sw:Eio.Switch.t ->
121 [ `Complete of Eio.Flow.source_ty Eio.Resource.t
122 | `Chunks of (Range.t * Eio.Flow.source_ty Eio.Resource.t) list
123 | `Not_found ]
124
125(** List all chunks for a key *)
126val list_chunks : t -> key:string -> Chunk.t list
127
128(** Check if chunks form a complete sequence *)
129val has_complete_chunks : t -> key:string -> total_size:int64 -> bool
130
131(** Get missing byte ranges for incomplete downloads *)
132val missing_ranges : t -> key:string -> total_size:int64 -> Range.t list
133
134(** Coalesce chunks into a complete file asynchronously *)
135val coalesce_chunks : t -> key:string -> ?verify:bool -> ?flags:Flags.t ->
136 ?ttl:float option -> unit -> unit Eio.Promise.or_exn
137
138(** Clean up all chunks for a key *)
139val cleanup_chunks : t -> key:string -> unit
140
141(** {1 Pretty Printing} *)
142
143(** Pretty printer for cache *)
144val pp : Format.formatter -> t -> unit