Skip to content

perf(image): add WithImageSquashOnly to drop per-layer squash trees - #731

Open
willejs wants to merge 1 commit into
anchore:mainfrom
willejs:fix/drop-intermediate-layer-squashes
Open

willejs wants to merge 1 commit into
anchore:mainfrom
willejs:fix/drop-intermediate-layer-squashes

Conversation

@willejs

@willejs willejs commented Oct 16, 2026 •

Copy link
Copy Markdown

squashLayers makes a squash for every layer by copying the one below and merging the layer on top. It keeps all of them, so memory grows with layers × files.

We hit this running syft as a DaemonSet. One of our images has 78 layers (~1 GB compressed): a big pip layer near the bottom and ~60 small ones above it. Each small layer gets its own full copy of that big tree, which comes to ~60 copies and 10.7 GB of heap. 85% of it was in FileTree.Copy.

A squashed-scope scan only reads the top layer's squash, so the rest is a waste of memory.

I think this could be related to #233 (and anchore/syft#2942) too.

Change

image.WithImageSquashOnly(ctx) tells Read to drop each layer's squash once the layer above it is squashed. That leaves at most two trees in memory at a time.

  • It's a context value, like LayerFetchExecutor, so it reaches every provider without any constructor changes.
  • It's off by default, so all-layers is untouched.
  • With it on, ResolveLinkByLayerSquash and Layer.OpenPathFromSquash return an error for a dropped layer instead of panicking.

Results

Same image, syft v1.52.0, squashed scope:

peak heap time
main 10.7 GB 1m42s
WithImageSquashOnly 1.6 GB 1m22s

The SBOM artifacts and files are identical.

Follow-up

If you're happy with this, I will open a Syft PR to set it for squashed scope.
Later on, we could probably look at merging one tree and avoiding the copies, but I am unsure of this right now.

Signed-off-by: Will Salt <william@cogna.co>
@willejs
willejs force-pushed the fix/drop-intermediate-layer-squashes branch from ade4a2b to 69beb61 Compare October 16, 2026 08:35
@dvdlevanon-miggo

Copy link
Copy Markdown

Thanks for this. We ran into the same issue independently and can confirm the diagnosis.

With a synthetic image (a 100k-file base layer plus small layers on top), peak heap in Image.Read grows linearly with layer count: about 0.6 GB at 10 layers and 5.1 GB at 100. Almost all of it is in Tree.Copy / FileNode.Copy from the per-layer squash.

An equivalent change on our side (dropping the previous layer's squash once the next one is built) brought the 100-layer case down to about 0.6 GB. SBOMs from the squashed scope were byte-identical before and after across a few dozen real-world images.

This branch has not been deployed

No deployments
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.

2 participants