New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC 0022] Minimal module list #22
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
--- | ||
feature: minimal-module-list | ||
start-date: 2018-01-12 | ||
author: Eelco Dolstra | ||
co-authors: (find a buddy later to help our with the RFC) | ||
related-issues: (will contain links to implementation PRs) | ||
--- | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
Evaluating a NixOS configuration is getting ever slower due to the | ||
reliance on a global list of modules unconditionally included by every | ||
configuration. The proposal is to switch from the use of `enable` | ||
options to explicit inclusion of needed modules. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
NixOS evaluation is getting slower with every release, as can be seen | ||
here: | ||
|
||
* https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.smallContainer.time | ||
* https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.lapp.time | ||
* https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.kde.time | ||
|
||
Evaluator memory use grows similarly, e.g. | ||
|
||
* https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.smallContainer.allocations | ||
|
||
This increase in resource consumption is particularly problematic for | ||
NixOps networks consisting of many machines. | ||
|
||
The main reason for the increase is the growth in the number of NixOS | ||
modules, from 497 in `module-list.nix` in NixOS 15.09 to 739 in | ||
17.09. The module system needs to evaluate every module, even though | ||
most modules are not needed for a particular configuration. | ||
|
||
The proposal is to require most modules to be included explicitly in a | ||
system configuration. That is, rather than writing e.g. | ||
|
||
hardware.pulseaudio.enable = true; | ||
|
||
you write | ||
|
||
imports = [ <nixpkgs/nixos/modules/config/pulseaudio.nix> ]; | ||
|
||
For example, here is the time and memory consumption for evaluating a | ||
50-machine NixOps network: | ||
|
||
| Nixpkgs | Time (s) | RSS (MiB) | | ||
| --------------------------- | -------- | --------- | | ||
| 17.09 | 73.7 | 6470 | | ||
| 17.09 minimal | 19.5 | 2577 | | ||
| 17.09 minimal + memoisation | 12.5 | 1532 | | ||
|
||
The "minimal" configuration replaces `module-list.nix` with a [smaller | ||
list](https://pastebin.com/GSHS8q67). (This list can be reduced a bit | ||
further by eliminating some unnecessary module dependencies, | ||
e.g.`hardware/opengl.nix` and `services/networking/dnsmasq.nix` are | ||
unnecessary. On the other hand, some modules that are not needed to | ||
evaluate the configuration but that are needed to get a workable | ||
system are probably missing.) "Memoisation" refers to using the | ||
[memoisation | ||
primop](https://github.com/NixOS/nix/commit/0395b9b94af56bb814810a32d680c606614b29e0) | ||
to eliminate repeated evaluations of Nixpkgs; it does not prevent | ||
repeated evaluations of the NixOS module system. | ||
|
||
# Detailed design | ||
[design]: #detailed-design | ||
|
||
* Replace `module-list.nix` with a minimal set of modules, i.e. a | ||
bare minimal NixOS system. | ||
|
||
* Get rid of unnecessary implicit module dependencies (but see | ||
below). In particular, `pam.nix` and `nsswitch.nix` need to be | ||
modularized. For example, options related to Kerberos should be | ||
moved out of `pam.nix`. | ||
|
||
* `xserver.nix` should not import all display/window/desktop manager | ||
modules. | ||
|
||
* For compatibility, we can provide a `all-modules.nix` that imports | ||
all modules. | ||
|
||
* Most `enable` options defaults should be changed to `true`, so that | ||
including a module activates it automatically. (But see below.) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would that work with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, |
||
|
||
* The manual needs to show what module needs to be imported to enable | ||
something. Or options should be listed per-module. | ||
|
||
... | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One drawback is that it will freeze our file hierarchy as we don't have a mechanism in place to notify of file renames. Each rename would have to also include a symlink from the original to new place. |
||
|
||
... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can see two major drawbacks: If nixpkgs is loaded using fetchFromGitHub then the How will the https://nixos.org/nixos/options.html# list be populated? |
||
|
||
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
The alternative is to continue with the current approach of including | ||
every module in `module-list.nix`. However, it's clear that this | ||
approach does not scale. Performance improvements to the evaluator can | ||
only delay the module apocalypse. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How much room for optimization is there in the nix evaluator? I remember while profiling ruby that |
||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
Many modules have unnecessary implicit module dependencies, where one | ||
module defines an option declared in another. For example, | ||
`network-interfaces.nix` sets `virtualisation.vswitch`, requiring | ||
`virtualisation/openvswitch.nix` to be included even when | ||
`networking.vswitches = {}`. It's not clear what the best way is to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about a hybrid where we keep all potential dependencies in
And my guess is that <10% of modules are dependencies for other modules. The major downsides IMO would be inconsistency (some modules are special) and discoverability (contributors need to know that they have to elevate their dependencies when they add them). |
||
deal with these. The option `networking.vswitches` could be moved to | ||
`virtualisation/openvswitch.nix`. Another approach is to provide | ||
modules with a mechanism to define options that may not have a | ||
declaration (in which case they would be ignored, rather than trigger | ||
an error). | ||
|
||
Should `enable` options default to `true`? This may not be desirable | ||
if one module imports another because it has an optional dependency on | ||
the latter's functionality. | ||
|
||
It might be nice to have a `<modules>` namespace in the Nix search | ||
path consisting of all modules available in the search path. This | ||
would make configurations agnostic as to the location of a module. For | ||
instance, instead of | ||
|
||
imports = [ (builtins.fetchgit https://github.com/edolstra/dwarffs + "/modules/dwarffs.nix") ]; | ||
|
||
you would write | ||
|
||
imports = [ <modules/dwarffs.nix> ]; | ||
|
||
where Nix would be invokes as | ||
|
||
nix-build ... -I https://github.com/edolstra/dwarffs | ||
|
||
# Future work | ||
[future]: #future-work | ||
|
||
... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
12 seconds is actually quite reasonable compared to building pretty much anything else. Is it not possible to prepare the system outputs through Hydra?