class

AptLarder::Cache

Inherits Reference / Object

Filesystem-backed cache for APT packages and index files.

Each entry is stored as a plain file under root using the cache key as a relative path. A .sha256 sidecar is written alongside every file so that immutable entries can be verified on first serve without trusting the filesystem alone.

Three in-memory indices avoid redundant syscalls at runtime:

  • @known — keys confirmed present on disk (never shrinks while running)
  • @mtime_cache — last-modified times, updated by store and touch
  • @verified — keys whose SHA256 has already been checked this session

All public methods are safe to call from concurrent fibers.

Constants

Log = ::Log.for("apt-larder.cache")

Constructors

new(root : String)
Source

Instance methods

clear

Removes every cached entry (data files and their .sha256 sidecars) in a single directory scan and clears all in-memory state. Returns the number of data files deleted.

Unlike iterating entries + invalidate, this builds no per-entry structs and holds the whole listing only as a glob stream, so it stays cheap on very large caches. Best-effort under concurrency: entries added while the scan runs may survive (acceptable for an admin flush).

Source
entries(prefix : String = "", page : Int32 = 1, per_page : Int32 = 50) : NamedTuple(entries: Array(EntryInfo), total: Int32)

Returns paginated cache entries, optionally filtered by prefix. Scans the root directory on each call — intended for infrequent admin use.

Source
entry_count

Returns the number of cached data files.

Fast (no disk scan): maintained incrementally by store/invalidate/ clear, seeded from disk at construction, and re-anchored to the exact on-disk count on every evict pass. May drift slightly between eviction passes if files are added or removed out-of-band.

Source
evict(max_age : Time::Span | Nil = nil, limit_bytes : Int64 | Nil = nil) : Tuple(Int32, Int64)

Deletes every cached file whose mtime is older than max_age.

Skips .sha256 sidecar files (they are removed together with their parent by invalidate). Returns {files_deleted, bytes_freed}. Performs time-based and/or size-based eviction in a single disk scan.

  • max_age — delete files whose mtime is older than this span (nil = skip)
  • limit_bytes — delete LRU files until total size is below this limit (nil = skip)

Returns {files_deleted, bytes_freed}.

Source
evict_stale(max_age : Time::Span) : Tuple(Int32, Int64)

Convenience wrapper: time-based eviction only.

Source
evict_to_limit(limit_bytes : Int64) : Tuple(Int32, Int64)

Convenience wrapper: size-based LRU eviction only.

Source
exists?(key : String) : Bool

Returns true if a file for key exists on disk.

Checks the in-memory @known set first to avoid a stat syscall on repeated lookups. Adds the key to @known on the first disk hit.

Source
fresh?(key : String, ttl : Time::Span) : Bool

Returns true if the cached file for key is younger than ttl.

Uses the in-memory mtime index when available, falling back to a single stat call on first access. Returns false if the key is not cached.

Source
invalidate(key : String) : Nil

Removes the file and its .sha256 sidecar from disk and clears all in-memory state for key. Safe to call when the file does not exist.

Source
modification_time?(key : String) : Time | Nil

Returns the modification time of the cached file, or nil if the file does not exist or disappears between the call and the stat (TOCTOU safe).

Source
open(key : String) : File

Opens the cached file for key in read-only mode. Raises File::Error if the key is not present.

Source
size(key : String) : Int64

Returns the byte size of the cached file for key. Raises File::Error if the key is not present.

Source
store(key : String, io : IO) : Nil

Streams io into the cache under key.

Writes to a randomly-named .tmp file first, then renames it into place atomically. The SHA256 of the content is computed during the write at no extra I/O cost and stored in a .sha256 sidecar file.

If the write fails for any reason (network drop, disk full, …) the .tmp file is deleted and the exception is re-raised. The destination is never left in a partially-written state.

Source
touch(key : String) : Nil

Updates the mtime of key to the current time and refreshes the in-memory index. Used to extend the freshness window after a 304 Not Modified response. Silently ignores missing files.

Source
valid?(key : String) : Bool

Returns true if the cached file matches its stored SHA256 sidecar.

Files that have no sidecar (written before integrity tracking was added) are trusted unconditionally. Each file is hashed at most once per server run: after the first successful check the key is added to @verified and subsequent calls return immediately.

Source
verified?(key : String) : Bool

Returns true if key's SHA256 has already been verified this session. O(1) and does no disk I/O: used as a fast pre-check before the single-flight so the expensive valid? hashing runs at most once per key.

Source

Nested types