Skip to main content

Module file

Module file 

Source
Expand description

Local filesystem backend, byte-compatible with cachekit-py’s File backend. Local filesystem backend, byte-compatible with cachekit-py’s File backend.

On-disk layout (shared cross-SDK contract — a py and an rs process pointed at the same directory read each other’s entries):

  • Filename: lowercase-hex Blake2b-128 (16-byte digest) of the UTF-8 cache key — filesystem-safe, no directory traversal.
  • Header (14 bytes): b"CK" magic [0:2], format version 1 [2:3], reserved 0 [3:4], flags u16 BE [4:6], expiry unix-seconds u64 BE [6:14] (0 = never expires). Payload follows the header.
  • Writes are atomic: payload lands in a unique {name}.tmp.{pid}.… file, fsynced, then renamed over the final path. Readers see the old or the new entry, never a torn one.
  • Expiry is lazy: expired and corrupt entries are unlinked when a read touches them, exactly like cachekit-py.

§Concurrency model (py parity)

  • In-process: every operation serializes on a backend-wide mutex — the equivalent of cachekit-py’s threading.RLock. This closes the expire-unlink vs. concurrent-set lost-write race within a process. (ponytail: one backend-wide lock, matching py; shard per-key if contention ever matters.)
  • Cross-process (unix): advisory flock — shared on reads, exclusive on the in-place expiry rewrite — matching py’s fcntl.flock usage, non-blocking with contention surfacing as a timeout error like py. Unlinks are additionally inode-validated: an entry is only removed if the path still names the inode the decision was made on. Combined with the in-process lock this fully closes the stale-unlink race within a process; cross-process it narrows the exposure to the stat-to-unlink instruction window (a rename landing exactly there loses one fresh entry — a cache miss, never corruption — the same class py accepts).
  • Remaining accepted exposure, identical to py: refresh_ttl racing a concurrent replace in another process returns false (refresh what was opened is no longer the live entry), and an in-place expiry rewrite torn by power loss yields a wrong expiry, never a corrupt payload.

Deliberate v1 divergences from cachekit-py (documented, not silent): LRU eviction / size caps / entry-count caps and the mmap zero-copy read path are not implemented — the directory grows until entries expire or the caller clears it.

Requires a tokio runtime: file I/O runs on the blocking thread pool via spawn_blocking (except under the unsync feature, where it runs inline on the caller — the sync-in-async trade-off cachekit-py documents).

Structs§

FileBackend
Local filesystem cache backend.
FileBackendBuilder
Builder for FileBackend.