Skip to content

Commit

Permalink
feat: start using lazyDerivation for faster TUI response times
Browse files Browse the repository at this point in the history
  • Loading branch information
blaggacao committed Jun 16, 2023
1 parent c4d2c85 commit 0a513df
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 133 deletions.
1 change: 1 addition & 0 deletions cells/lib/ops.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ in {
mkSetup = import ./ops/mkSetup.nix {inherit inputs cell;};
mkUser = import ./ops/mkUser.nix {inherit inputs cell;};
writeScript = import ./ops/writeScript.nix {inherit inputs cell;};
lazyDerivation = import ./ops/lazyDerivation.nix {inherit inputs cell;};

mkOCI = import ./ops/mkOCI.nix {inherit inputs cell;};
mkDevOCI = import ./ops/mkDevOCI.nix {inherit inputs cell;};
Expand Down
108 changes: 108 additions & 0 deletions cells/lib/ops/lazyDerivation.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
inputs,
cell,
}: let
inherit (inputs) nixpkgs std;
l = nixpkgs.lib // builtins;
inherit (l) throwIfNot;
in (
if l ? lazyDerivation
then l.lazyDerivation
else
{
/*
Restrict a derivation to a predictable set of attribute names, so
that the returned attrset is not strict in the actual derivation,
saving a lot of computation when the derivation is non-trivial.
This is useful in situations where a derivation might only be used for its
passthru attributes, improving evaluation performance.
The returned attribute set is lazy in `derivation`. Specifically, this
means that the derivation will not be evaluated in at least the
situations below.
For illustration and/or testing, we define derivation such that its
evaluation is very noticeable.
let derivation = throw "This won't be evaluated.";
In the following expressions, `derivation` will _not_ be evaluated:
(lazyDerivation { inherit derivation; }).type
attrNames (lazyDerivation { inherit derivation; })
(lazyDerivation { inherit derivation; } // { foo = true; }).foo
(lazyDerivation { inherit derivation; meta.foo = true; }).meta
In these expressions, it `derivation` _will_ be evaluated:
"${lazyDerivation { inherit derivation }}"
(lazyDerivation { inherit derivation }).outPath
(lazyDerivation { inherit derivation }).meta
And the following expressions are not valid, because the refer to
implementation details and/or attributes that may not be present on
some derivations:
(lazyDerivation { inherit derivation }).buildInputs
(lazyDerivation { inherit derivation }).passthru
(lazyDerivation { inherit derivation }).pythonPath
*/
lazyDerivation = args @ {
# The derivation to be wrapped.
derivation,
# Optional meta attribute.
#
# While this function is primarily about derivations, it can improve
# the `meta` package attribute, which is usually specified through
# `mkDerivation`.
meta ? null,
# Optional extra values to add to the returned attrset.
#
# This can be used for adding package attributes, such as `tests`.
passthru ? {},
}: let
# These checks are strict in `drv` and some `drv` attributes, but the
# attrset spine returned by lazyDerivation does not depend on it.
# Instead, the individual derivation attributes do depend on it.
checked =
throwIfNot (derivation.type or null == "derivation")
"lazySimpleDerivation: input must be a derivation."
throwIfNot
(derivation.outputs == ["out"])
# Supporting multiple outputs should be a matter of inheriting more attrs.
"The derivation ${derivation.name or "<unknown>"} has multiple outputs. This is not supported by lazySimpleDerivation yet. Support could be added, and be useful as long as the set of outputs is known in advance, without evaluating the actual derivation."
derivation;
in
{
# Hardcoded `type`
#
# `lazyDerivation` requires its `derivation` argument to be a derivation,
# so if it is not, that is a programming error by the caller and not
# something that `lazyDerivation` consumers should be able to correct
# for after the fact.
# So, to improve laziness, we assume correctness here and check it only
# when actual derivation values are accessed later.
type = "derivation";

# A fixed set of derivation values, so that `lazyDerivation` can return
# its attrset before evaluating `derivation`.
# This must only list attributes that are available on _all_ derivations.
inherit (checked) outputs out outPath outputName drvPath name system;

# The meta attribute can either be taken from the derivation, or if the
# `lazyDerivation` caller knew a shortcut, be taken from there.
meta = args.meta or checked.meta;
}
// passthru;
}
.lazyDerivation
)
2 changes: 1 addition & 1 deletion cells/lib/ops/mkDevOCI.nix
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ in
vscode ? false,
slim ? false,
user ? "user",
tag ? "",
tag ? null,
pkgs ? [],
setup ? [],
perms ? [],
Expand Down
110 changes: 61 additions & 49 deletions cells/lib/ops/mkOCI.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ in
tag ?
if meta ? tags && (l.length meta.tags) > 0
then l.head meta.tags
else "",
else null,
setup ? [],
layers ? [],
runtimeInputs ? [],
Expand All @@ -47,57 +47,69 @@ in
mkdir -p $out/bin
ln -s ${l.getExe entrypoint} $out/bin/entrypoint
'';
options' =
{
inherit name meta;

# Layers are nested to reduce duplicate paths in the image
layers =
[
# Primary layer is the entrypoint layer
(n2c.buildLayer {
deps = [entrypoint];
maxLayers = 50;
layers = [
# Runtime inputs layer
(n2c.buildLayer {
deps = runtimeInputs;
maxLayers = 10;
})
];
})
]
++ layers;
image =
l.throwIf (args ? tag && meta ? tags)
"mkOCI/mkStandardOCI/mkDevOCI: use of `tag` and `meta.tags` arguments are not supported together. Remove the former."
n2c.buildImage (
l.recursiveUpdate options {
inherit name tag;

maxLayers = 25;
copyToRoot =
[
(nixpkgs.buildEnv {
name = "root";
paths = [setupLinks] ++ setup;
})
]
++ options.copyToRoot or [];
# Layers are nested to reduce duplicate paths in the image
layers =
[
# Primary layer is the entrypoint layer
(n2c.buildLayer {
deps = [entrypoint];
maxLayers = 50;
layers = [
# Runtime inputs layer
(n2c.buildLayer {
deps = runtimeInputs;
maxLayers = 10;
})
];
})
]
++ layers;

config = l.recursiveUpdate config {
User = uid;
Group = gid;
Entrypoint = ["/bin/entrypoint"];
Labels = l.mapAttrs' (n: v: l.nameValuePair "org.opencontainers.image.${n}" v) labels;
};
maxLayers = 25;
copyToRoot =
[
(nixpkgs.buildEnv {
name = "root";
paths = [setupLinks] ++ setup;
})
]
++ options.copyToRoot or [];

# Setup tasks can include permissions via the passthru.perms attribute
perms = l.flatten ((l.map (s: l.optionalAttrs (s ? passthru && s.passthru ? perms) s.passthru.perms)) setup) ++ perms;
}
// l.throwIf (args ? tag && meta ? tags)
"mkOCI: use of `tag` and `meta.tags` arguments are not supported together. Remove the former."
(l.optionalAttrs (tag != "") {inherit tag;});
config = l.recursiveUpdate config {
User = uid;
Group = gid;
Entrypoint = ["/bin/entrypoint"];
Labels = l.mapAttrs' (n: v: l.nameValuePair "org.opencontainers.image.${n}" v) labels;
};

image = n2c.buildImage (l.recursiveUpdate options options');
# Setup tasks can include permissions via the passthru.perms attribute
perms = l.flatten ((l.map (s: l.optionalAttrs (s ? passthru && s.passthru ? perms) s.passthru.perms)) setup) ++ perms;
}
);
in let
mainTag =
if tag != null && tag != ""
then tag
else
builtins.unsafeDiscardStringContext
(l.head (l.strings.splitString "-" (baseNameOf image.outPath)));
tags = l.unique ([mainTag] ++ meta.tags or []);
in
l.removeAttrs image [
"copyTo"
"copyToDockerDaemon"
"copyToPodman"
"copyToRegistry"
]
cell.ops.lazyDerivation {
inherit meta;
derivation = image;
passthru = {
image.name = "${name}:${mainTag}";
image.repo = name;
image.tag = mainTag;
image.tags = l.unique ([mainTag] ++ meta.tags or []);
};
}
27 changes: 15 additions & 12 deletions cells/lib/ops/mkOperable.nix
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ in
debugInputs ? [],
livenessProbe ? null,
readinessProbe ? null,
meta ? {},
}: let
# nixpkgs.runtimeShell is a path to the shell, not a derivation
runtimeShellBin =
Expand Down Expand Up @@ -69,18 +70,20 @@ in
'';
};
in
(cell.ops.writeScript
({
inherit runtimeInputs runtimeEnv;
name = "operable-${package.name}";
text = ''
${runtimeScript}
'';
}
// l.optionalAttrs (runtimeShell != null) {
inherit runtimeShell;
}))
// {
cell.ops.lazyDerivation {
inherit meta;
derivation =
cell.ops.writeScript
({
inherit runtimeInputs runtimeEnv;
name = "operable-${package.name}";
text = ''
${runtimeScript}
'';
}
// l.optionalAttrs (runtimeShell != null) {
inherit runtimeShell;
});
passthru =
# These attributes are useful for informing later stages
{
Expand Down
19 changes: 7 additions & 12 deletions cells/lib/ops/mkStandardOCI.nix
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ in
args @ {
name,
operable,
tag ? "",
tag ? null,
setup ? [],
uid ? "65534",
gid ? "65534",
Expand All @@ -40,11 +40,10 @@ in
options ? {},
meta ? {},
}: let
inherit (operable) passthru;
inherit (operable.passthru) livenessProbe readinessProbe runtimeInputs runtime debug;
inherit (operable) livenessProbe readinessProbe runtimeInputs runtime debug;

hasLivenessProbe = passthru ? livenessProbe;
hasReadinessProbe = passthru ? readinessProbe;
hasLivenessProbe = operable ? livenessProbe;
hasReadinessProbe = operable ? readinessProbe;
hasDebug = args.debug or false;

# Link useful paths into the container.
Expand Down Expand Up @@ -105,19 +104,15 @@ in
nss = nixpkgs.writeTextDir "etc/nsswitch.conf" ''
hosts: files dns
'';

tag' =
l.throwIf (args ? tag && meta ? tags)
"mkStandardOCI: use of `tag` and `meta.tags` arguments are not supported together. Remove the former."
l.optionalString (tag != "") ((import "${inputs.self}/deprecation.nix" inputs).warnLegacyTag tag);
in
with dmerge;
l.throwIf (args ? tag && meta ? tags)
"mkStandardOCI: use of `tag` and `meta.tags` arguments are not supported together. Remove the former."
cell.ops.mkOCI (
merge
{
inherit name uid gid labels options perms config meta setup runtimeInputs;
inherit name tag uid gid labels options perms config meta setup runtimeInputs;
entrypoint = operable';
tag = tag'; # ideally ''
}
{
# mkStandardOCI differentiators over mkOCI
Expand Down
Loading

0 comments on commit 0a513df

Please sign in to comment.