Skip to content

Shared-layer lifecycle: accounting, eviction, recovery, materialization - #458

Draft
chruffins wants to merge 2 commits into
hypeship/rootfs-compositionfrom
hypeship/storage-lifecycle
Draft

Shared-layer lifecycle: accounting, eviction, recovery, materialization#458
chruffins wants to merge 2 commits into
hypeship/rootfs-compositionfrom
hypeship/storage-lifecycle

Conversation

@chruffins

@chruffins chruffins commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

summary

Capstone stage of the image-storage project — finishes migration, eviction, accounting, recovery, and observability on the shared-layer path.

  • Materialization in builds: after a pull, each manifest layer is materialized into images/layers/<digest>/ (best effort; the composed rootfs still comes from blobs, so an artifact failure only degrades sharing). New layer_materialization build phase with cache-hit attribution.
  • Reference-protected eviction: image deletion evicts only layer artifacts unreferenced by any remaining manifest model; a 10-minute grace period keeps fresh artifacts safe from racing in-flight builds. Startup also evicts orphans left by an unclean shutdown.
  • Interrupted-build recovery: stale .unpack-* / .install-* temp directories are swept at startup (age-gated so live builds are untouched).
  • Layer-aware accounting: TotalImageBytes now includes the layer store's physical bytes (TotalLayerBytes exposes them separately), so capacity admission sees the real footprint.
  • Observability: hypeman_images_layer_artifacts_evicted_total counter plus slog eviction summaries and per-layer materialization warnings.
  • Legacy flattened images remain readable and bootable; they carry no manifest model and no layer references, and are retired only through the existing delete/promotion paths.

validation

  • End-to-end lifecycle test: two images sharing a base layer materialize the base once; deleting one image keeps the shared layer; deleting the last reference evicts everything.
  • Accounting, grace-period, reference-protection, and stale-temp-dir tests.
  • go test ./lib/images ./lib/paths ./lib/builds ./lib/scopes green; cmd/api/api green except Docker Hub pulls (anonymous rate limit — confirmed via direct probe returning TOOMANYREQUESTS) and VM lifecycle tests (need bridge privileges; verified failing identically on unmodified main).
  • CI is unavailable during this work; all validation is local.

Note

Medium Risk
Changes image deletion, startup cleanup, and disk admission accounting; incorrect eviction could remove data still needed by images, though manifest references and a grace period mitigate that.

Overview
Completes the shared-layer storage path: pulled images materialize per-layer artifacts under images/layers/ (best effort, with a layer_materialization build phase), and reference-aware eviction removes layer dirs only when no manifest model still references them.

Deletion and startup run eviction plus a sweep of stale .unpack-* / .install-* temps, gated by a 10-minute grace period so in-flight builds are not raced. TotalImageBytes now counts ready rootfs metadata plus physical layer-store bytes (TotalLayerBytes exposes layers alone); disk-usage caching tracks the new layerBytes component.

Adds hypeman_images_layer_artifacts_evicted_total and lifecycle tests (shared base layer materialized once, survives partial deletes, full eviction when the last reference goes).

Reviewed by Cursor Bugbot for commit 33c8ca3. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 33c8ca3. Configure here.

Comment thread lib/images/layer_gc.go Outdated
info, statErr := os.Stat(dirPath)
if statErr != nil || info.ModTime().After(cutoff) {
continue
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eviction can wipe in-flight layers

Medium Severity

Layer eviction decides freshness from the digest directory ModTime, and it runs concurrently with materialization. Unpack and mkfs.erofs write inside child temp dirs, so the parent mtime goes stale; DeleteImage can then RemoveAll that tree while a build is still using it, especially during the later unlocked ExportRootfs window before the manifest is written.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 33c8ca3. Configure here.

Comment thread lib/images/manager.go Outdated
if result.CacheHit {
cacheStatus = "hit"
}
m.recordImageBuildPhase(ctx, ref.Digest(), "layer_materialization", time.Since(materializeStart), "success", cacheStatus)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layer bytes skipped after failed builds

Medium Severity

Materialized layer artifacts are written to the layer store before conversion, but refreshDiskUsageTotals only runs on successful finalize, delete, or startup. After a failed conversion the artifacts remain on disk while TotalImageBytes keeps the stale cached total, so capacity admission undercounts the real footprint.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 33c8ca3. Configure here.

@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 41f4080 to 35de95e Compare August 26, 2026 18:22
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 35de95e to f9a5fcc Compare August 26, 2026 18:46
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from d9caa0e to b0a27b4 Compare August 26, 2026 18:51
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from b0a27b4 to 37b8128 Compare August 26, 2026 18:52
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 37b8128 to 13adf76 Compare August 26, 2026 18:53
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 13adf76 to c0e6c32 Compare August 26, 2026 18:54
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from c0e6c32 to aac8b57 Compare August 26, 2026 18:55
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from aac8b57 to 7464627 Compare August 26, 2026 18:56
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 4467fe9 to 588fff8 Compare August 26, 2026 18:58
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from ac3de8e to 165e42f Compare August 26, 2026 19:30
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 165e42f to 3cac19c Compare August 26, 2026 19:38
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 8ca57c1 to 419c294 Compare August 26, 2026 19:41
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch 2 times, most recently from c238f03 to 5051c46 Compare August 26, 2026 19:49
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 5051c46 to 53b68ce Compare August 26, 2026 19:50
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch 2 times, most recently from 4578e67 to 1dddcc5 Compare August 31, 2026 18:25
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 1dddcc5 to aba220f Compare August 31, 2026 18:38
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
-->

✱ stlc build

go code · compare

Your SDK build was successful.

generate ✅bootstrap ✅format ✅

116 files generated at 1d4bcbb (pushed)

go get github.com/kernel/hypeman-go-staging@1d4bcbb4a96b963dc3fc1c3c75bee83ba9cc3d64
python code · compare

Your SDK build was successful.

generate ✅bootstrap ✅format ✅

232 files generated at 4bd7601 (pushed)

typescript code · compare

Your SDK build was successful.

generate ✅bootstrap ✅format ✅

138 files generated at aef5e78 (pushed)

Diagnostics: ❗ 0 new / 1 total error, 💡 0 new / 5 total note
LevelCodeMessageTargets
Build metadata
Buildbd_76G2SRhD-kind-shaft
Timestamp2026-08-31T22:48:01.884Z
stlc8413509
Spec hasheebba0a671f6
Config hash659c3687c3f0

This comment is auto-generated by stlc and is kept up to date as you push.
If you push new commits, re-run this workflow to update this comment.
Last updated: 2026-08-31 22:48:25 UTC

@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from aba220f to f0c7189 Compare August 31, 2026 21:35
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from f0c7189 to 3cf0501 Compare August 31, 2026 21:38
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 3cf0501 to 10f5e38 Compare August 31, 2026 21:56
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 10f5e38 to 9cbf5fb Compare August 31, 2026 21:59
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch 3 times, most recently from 466f089 to 0fa6aa6 Compare August 31, 2026 22:35
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch 2 times, most recently from 78e404c to 0e1b801 Compare August 31, 2026 23:12
@chruffins
chruffins force-pushed the hypeship/storage-lifecycle branch from 0e1b801 to 888f303 Compare August 31, 2026 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant