Layer-aware accounting and GC groundwork for shared content - #451
Closed
chruffins wants to merge 2 commits into
Closed
Layer-aware accounting and GC groundwork for shared content#451chruffins wants to merge 2 commits into
chruffins wants to merge 2 commits into
Conversation
Record manifest layer references (digest, size, media type) in image metadata at finalize, count shared layers once in disk accounting, and protect metadata-referenced and in-flight artifacts from OCI cache garbage collection by contributing them as extra GC roots. No runtime composition, tag API, or VM boot changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #449 (content-addressed image storage).
summary
Layer-aware accounting and garbage-collection groundwork that does not depend on runtime composition:
LayerRefentries, written at finalize from the already-read manifest. No separate manifest-metadata store — this extends the existingimageMetadataonly (see sequencing note below).computeLayerAccountingsums referenced layer bytes with each layer digest counted once regardless of how many images reference it; exposed ashypeman_images_referenced_layer_bytes{scope=unique|shared}from the existing metrics callback. Rootfs disks were already deduped by inode in Move image references to shared content storage #449; OCI cache blobs are content-addressed files counted once by the existing walk.LiveOCICacheDigestsreturns the manifest digest plus recorded layer digests of every non-failed image;images.OCICacheRootsfeeds them into the existingocicachegccollector as extra roots (wired into the composite roots incmd/api). Failed images contribute nothing so their blobs stay collectable. Layer digests are marked live as opaque leaves — no manifest parsing needed.what this does not do (per scope)
sequencing / dependency note
The layer reference list lives in
imageMetadata.Layersand is populated at finalize. If a follow-up introduces a dedicated manifest-metadata store,Layersshould migrate there (or be dropped in favor of it); nothing else in this PR depends on the field's location. In-flight protection detail: a queued build's metadata carries the manifest digest immediately, which protects blobs once the manifest blob exists on disk; layer blobs written before the manifest blob duringAppendImageremain covered by the collector's existingmin_blob_agegrace period.validation
go build ./...(embedded hypervisor/caddy/guest-agent binaries stubbed locally; stubs are gitignored)go vet ./lib/images/... ./lib/ocicachegc/... ./cmd/api/...clean-race):TestLiveOCICacheDigestsProtectsReferencedAndInflightArtifactsTestComputeLayerAccountingCountsSharedLayersOnceTestOCICacheGCProtectsImageReferencedBlobs(real collector sweep: metadata-referenced blob survives, orphan is collected)TestFinalizeImageRecordsLayersTestExtractOCIImageDetailsReturnsLayerRefsgo test -race -run 'Test(DeleteImagePreservesCrossRepositoryContent|DeleteTagRemovesBothLayoutReferences|LegacyImageIsNotShadowedByContentMetadata|ListAllMetadataDeduplicatesDualLayouts|FailedLegacyImageUsesReadyContent|ReadyContentDoesNotFallBackToLegacyDisk|WriteMetadataUsesContentWhenLegacyDirectoryIsEmpty|ContentLayoutResolvesDiskByDigest|ListAllMetadataContentLayout|TotalReadyImageBytes|ImageMetadata|TagFollowsLastPull)' ./lib/images ./lib/paths./lib/ocicachegc ./lib/registry ./lib/imagepush ./lib/imageretentiontests passPre-existing failures on the base branch (verified identical on #449's head, environment/network related, not caused by this PR):
TestImportLocalImageFromOCICache,TestDeleteAndRecreateDuringBuildTail,TestRecoverInterruptedCredentialed*, and the docker.io-pulling tests (TestCreateImage*,TestListImages,TestGetImage,TestDeleteImage*,TestLayerCaching).Note
Medium Risk
Changes OCI cache garbage collection roots—incorrect digest rooting could delete still-needed blobs, though failed images are excluded and tests exercise a real collector sweep.
Overview
Image metadata now records manifest layers (
LayerRef: digest, size, media type) when a pull finishes, replacing a simple layer count onpullResult. That list is what GC and metrics use without re-reading manifests.OCI cache GC keeps blobs alive from metadata, not only from
index.json, registry BuildKit tags, and in-flight pushes.LiveOCICacheDigests/images.OCICacheRootssupply manifest plus layer digests for every non-failed image (failed images contribute nothing); the API wires this into the existing compositeRootsProvider. Layer digests are treated as opaque live leaves inocicachegc.Observability:
computeLayerAccountingdedupes shared layers across images and exposeshypeman_images_referenced_layer_byteswithscope=unique|shared.Tests cover digest rooting, accounting, finalize persistence, extraction, and an end-to-end GC sweep where metadata-only references survive.
Reviewed by Cursor Bugbot for commit 9b8e641. Configure here.