pub struct FileBackend { /* private fields */ }Expand description
Filesystem-backed StorageBackend.
One file per key under dir. Concurrent writers are safe at the
per-key granularity (atomic rename via tempfile); concurrent writers
to the SAME key race in unspecified-but-atomic fashion (last commit wins).
§Filesystem portability (B2 — 2026-05-22, /porting-to-rs)
Key→filename encoding preserves ASCII case: Foo and foo encode to
Foo.bin and foo.bin. On case-insensitive filesystems (default macOS
APFS, default Windows NTFS) these collide silently — last write wins.
To surface this loudly rather than corrupting data, FileBackend probes
the filesystem on first write() and rejects subsequent writes whose
encoded filename differs from a previously-written key only in casing.
The probe is per-instance and runs at most once.
- Case-sensitive filesystems (Linux ext4/tmpfs, macOS APFS configured
case-sensitive at format time): no enforcement; both
Fooandfoosucceed and resolve to distinct files. - Case-insensitive filesystems (default macOS APFS, Windows NTFS):
second of
Foo/foofails withStorageError::BackendErrorwhose message names both the existing and would-collide keys for diagnosis. - Read / list / delete paths are zero-overhead — the probe runs only on
write, since collisions are write-introduced.
Tests force the probe outcome via
[FileBackend::with_case_insensitive] so they’re FS-independent.
§Example
use std::sync::Arc;
use graphrefly_storage::{file_backend, snapshot_storage, SnapshotStorageOptions};
let backend = file_backend("./checkpoints");
let tier = snapshot_storage(backend, SnapshotStorageOptions::<MyState, _>::default());
tier.save(state).unwrap();Implementations§
Source§impl FileBackend
impl FileBackend
Sourcepub fn new(dir: impl AsRef<Path>) -> Self
pub fn new(dir: impl AsRef<Path>) -> Self
Construct a backend rooted at dir. The directory is created lazily on
first write() — read / list / delete tolerate its absence.
Override whether list() includes filenames beginning with . (D161).
Default false: hidden filenames are skipped. This protects against
in-flight tempfile::NamedTempFile temp files (which are created with
a leading-. prefix) leaking into enumeration results during a
concurrent flush.
Pass true if your application intentionally writes keys whose
percent-encoding produces a leading-. filename and you need them
visible in list().
Whether list() includes dot-prefixed filenames.
Trait Implementations§
Source§impl Debug for FileBackend
impl Debug for FileBackend
Source§impl StorageBackend for FileBackend
impl StorageBackend for FileBackend
Source§fn name(&self) -> &str
fn name(&self) -> &str
"memory", "file:./checkpoints"). Surfaces
in error messages and tier Display impls.Source§fn read(&self, key: &str) -> Result<Option<Vec<u8>>, StorageError>
fn read(&self, key: &str) -> Result<Option<Vec<u8>>, StorageError>
Ok(None) on miss.Source§fn delete(&self, key: &str) -> Result<(), StorageError>
fn delete(&self, key: &str) -> Result<(), StorageError>
Source§fn list(&self, prefix: &str) -> Result<Vec<String>, StorageError>
fn list(&self, prefix: &str) -> Result<Vec<String>, StorageError>
prefix (lex-ASC). Empty prefix enumerates
all keys. Default returns BackendNoListSupport — backends that don’t
support enumeration surface the diagnostic here at first call, NOT at
attach (mirrors TS lazy-throw semantics for list_by_prefix).