FilesystemObjectStorageOptions
Defined in: src/persistence/object-storage/FilesystemObjectStorageBackend.ts:57
Filesystem-backed ObjectStorageBackend — stores each object as a file
under a root directory, with the storage key mapped 1:1 to a relative
path. Suitable for unit tests, local development, and “S3-API parity
without the cloud”, and safe for concurrent multi-process writers:
every put / delete acquires a per-key advisory file lock (atomic
O_EXCL create) so the CAS check + write block is serialized at the
filesystem layer, and writes use a temp-file + rename so concurrent
readers never observe a half-written object.
The backend lazy-imports node:fs/promises and node:path so this
module is harmless to include on Bun / Deno where those built-ins
already exist (Node-compat layer) — only the actual operations touch
them.
Implementation notes:
- Disk is canonical. Etags are content-derived (computeEtag — deterministic FNV-1a + length). No in-memory map; the file content alone determines the etag, so a fresh process sees the exact same etags every other process does. Same key, same bytes → same etag, regardless of who wrote them or when.
- Per-key advisory lock. The lock file lives next to the target
file as
<key>.lock. Acquisition usesfs.writeFile(lockPath, ..., { flag: 'wx' }), which is atomic-create-only on every POSIX and NTFS filesystem the framework targets. - Stale-lock recovery. Lock files older than staleLockMs
(default 30 s) are assumed to be left behind by a crashed writer
and forcibly removed; one final acquisition retry is then made.
This keeps the pathological “process died holding a lock” case from
blocking the directory forever, at the cost of being technically
incorrect if a real writer is taking longer than
staleLockMsfor a singleput— which shouldn’t happen for the small payloads this backend targets. - Atomic body writes.
putwrites to a per-process tmp file (<key>.tmp.<pid>.<ts>.<rand>), then renames over the target. On POSIXrename(2)is atomic on the same filesystem; on WindowsMoveFileEx(MOVEFILE_REPLACE_EXISTING)provides equivalent behaviour. Concurrent readers always see either the old body or the new body, never a truncated buffer.
Properties
Section titled “Properties”
readonlydir:string
Defined in: src/persistence/object-storage/FilesystemObjectStorageBackend.ts:59
Root directory. Will be created (recursively) if it doesn’t exist.
lockTimeoutMs?
Section titled “lockTimeoutMs?”
readonlyoptionallockTimeoutMs?:number
Defined in: src/persistence/object-storage/FilesystemObjectStorageBackend.ts:66
How long to wait when contending for a per-key write lock before
giving up with ObjectStorageBackendError. Default 5_000 ms — long
enough that legitimate contenders complete first, short enough that
a stuck holder gets surfaced quickly.
staleLockMs?
Section titled “staleLockMs?”
readonlyoptionalstaleLockMs?:number
Defined in: src/persistence/object-storage/FilesystemObjectStorageBackend.ts:73
Lock files older than this are assumed stale (left behind by a
crashed writer) and forcibly removed. Default 30_000 ms — well
above the expected duration of any single put, so legitimate
writers never get their lock yanked.