The file:// variant store: a directory tree under the configured root.
This is the store for a deployment with no object storage at all — renders
of local:// (or any other) sources cached on a disk the operator mounted.
It is a single-node choice by design: two nodes with separate roots each
render a variant once, and that is accepted rather than worked around.
Shared caches want the s3:// backend.
Layout
Keys are cache keys — 64 lowercase hex characters (AudioProxy.CacheKey) —
and fan out by prefix so one directory never accumulates every variant:
<root>/ab/cd/abcd… the bytes
<root>/ab/cd/abcd….meta the metadata, as JSON
<root>/tmp/ in-flight writes, swept at bootAnything that is not a well-formed cache key is refused before it can name a path, so a key cannot traverse out of the root.
Atomicity from rename-within-a-filesystem
put_stream/3 stages both files under <root>/tmp — inside the store,
because a cross-device rename is a copy and not atomic — and commits with
File.rename/2, sidecar first. The data file is the commit point:
head/1 requires both files, so a crash between the two renames leaves a
sidecar nobody reads rather than bytes served with no metadata, and a
write that never completes leaves nothing readable at all.
Streamed both ways
Reads and writes move chunk by chunk in raw mode — never a whole variant in memory, which is what lets this back full-length transcodes and not just previews. Range slices are positioned reads over the same chunking.
Summary
Functions
Removes leftover staging files under <root>/tmp.
Functions
@spec sweep_staging(Path.t()) :: :ok
Removes leftover staging files under <root>/tmp.
Every in-process failure cleans its own staging, but a write killed
outright — a VM crash, a shutdown that outran a mid-commit tee — leaves its
temp files behind, and nothing else ever looks at them again. Called once
from AudioProxy.Application.start/2, which is safe precisely because a
file:// store is single-node by design: at boot, no writer of this store
can be staging.