You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
atelet: node-local cache for golden snapshots (OnGolden data resumes re-download the golden every time) #690
Follow-up from the #683 review (#683): with onResume.fromData: Golden, every resume of a Data snapshot downloads the template's golden snapshot from object storage — including un-pause, which is meant to be the fast, node-local path. The golden's memory image is the dominant transfer, so this adds significant latency per un-pause. The pre-existing golden-fallback boot (fresh actor, no snapshot) has the same characteristic today.
Proposal
Cache golden snapshot files node-locally in atelet, content-addressed by the golden snapshot URI prefix (a golden snapshot is immutable once written, and one golden serves every actor of the template on the node):
On a DATA_ON_GOLDEN restore (or golden-fallback boot), download the golden's files once into a shared cache dir (alongside the existing static-files/sandbox-asset cache), then hardlink or copy them into the per-actor restore dir.
Subsequent restores on the node skip the download entirely.
Eviction: goldens are per-template-version; an LRU or size cap over the cache dir handles template churn. Invalidation is not needed for a given URI (immutable), only cleanup of unused goldens.
Care points: the restore dir must remain private per actor (cloud-hypervisor demand-pages from it for the VM's lifetime, so hardlinks must not allow cross-actor mutation; and the cache write must be atomic (temp + rename) like the asset cache.
Design proposal: filecache — a generic node-local artifact cache in atelet
Full design written up as docs/dev/filecache-design.md (PR to follow); summary below. It covers this issue's golden-snapshot case and, in a later milestone, folds the sandbox-asset cache (fetchAsset/fetchGVisorRelease) onto the same library — that path has the same check-then-download race and never deletes anything.
Shape
A small library at cmd/atelet/internal/filecache, porting the mechanics internal/imagecache already proved for OCI layers (singleflight dedup, mtime-LRU, two-phase retire) to a generic key→file cache. Two independent store instances: a golden store and (M3) an assets store, each with its own root and byte budget.
func (s*Store) GetFileTo(ctx, keyKey, dststring, fetchFileFetcher) error// golden filesfunc (s*Store) GetFile(ctx, keyKey, fetchFileFetcher) (Entry, error) // in-place single filesfunc (s*Store) GetDir(ctx, keyKey, fetchDirFetcher) (Entry, error) // extracted trees (gVisor release)
Fetchers are injected (the store owns dedup/publication/GC; download policy — zstd+sparse for goldens via ategcs.FetchLocalFileFromGCSWithZstd, sha256+auth-fallback for assets — stays at the call site). Keys are identities, not addresses: URIKey(goldenURI, file) for goldens, SHA256Key(hex) for content-addressed assets.
Golden restore path (this issue)
GetFileTo is atomic get-and-link:
Miss: concurrent callers for one key join a single flight (singleflight.DoChan); the download runs on context.WithoutCancel + timeout, so one canceled restore never kills the download other resumes are waiting on. Fetch lands in tmp/, is chmod'd 0444, and is atomically renamed into the cache — crash-safe, never a partial file.
Hit: stat + hardlink into the actor's restore dir + mtime touch. N concurrent DATA_ON_GOLDEN resumes (or golden-fallback boots) of one golden = one download; every subsequent restore on the node is link-time.
The hardlink answers the demand-paging care point from the issue description directly: the actor's link shares the inode, so even if GC evicts the cache entry mid-VM-lifetime, cloud-hypervisor's mapping stays valid until the actor dir is wiped. Eviction can only ever cost a re-download, never break a running actor. Cross-actor mutation is excluded by design assumption (restore files are consumed read-only) and enforced mechanically: cache entries are published 0444, so an in-place write fails with EACCES instead of poisoning the shared copy.
GC
Pressure-driven only — age alone never removes a file. Per-store budget (--golden-cache-max-bytes, absolute or % of disk capacity, resolved via statfs at startup), LRU by entry mtime, a per-store min-age guard for the just-published window, and preference for entries with Nlink == 1 (evicting a still-linked entry frees nothing until the actor dir is wiped). Removal is two-phase like imagecache's retire: rename to .rm-* inside the key's singleflight (an in-flight fetch or fresh hit vetoes), bulk RemoveAll outside all locks. Startup sweep reaps tmp/* and .rm-* crash debris. The assets store (M3) additionally gets a root set recomputed each pass from the on-disk actor sandbox records, mirroring imagecache's InUse().
Design assumptions (stated, not open)
Golden objects are immutable under their URI — matches "immutable once written" in the issue; changed goldens must get new URIs, no generation suffix in the key.
Restore consumes checkpoint files read-only — enforced by the 0444 tripwire.
M2 — golden integration in Restore (downloadExternalCheckpoint/downloadCombinedCheckpoint golden halves, incl. the LOCAL-pause un-pause path, plus the golden manifest.json fetch) + GC driver loop. Exit criterion: two concurrent resumes from one golden on a node → one download.
M3 — GetFile/GetDir + root set; migrate sandbox assets and prewarm onto the assets store.
M4 — (conditional, metrics-gated) one shared GC coordinator owning the node-wide disk signal across golden/assets/image caches; v1 deliberately reacts only to per-store budgets.
M5 — metrics registry entries, e2e under eviction pressure.
Closing: what this issue asked for, no re-download of a shared snapshot on a node, is on main, under today's names (tags, sharing) rather than the DATA_ON_GOLDEN the issue was written against, which #2105 removed.
Not part of this issue, tracked separately: M4, one eviction coordinator over the node's disk, is #2987, open for discussion and not committed to. Two performance follow-ups that came out of #2796's review are #2948 (links instead of copies for micro-VM restores) and #2974 (keeping shared snapshot images resident outside actor cgroups).
Activity
dberkov commentedon Sep 4, 2026
Design proposal:
filecache— a generic node-local artifact cache in ateletFull design written up as
docs/dev/filecache-design.md(PR to follow); summary below. It covers this issue's golden-snapshot case and, in a later milestone, folds the sandbox-asset cache (fetchAsset/fetchGVisorRelease) onto the same library — that path has the same check-then-download race and never deletes anything.Shape
A small library at
cmd/atelet/internal/filecache, porting the mechanicsinternal/imagecachealready proved for OCI layers (singleflight dedup, mtime-LRU, two-phase retire) to a generic key→file cache. Two independent store instances: a golden store and (M3) an assets store, each with its own root and byte budget.Fetchers are injected (the store owns dedup/publication/GC; download policy — zstd+sparse for goldens via
ategcs.FetchLocalFileFromGCSWithZstd, sha256+auth-fallback for assets — stays at the call site). Keys are identities, not addresses:URIKey(goldenURI, file)for goldens,SHA256Key(hex)for content-addressed assets.Golden restore path (this issue)
GetFileTois atomic get-and-link:singleflight.DoChan); the download runs oncontext.WithoutCancel+ timeout, so one canceled restore never kills the download other resumes are waiting on. Fetch lands intmp/, is chmod'd0444, and is atomically renamed into the cache — crash-safe, never a partial file.DATA_ON_GOLDENresumes (or golden-fallback boots) of one golden = one download; every subsequent restore on the node is link-time.The hardlink answers the demand-paging care point from the issue description directly: the actor's link shares the inode, so even if GC evicts the cache entry mid-VM-lifetime, cloud-hypervisor's mapping stays valid until the actor dir is wiped. Eviction can only ever cost a re-download, never break a running actor. Cross-actor mutation is excluded by design assumption (restore files are consumed read-only) and enforced mechanically: cache entries are published
0444, so an in-place write fails withEACCESinstead of poisoning the shared copy.GC
Pressure-driven only — age alone never removes a file. Per-store budget (
--golden-cache-max-bytes, absolute or%of disk capacity, resolved via statfs at startup), LRU by entry mtime, a per-store min-age guard for the just-published window, and preference for entries withNlink == 1(evicting a still-linked entry frees nothing until the actor dir is wiped). Removal is two-phase like imagecache's retire: rename to.rm-*inside the key's singleflight (an in-flight fetch or fresh hit vetoes), bulkRemoveAlloutside all locks. Startup sweep reapstmp/*and.rm-*crash debris. The assets store (M3) additionally gets a root set recomputed each pass from the on-disk actor sandbox records, mirroring imagecache'sInUse().Design assumptions (stated, not open)
0444tripwire.Milestones
GetFileTo+ GC core + concurrency/crash tests.downloadExternalCheckpoint/downloadCombinedCheckpointgolden halves, incl. the LOCAL-pause un-pause path, plus the goldenmanifest.jsonfetch) + GC driver loop. Exit criterion: two concurrent resumes from one golden on a node → one download.GetFile/GetDir+ root set; migrate sandbox assets and prewarm onto the assets store.🤖 Generated with Claude Code
atelet: node-local file cache library (filecache, M1) (#1517)
ateletpb: carry the Restore base snapshot as a typed source, dropping…
atelet: serve shared snapshot restores from the node-local file cache (…
dberkov commentedon Oct 20, 2026
Closing: what this issue asked for, no re-download of a shared snapshot on a node, is on
main, under today's names (tags,sharing) rather than theDATA_ON_GOLDENthe issue was written against, which #2105 removed.Milestones from the design comment, as landed:
filecachelibrary with link-out and the eviction core: atelet: node-local file cache library (filecache, M1) #1517.Not part of this issue, tracked separately: M4, one eviction coordinator over the node's disk, is #2987, open for discussion and not committed to. Two performance follow-ups that came out of #2796's review are #2948 (links instead of copies for micro-VM restores) and #2974 (keeping shared snapshot images resident outside actor cgroups).