From 3d8318870289d7b6b08677fcd2da6ceb0b082f8c Mon Sep 17 00:00:00 2001 From: Christian Theune Date: Mon, 18 May 2015 08:38:49 +0200 Subject: [PATCH 1/3] Enable lazy/sparse allocation of generation symlinks: avoid creating new generations if a generation already exists. Alternatively or additionally I propose a mode where only the *last* generation will be sparse. --- src/nix-env/profiles.cc | 17 ++++++++++++++++- tests/user-envs.sh | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc index d8eb0ef52..1691dc099 100644 --- a/src/nix-env/profiles.cc +++ b/src/nix-env/profiles.cc @@ -80,7 +80,22 @@ Path createGeneration(Path profile, Path outPath) previous ones. */ int dummy; Generations gens = findGenerations(profile, dummy); - unsigned int num = gens.size() > 0 ? gens.back().number : 0; + + unsigned int num; + if (gens.size() > 0) { + /* Check existing generations whether they represent an + environment we already materialized before. In that case: + avoid cluttering the system with additional symlinks. */ + for (auto & gen : gens) { + if (readLink(gen.path) == outPath) { + return gen.path; + } + } + + num = gens.back().number; + } else { + num = 0; + } /* Create the new generation. Note that addPermRoot() blocks if the garbage collector is running to prevent the stuff we've diff --git a/tests/user-envs.sh b/tests/user-envs.sh index c9eed5a66..ad15ddc3e 100644 --- a/tests/user-envs.sh +++ b/tests/user-envs.sh @@ -99,6 +99,15 @@ if nix-env -q '*' | grep -q bar; then false; fi nix-env --list-generations test "$(nix-env --list-generations | wc -l)" -eq 7 +# Doing the same operation twice should result in the same generation, not an +# additional one. At this point we just brought back foo. Installing it again +# should not create a new generation. +nix-env -i foo + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 7 + # Switch to a specified generation. nix-env --switch-generation 7 [ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ] From ea39c98d419a816029299b99f70455798e66f6b7 Mon Sep 17 00:00:00 2001 From: Christian Theune Date: Tue, 19 May 2015 20:03:36 +0200 Subject: [PATCH 2/3] Implement alternative to lazy generations: * only the last generation can be lazy * depend on the '--lazy-generation' flag to be set --- src/nix-env/nix-env.cc | 15 ++++++++++----- src/nix-env/profiles.cc | 19 +++++++++++-------- src/nix-env/profiles.hh | 2 +- src/nix-env/user-env.cc | 4 ++-- src/nix-env/user-env.hh | 2 +- tests/user-envs.sh | 18 ++++++++++++++---- 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 10b95dad1..8a7f5f059 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -58,6 +58,7 @@ struct Globals bool removeAll; string forceName; bool prebuiltOnly; + bool lazyGeneration; }; @@ -510,7 +511,7 @@ static void installDerivations(Globals & globals, if (globals.dryRun) return; if (createUserEnv(*globals.state, allElems, - profile, settings.envKeepDerivations, lockToken)) break; + profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; } } @@ -524,6 +525,8 @@ static void opInstall(Globals & globals, Strings opFlags, Strings opArgs) globals.preserveInstalled = true; else if (arg == "--remove-all" || arg == "-r") globals.removeAll = true; + else if (arg == "--lazy-generation") + globals.lazyGeneration = true; else throw UsageError(format("unknown flag ‘%1%’") % arg); } @@ -617,7 +620,7 @@ static void upgradeDerivations(Globals & globals, if (globals.dryRun) return; if (createUserEnv(*globals.state, newElems, - globals.profile, settings.envKeepDerivations, lockToken)) break; + globals.profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; } } @@ -681,7 +684,7 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs) /* Write the new user environment. */ if (createUserEnv(*globals.state, installedElems, - globals.profile, settings.envKeepDerivations, lockToken)) break; + globals.profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; } } @@ -718,7 +721,8 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) } debug(format("switching to new user environment")); - Path generation = createGeneration(globals.profile, drv.queryOutPath()); + Path generation = createGeneration(globals.profile, drv.queryOutPath(), + globals.lazyGeneration); switchLink(globals.profile, generation); } @@ -751,7 +755,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors, if (globals.dryRun) return; if (createUserEnv(*globals.state, newElems, - profile, settings.envKeepDerivations, lockToken)) break; + profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; } } @@ -1355,6 +1359,7 @@ int main(int argc, char * * argv) globals.preserveInstalled = false; globals.removeAll = false; globals.prebuiltOnly = false; + globals.lazyGeneration = false; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { Operation oldOp = op; diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc index 1691dc099..f2caa9868 100644 --- a/src/nix-env/profiles.cc +++ b/src/nix-env/profiles.cc @@ -74,7 +74,7 @@ static void makeName(const Path & profile, unsigned int num, } -Path createGeneration(Path profile, Path outPath) +Path createGeneration(Path profile, Path outPath, bool lazy) { /* The new generation number should be higher than old the previous ones. */ @@ -83,13 +83,16 @@ Path createGeneration(Path profile, Path outPath) unsigned int num; if (gens.size() > 0) { - /* Check existing generations whether they represent an - environment we already materialized before. In that case: - avoid cluttering the system with additional symlinks. */ - for (auto & gen : gens) { - if (readLink(gen.path) == outPath) { - return gen.path; - } + Generation last = gens.back(); + + if (lazy && readLink(last.path) == outPath) { + /* If lazy generations are enabled then we only create a + new generation symlink if it differs from the last one. + + This helps keeping gratuitous installs/rebuilds from piling + up uncontrolled numbers of generations, cluttering up the + UI like grub. */ + return last.path; } num = gens.back().number; diff --git a/src/nix-env/profiles.hh b/src/nix-env/profiles.hh index 30d2376d9..64c98e0d6 100644 --- a/src/nix-env/profiles.hh +++ b/src/nix-env/profiles.hh @@ -31,7 +31,7 @@ typedef list Generations; profile, sorted by generation number. */ Generations findGenerations(Path profile, int & curGen); -Path createGeneration(Path profile, Path outPath); +Path createGeneration(Path profile, Path outPath, bool lazy); void deleteGeneration(const Path & profile, unsigned int gen); diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 3bc31b9ea..43db6b513 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -28,7 +28,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv) bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, bool keepDerivations, - const string & lockToken) + const string & lockToken, bool lazyGeneration) { /* Build the components in the user environment, if they don't exist already. */ @@ -141,7 +141,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, } debug(format("switching to new user environment")); - Path generation = createGeneration(profile, topLevelOut); + Path generation = createGeneration(profile, topLevelOut, lazyGeneration); switchLink(profile, generation); return true; diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh index f188efe9b..4fedd19aa 100644 --- a/src/nix-env/user-env.hh +++ b/src/nix-env/user-env.hh @@ -8,6 +8,6 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv); bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, bool keepDerivations, - const string & lockToken); + const string & lockToken, bool lazyGeneration); } diff --git a/tests/user-envs.sh b/tests/user-envs.sh index ad15ddc3e..6b290e501 100644 --- a/tests/user-envs.sh +++ b/tests/user-envs.sh @@ -99,14 +99,24 @@ if nix-env -q '*' | grep -q bar; then false; fi nix-env --list-generations test "$(nix-env --list-generations | wc -l)" -eq 7 -# Doing the same operation twice should result in the same generation, not an -# additional one. At this point we just brought back foo. Installing it again -# should not create a new generation. +# Doing the same operation twice results in the same generation, but creates an +# additional one. At this point we just brought back foo. + nix-env -i foo # Count generations. nix-env --list-generations -test "$(nix-env --list-generations | wc -l)" -eq 7 +test "$(nix-env --list-generations | wc -l)" -eq 8 + +# Now, doing that again but passing the --lazy-generations flag will not +# create a new generation. + +nix-env -i foo --lazy-generation + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 8 + # Switch to a specified generation. nix-env --switch-generation 7 From 12a888894bc528fbfa9d4c1ef2dbeb5909550894 Mon Sep 17 00:00:00 2001 From: Christian Theune Date: Wed, 20 May 2015 17:29:52 +0200 Subject: [PATCH 3/3] Mis-read Eelko's request to not make this an option: now, let's not make it an option. :) --- src/nix-env/nix-env.cc | 15 +++++---------- src/nix-env/profiles.cc | 8 ++++---- src/nix-env/profiles.hh | 2 +- src/nix-env/user-env.cc | 4 ++-- src/nix-env/user-env.hh | 2 +- tests/user-envs.sh | 15 +++------------ 6 files changed, 16 insertions(+), 30 deletions(-) diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 8a7f5f059..10b95dad1 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -58,7 +58,6 @@ struct Globals bool removeAll; string forceName; bool prebuiltOnly; - bool lazyGeneration; }; @@ -511,7 +510,7 @@ static void installDerivations(Globals & globals, if (globals.dryRun) return; if (createUserEnv(*globals.state, allElems, - profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; + profile, settings.envKeepDerivations, lockToken)) break; } } @@ -525,8 +524,6 @@ static void opInstall(Globals & globals, Strings opFlags, Strings opArgs) globals.preserveInstalled = true; else if (arg == "--remove-all" || arg == "-r") globals.removeAll = true; - else if (arg == "--lazy-generation") - globals.lazyGeneration = true; else throw UsageError(format("unknown flag ‘%1%’") % arg); } @@ -620,7 +617,7 @@ static void upgradeDerivations(Globals & globals, if (globals.dryRun) return; if (createUserEnv(*globals.state, newElems, - globals.profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; + globals.profile, settings.envKeepDerivations, lockToken)) break; } } @@ -684,7 +681,7 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs) /* Write the new user environment. */ if (createUserEnv(*globals.state, installedElems, - globals.profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; + globals.profile, settings.envKeepDerivations, lockToken)) break; } } @@ -721,8 +718,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) } debug(format("switching to new user environment")); - Path generation = createGeneration(globals.profile, drv.queryOutPath(), - globals.lazyGeneration); + Path generation = createGeneration(globals.profile, drv.queryOutPath()); switchLink(globals.profile, generation); } @@ -755,7 +751,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors, if (globals.dryRun) return; if (createUserEnv(*globals.state, newElems, - profile, settings.envKeepDerivations, lockToken, globals.lazyGeneration)) break; + profile, settings.envKeepDerivations, lockToken)) break; } } @@ -1359,7 +1355,6 @@ int main(int argc, char * * argv) globals.preserveInstalled = false; globals.removeAll = false; globals.prebuiltOnly = false; - globals.lazyGeneration = false; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { Operation oldOp = op; diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc index f2caa9868..cedefb157 100644 --- a/src/nix-env/profiles.cc +++ b/src/nix-env/profiles.cc @@ -74,7 +74,7 @@ static void makeName(const Path & profile, unsigned int num, } -Path createGeneration(Path profile, Path outPath, bool lazy) +Path createGeneration(Path profile, Path outPath) { /* The new generation number should be higher than old the previous ones. */ @@ -85,9 +85,9 @@ Path createGeneration(Path profile, Path outPath, bool lazy) if (gens.size() > 0) { Generation last = gens.back(); - if (lazy && readLink(last.path) == outPath) { - /* If lazy generations are enabled then we only create a - new generation symlink if it differs from the last one. + if (readLink(last.path) == outPath) { + /* We only create a new generation symlink if it differs + from the last one. This helps keeping gratuitous installs/rebuilds from piling up uncontrolled numbers of generations, cluttering up the diff --git a/src/nix-env/profiles.hh b/src/nix-env/profiles.hh index 64c98e0d6..30d2376d9 100644 --- a/src/nix-env/profiles.hh +++ b/src/nix-env/profiles.hh @@ -31,7 +31,7 @@ typedef list Generations; profile, sorted by generation number. */ Generations findGenerations(Path profile, int & curGen); -Path createGeneration(Path profile, Path outPath, bool lazy); +Path createGeneration(Path profile, Path outPath); void deleteGeneration(const Path & profile, unsigned int gen); diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 43db6b513..3bc31b9ea 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -28,7 +28,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv) bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, bool keepDerivations, - const string & lockToken, bool lazyGeneration) + const string & lockToken) { /* Build the components in the user environment, if they don't exist already. */ @@ -141,7 +141,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, } debug(format("switching to new user environment")); - Path generation = createGeneration(profile, topLevelOut, lazyGeneration); + Path generation = createGeneration(profile, topLevelOut); switchLink(profile, generation); return true; diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh index 4fedd19aa..f188efe9b 100644 --- a/src/nix-env/user-env.hh +++ b/src/nix-env/user-env.hh @@ -8,6 +8,6 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv); bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, bool keepDerivations, - const string & lockToken, bool lazyGeneration); + const string & lockToken); } diff --git a/tests/user-envs.sh b/tests/user-envs.sh index 6b290e501..5e4fcb7bc 100644 --- a/tests/user-envs.sh +++ b/tests/user-envs.sh @@ -99,25 +99,16 @@ if nix-env -q '*' | grep -q bar; then false; fi nix-env --list-generations test "$(nix-env --list-generations | wc -l)" -eq 7 -# Doing the same operation twice results in the same generation, but creates an -# additional one. At this point we just brought back foo. +# Doing the same operation twice results in the same generation, which triggers +# "lazy" behaviour and does not create a new symlink. +nix-env -i foo nix-env -i foo # Count generations. nix-env --list-generations test "$(nix-env --list-generations | wc -l)" -eq 8 -# Now, doing that again but passing the --lazy-generations flag will not -# create a new generation. - -nix-env -i foo --lazy-generation - -# Count generations. -nix-env --list-generations -test "$(nix-env --list-generations | wc -l)" -eq 8 - - # Switch to a specified generation. nix-env --switch-generation 7 [ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ]