Repository navigation
Conversation
sipsma
force-pushed
the
sipsma/general-cache-export
branch
3 times, most recently
from
November 11, 2026 00:27
45c28a1 to
28abf8f
Compare
The engine image's entries were exported by Query._engineImageBundle, a field only the image's build called. It picked each result's parts from a table of types, and reached into a Git module source for its checkout. engine.localCache._export replaces it. It exports any results' cache entries as a bundle directory, with the parts of each result and of every result its value holds, read from their records rather than from a table of types. It evaluates each of them, and every part they declare, first. A Git module source carries its checkout because the source's value holds it; a container carries its image config, filesystem and exec metadata because they are its parts. A caller may force the layers' compression, as the image's build does with uncompressed layers. cmd/engine-image-entries calls it; the engine still merges the bundle at the image's fixed path at start. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
Module._prebuild and Module._runtimeImage build a module's runtime pieces as an engine of a given platform builds them for its calls, without running the module there. Say that, rather than naming the engine image as their user. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
…lure A result an export reaches through what a value holds was held undecoded, and an undecoded value evaluates nothing, so a part it declared could stay pending and be left out of the bundle. The export now decodes each one, and fails if a declared part is still not complete after evaluating it. A failed export released nothing it had allocated for its output; it now releases the output's ref. The test of a module source's export exports the source alone, so its checkout can only arrive through what the source holds. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
A cache export carried the parts of every result a value held, followed down through all of them. For a directory or a container those are what made it, such as the execs a build ran, whose data a lookup of the result never reads: the engine image's bundle grew from 767 MB to 1.8 GB. The export now carries each result's own parts, and looks into what a result holds only where the result has no parts of its own, as a module source has none and carries its checkout's. What made a result with parts travels as records only. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
…as records An exported directory that an exec wrote carries its own snapshot's data, and the exec that made it only as a record, which a lookup of the directory never reads. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
The export is new after v1.0.0-beta.27, so a caller that declares an earlier view gets the API that version's engine served, without it. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
Its description still said it carries the data of every result a value holds. It carries a result's own data, and looks into what a result holds only where the result has none of its own. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
…whole A Go module's first runtime build on a fresh engine found each of its ~450 dependency packages in the image's entries, but one at a time: the image shipped each package's build and not the dependency call that builds them, which a module's own imports make unique to it. The built-in Go SDK now builds every runtime with dagger/go's shared imports set to what generated code imports (GoSDKSharedImports). dagger/go then compiles the packages those reach in dependency calls of their own, the standard library's and the rest's, which are the same calls in every module that keeps the SDK's versions of those packages' modules, whatever else it imports. The image's build makes those calls for its seed and ships their results with the package builds, so a fresh engine finds each call whole, and looks up only the module's own other packages. It moves the built-in Go SDK's dagger/go pin to the commit that adds sharedImports and sharedDependencies (dagger/go #104), and the workspace's go-sdk pin to a go-sdk commit whose daggerGo names the same dagger/go, which the image's build requires of the two. Signed-off-by: Erik Sipsma <erik@sipsma.dev>
sipsma
force-pushed
the
sipsma/general-cache-export
branch
from
November 11, 2026 17:56
1caa7ee to
2aa97de
Compare
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.
The engine image's pre-warmed cache was made by a hidden field that only the image's build called,
Query._engineImageBundle. It picked each result's parts from a table of types, and reached into a Git module source for its checkout. This replaces it with a general export,engine.localCache._export(results, forcedCompression), which the image's build now calls like any other caller. Nothing changes for users.What the export does. It writes any results' cache entries to a directory as
bundle.jsonplus their blobs. That is the format the engine merges from the image at start, which is unchanged.Removed:
Query._engineImageBundleand its type table.dagql.Cache.ExportImageBundle. It is nowdagql.Cache.ExportBundle, which has no image-specific rules.Unchanged:
Module._prebuildandModule._runtimeImageare kept. Their descriptions now present them as a general capability: a module's runtime for a given platform, built without running it.The built-in Go SDK's shared dependency calls, shipped in the image. The Go SDK now builds every module runtime with dagger/go's
sharedImportsset to what generated code imports (distconsts.GoSDKSharedImports; dagger/go #104).engine-image-entriesbuilds its seeds with the same list. It exports the two shared dependency calls' results throughengine.localCache._export. Each is a module object holding package builds, and the export carries its record and the files and directories it holds.A Go module's first build on a fresh engine used to look up each of ~450 packages one by one. Now it finds the standard library's call and the generated code's call whole, and looks up only its own other packages. A module that requires newer versions of the base modules still finds the standard library's call.
Measured on one 16-CPU amd64 machine, against main with dagger/go #104's other commits:
Tests:
TestGo/TestRuntimeFindsSharedDependenciesInImageruns in test-module-runtimes with the image's entries, andcmd/engine-image-entrieschecks that the list covers whatcodegendepspins. The built-in Go SDK's dagger/go pin and the workspace's go-sdk pin move together. The workspace's go-sdk pin is go-sdk v1.6.0 plus one commit that points its generated entrypoints at the same dagger/go as the built-in Go SDK, as the image's build requires. Both dagger/go pins follow dagger/go #104, which is still a draft, and move again to its final head before this merges.Version gate:
_exportis new after v1.0.0-beta.27, so it is served only to views from v1.0.0-beta.28 on, as other APIs are gated to the version that first serves them.Known limit: files a container mounts with
withMountedFilesare results it holds, not parts of its own, so they travel as records only. The previous field didn't carry them either, and the image ships no such container.Measured on one m8gd.4xlarge, comparing main 8376266 with this change. Each arm was built as a full engine image with its entries, and each run was a fresh engine running
dagger -W github.com/dagger/dagger@83762668d8 check -l, in 4 interleaved pairs:Tests, in
RemoteCacheTransferSuite, which CI runs with the image's entries:Every dev engine build also exercises the export, because it builds the image's entries with it.