This won't be an explicit feature in the protocol, but something that will be implementable with the following feature.
A realizer will be able to communicate back that the derivation graph. So instead of:
fn realize(&Graph<Derivation>, &[Realizer]) -> Result<(), RealizationError>
The interface of telling a realizer to realize a derivation will be similar to;:
fn realize(&mut Graph<Derivation>, &[Realizer]) -> Result<(), RealizationError>
Where the realizer will be able to modify the graph to change paths / files / whatever.
This will also allow a theoretical realizer to:
Considering that if our realizer mutates our graphs, we cannot know the store path of the final realization without realizing. This is catastrophic for caching, as using store paths to fetch will not work.
Unless we also link the every input-addressed store path to the content-addressed equivalent. This way, we also ensure that we cannot have more than a single canonical content-addressed version of an input addressed derivation.
Though, none of this will be exposed through the realizer protocol. This is simply an implementation detail, the real API will look more like this:
fn fetch(&mut Graph<Derivation>, &[Cacher]) -> Result<(), FetchError>
Did you notice how similar this is to the realize
function above? Yes, we did too. And so we can just unify these, making "realizing" and "caching" the exact same concept.
The only difference between a "cacher" and a general "realizer" will be that the cacher refuses to actually realize derivations, instead opting to only serve already realized ones.
The way realizers will communicate with each other is that the client will only send the derivations it wants them to realize one by one. No realizer will see the whole build graph at once.
Every derivation that has already been realized (for example, dependencies of a derivation which is ready to be realized) will be accompanied by a realizer where the realizing realizer can fetch it from (the sent realizer being the realizer we're sending it to means that the dependency was built on the same realizer).
This way, we will not mess up our state.
Note: The client will never store anything at all. The way we will ensure that the realization result is copied locally (if configured to) is by telling our local realizer to realize the final derivation. This is why we can conflate caching and realizing.
When you think about it, the system
field of Nix doesn't make sense, as it's just a feature.
Thus, the realizer protocol will not be special cased for Arch + OS combinations.
The way it will be is as follows: Every realizer will append the Arch and OS to its feature set. For example if I configure my realizer to support WASM execution, it will advertise its features set as [ "x86_64", "linux", "wasm" ]
.
Last updated: Nov 15 2024 at 11:45 UTC