Table of Contents

Class DiskCacheBudget

Namespace
EncDotNet.S100.Core.Caching
Assembly
EncDotNet.S100.Core.dll

Byte-budget and least-recently-used bookkeeping for a directory of cache files, plus the atomic read/write primitives every disk cache shares.

public sealed class DiskCacheBudget
Inheritance
DiskCacheBudget
Inherited Members

Remarks

The budget keeps an in-memory index of the entries under Directory (path, size, recency) and a running total. The directory is scanned once, lazily, on the first write. After that, writes and reads update the index in memory, and eviction picks victims from it without enumerating or stat-ing the directory. Cost per write is therefore independent of how many files the cache holds.

The index can drift when something else changes the directory (a host "clear caches" sweep, or another process sharing the cache). It is reconciled with a rescan when an eviction finds its victim already gone, and otherwise at most once per reconcile interval. Between rescans the cap is soft: another process's writes can push the directory past it.

Recency is an in-process sequence number, so eviction order is exact even when several entries are written within the file system's timestamp resolution. Entries found by a scan are ordered by their last-access time and rank older than anything this process has written or read. Reads and writes stamp the file's last-access time, so the order survives a restart even on noatime mounts.

All members are thread-safe. Reads never take the lock; a write takes it only to update the index and, when over budget, to evict.

Constructors

DiskCacheBudget(string, string, long, bool, TimeSpan?)

Creates a budget over the fileExtension files under directory. Nothing is read from disk until the first TryWrite(string, ReadOnlySpan<byte>, Func<bool>?) or RecordWrite(string, long).

public DiskCacheBudget(string directory, string fileExtension, long maxBytes, bool recursive = false, TimeSpan? reconcileInterval = null)

Parameters

directory string

Cache directory. Assumed private to the cache: every fileExtension file under it counts toward the budget and may be evicted, and stale *.tmp files are removed.

fileExtension string

Extension of entry files, including the leading dot (for example .dlist).

maxBytes long

Soft upper bound on the total size of entry files. Must be positive.

recursive bool

Whether entries live in subdirectories of directory.

reconcileInterval TimeSpan?

Minimum time between reconciling rescans; null uses DefaultReconcileInterval.

Exceptions

ArgumentException

directory or fileExtension is null or empty.

ArgumentOutOfRangeException

maxBytes is not positive.

Fields

DefaultReconcileInterval

Default interval between reconciling rescans of the directory.

public static readonly TimeSpan DefaultReconcileInterval

Field Value

TimeSpan

Properties

Count

The number of indexed entry files. Zero until the first write loads the index.

public int Count { get; }

Property Value

int

Directory

The cache directory this budget governs.

public string Directory { get; }

Property Value

string

MaxBytes

Soft upper bound, in bytes, on the total size of entry files.

public long MaxBytes { get; }

Property Value

long

TotalBytes

The indexed total size of entry files, in bytes. Zero until the first write loads the index.

public long TotalBytes { get; }

Property Value

long

Methods

EnsureIndexed()

Loads the index from disk if no write has loaded it yet, so TotalBytes and Count report what the directory holds rather than zero.

public void EnsureIndexed()

RecordAccess(string)

Marks the indexed entry at path most recently used. A path the index does not hold yet is ignored; it is picked up by the next scan. Lock-free.

public void RecordAccess(string path)

Parameters

path string

Full path of the entry file.

RecordWrite(string, long)

Records that the entry at path now holds length bytes and is the most recently used, then evicts least-recently-used entries until the total is within MaxBytes. The first call loads the index from disk.

public void RecordWrite(string path, long length)

Parameters

path string

Full path of the entry file that was just written.

length long

Size of the entry file in bytes.

TouchAccessTime(string)

Sets a file's last-access time to now, so a later scan (after a restart) orders it as recently used without relying on file-system access-time tracking. Never throws.

public static void TouchAccessTime(string path)

Parameters

path string

The file to stamp.

TryDelete(string)

Deletes a file if present, ignoring any IO error.

public static void TryDelete(string path)

Parameters

path string

The file to delete.

TryRead(string)

Reads the entry at path and marks it most recently used, or returns null when it is absent or unreadable. Never throws for IO errors.

public byte[]? TryRead(string path)

Parameters

path string

Full path of the entry file.

Returns

byte[]

TryWrite(string, ReadOnlySpan<byte>, Func<bool>?)

Atomically writes bytes as the entry at path (temp file in the same directory, then rename), records it, and evicts least-recently-used entries if the budget is exceeded. Never throws for IO errors.

public bool TryWrite(string path, ReadOnlySpan<byte> bytes, Func<bool>? beforeCommit = null)

Parameters

path string

Full path of the entry file; its directory is created if absent.

bytes ReadOnlySpan<byte>

The entry content.

beforeCommit Func<bool>

Optional check run after the temp file is written and before it is renamed into place; returning false abandons the write.

Returns

bool

true when the entry was committed.