Record materialized top-level outputs in completion values - #30877
Conversation
When the prefetcher finds a file already present in the local filesystem and its metadata carries no (matching) contents proxy, it verifies the file by digest before skipping the download, but previously left the metadata unchanged, so every subsequent modification check had to digest the file again. Record the verified file's contents proxy just like after a fresh download so that future checks only require a stat.
844a9e9 to
939a367
Compare
|
I think solving the problem in skyframe is the right direction. I fed Gemini with the problem space and the PR, had some discussion and came up with another idea: artifact_level_materialization_design_doc.md. |
|
Thanks, I'll spend more time thinking through this analysis. My main concern is that one of the premises is wrong: Folks regularly do We may need to revisit what top-level mode means for BwoB before we attempt this. |
|
Yes, we don't want to increase the memory usage. |
|
Asked Gemini to address the memory concern: consolidated_skyframe_materialization.md
|
With Build without the Bytes, the completion functions download top-level outputs that only exist as remote metadata. Which outputs they materialize depends on the invocation's download policy (download mode, --remote_download_regex patterns, and command), so completion values are only valid for invocations with the same policy. This change makes that dependency explicit: the policy is injected as a precomputed value on each sync and completion functions depend on it, so that a policy change (e.g. a different download mode or a switch between build and run) reevaluates them. This prepares for replacing RemoteOutputChecker#maybeInvalidateSkyframeValues, which deletes all completion nodes on download mode or command changes and misses --remote_download_regex changes entirely. The policy is set whenever a remote or disk cache is in use. It is deliberately not gated on Skymeld even though completion functions only download outputs when the Skymeld-only important output handler is registered: policy-change invalidation must also cover non-Skymeld invocations, and the policy must not depend on state that other modules' beforeCommand implementations have not yet initialized (SkymeldModule runs after RemoteModule).
With Build without the Bytes, the completion functions download top-level outputs of actions with an action cache hit as an unmodeled side effect of their evaluation: nothing in Skyframe records which files were materialized this way, so nothing can be invalidated when their local filesystem state diverges. This change records the materialization in the completion values themselves: TargetCompletionValue and AspectCompletionValue, previously empty singletons, now carry the output files that are present in the local filesystem while the metadata tracked for them in Skyframe remains remote, together with the contents proxy they were last observed with (recorded by the prefetcher when it materializes or verifies a file). Since the values already exist for every top-level target and aspect, this adds no Skyframe nodes or edges, and invocations that materialize nothing keep sharing the empty singleton.
FilesystemValueChecker now compares the outputs recorded as locally materialized by each completion value against the local filesystem and invalidates the completion node if a file is missing or modified. The next invocation that requests the corresponding top-level target reevaluates the completion function, which rematerializes the file if the invocation's download policy wants it locally - without reexecuting or even revalidating the generating action, whose outputs didn't change. The completion functions become non-hermetic to permit this invalidation, matching action execution nodes. This fixes a hole in incremental builds: a top-level output that was materialized by the completion function (rather than by its generating action, which had an action cache hit) is tracked in Skyframe with remote metadata, so its local deletion was only detected by RemoteOutputChecker consulting the immediately preceding invocation's state. Any intervening invocation that didn't request the output discarded that record, after which an incremental build reported success without restoring the deleted top-level output. The new integration tests cover the deletion of such an output after an unrelated build (restored), the deletion of an output that later invocations no longer want locally (not restored, and nothing is reevaluated), and the deletion of outputs downloaded during action execution by a --remote_download_outputs=all build (restored via the ordinary modified output check, which is unaffected by this change).
With materialized top-level outputs recorded in completion values and invalidated through the modified output check, the mechanisms that approximated cross-invocation materialization state are no longer needed: - shouldTrustMetadata no longer consults the previous invocation's checker: a top-level output materialized without its generating action being reexecuted is now restored by invalidating the completion value that recorded it instead of the generating action, no matter how many invocations happened in between. - maybeInvalidateSkyframeValues is deleted: a change to the download mode, --remote_download_regex or command changes the download policy, which is injected as a precomputed value and thus invalidates all completion functions. The current invocation's policy check in shouldTrustMetadata stays: without Skymeld, where the important output handler is not registered, top-level outputs are still restored by invalidating their generating action based on the download policy, which is fully known by the time the modified output check runs after the analysis phase.
The four tests document that an invocation using a broader download policy (including one that doesn't build anything) doesn't cause the next invocation to reevaluate or reexecute actions whose outputs it doesn't want locally, while still materializing the outputs it does want. They fail on master and pass with materialized outputs recorded in completion values, which no longer consult the previous invocation's download policy at all. Fixes bazelbuild#26924.
939a367 to
a3b746e
Compare
Description
This PR implements the consolidated design from @coeuvre's design document, replacing the dedicated download SkyFunction from earlier revisions of this PR: the materialization state of top-level outputs is recorded directly in the existing completion values instead of a child node, adding no Skyframe nodes or edges.
The commits are meant to be reviewed individually:
--remote_download_regexpatterns, command) is injected as a precomputed value that completion functions depend on, so a policy change reevaluates them. This is a correction to the design document, which doesn't specify how completions are invalidated on policy changes aftermaybeInvalidateSkyframeValuesis deleted; without it, e.g. aminimalbuild followed by atoplevelbuild of the same target would never download the top-level outputs. The policy must not be gated on Skymeld (non-Skymeld invocations also need policy-change invalidation, andSkymeldModule.beforeCommandruns afterRemoteModule's).TargetCompletionValueandAspectCompletionValue, previously empty singletons, record the outputs that are present in the local filesystem while their metadata tracked in Skyframe remains remote, together with their contents proxy. Invocations that materialize nothing keep sharing the empty singleton.FilesystemValueCheckercompares each completion value's record against the local filesystem and invalidates the completion node if a file is missing or modified; its reevaluation rematerializes the file if the policy wants it locally, without reexecuting or even revalidating the generating action. The completion functions become non-hermetic to permit this invalidation, matching action execution nodes. Lost-output rewinding stays inCompletionFunction, unchanged.RemoteOutputChecker(thelastRemoteOutputCheckerchain andmaybeInvalidateSkyframeValues) is deleted. The current invocation's policy check inshouldTrustMetadatastays for the non-Skymeld path.The base commit is #30871, whose contents-proxy recording keeps the completion values' record precise for files that were verified by digest rather than freshly downloaded.
Two caveats relative to the design document, to be addressed separately: completion values are only produced on success, so partially materialized outputs of failed targets are still not tracked (the failed event path keeps its inline important output handler call); and the FSVC-notification-to-prefetcher-submission mechanism for
--remote_download_regex-matched intermediate outputs (the document's second pillar) is left to a follow-up, including a story for lost remote blobs on that path.Motivation
With Build without the Bytes, the completion functions download top-level outputs that only exist as remote metadata as an unmodeled side effect of their evaluation, and correctness across invocations was maintained by point mechanisms whose gaps surfaced as bugs: a top-level output deleted locally was silently not restored if any intervening invocation didn't request it (the new
downloadToplevel_outputDeletedAfterUnrelatedBuild_toplevelOutputIsRestoredtest fails on master), and an invocation with a broader download policy caused the next incremental build to reevaluate and reexecute actions wholesale (#26924).Full test runs:
BuildWithoutTheBytesIntegrationTest(Java and shell),RemoteTests,FilesystemValueCheckerTest,RewindingTest,SkymeldBuildIntegrationTest,TargetCompleteEventTest.Build API Changes
No
Checklist
Release Notes
RELNOTES: With Build without the Bytes, locally materialized top-level outputs are now tracked in Skyframe. A requested top-level output that was deleted locally is reliably redownloaded in the next build that requests it, and changes to the download policy no longer cause unnecessary reevaluation or reexecution of actions.