max-substitution-jobs setting

This commit is contained in:
Matej Urbas 2023-04-30 09:56:24 +01:00
parent 89d3cc5a47
commit 613bc699bb
5 changed files with 74 additions and 10 deletions

View file

@ -204,7 +204,7 @@ void PathSubstitutionGoal::tryToRun()
if maxBuildJobs == 0 (no local builds allowed), we still allow if maxBuildJobs == 0 (no local builds allowed), we still allow
a substituter to run. This is because substitutions cannot be a substituter to run. This is because substitutions cannot be
distributed to another machine via the build hook. */ distributed to another machine via the build hook. */
if (worker.getNrLocalBuilds() >= std::max(1U, (unsigned int) settings.maxBuildJobs)) { if (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) {
worker.waitForBuildSlot(shared_from_this()); worker.waitForBuildSlot(shared_from_this());
return; return;
} }

View file

@ -18,6 +18,7 @@ Worker::Worker(Store & store, Store & evalStore)
{ {
/* Debugging: prevent recursive workers. */ /* Debugging: prevent recursive workers. */
nrLocalBuilds = 0; nrLocalBuilds = 0;
nrSubstitutions = 0;
lastWokenUp = steady_time_point::min(); lastWokenUp = steady_time_point::min();
permanentFailure = false; permanentFailure = false;
timedOut = false; timedOut = false;
@ -176,6 +177,12 @@ unsigned Worker::getNrLocalBuilds()
} }
unsigned Worker::getNrSubstitutions()
{
return nrSubstitutions;
}
void Worker::childStarted(GoalPtr goal, const std::set<int> & fds, void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
bool inBuildSlot, bool respectTimeouts) bool inBuildSlot, bool respectTimeouts)
{ {
@ -187,7 +194,10 @@ void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
child.inBuildSlot = inBuildSlot; child.inBuildSlot = inBuildSlot;
child.respectTimeouts = respectTimeouts; child.respectTimeouts = respectTimeouts;
children.emplace_back(child); children.emplace_back(child);
if (inBuildSlot) nrLocalBuilds++; if (inBuildSlot) {
if (dynamic_cast<PathSubstitutionGoal *>(child.goal2)) nrSubstitutions++;
else nrLocalBuilds++;
}
} }
@ -198,9 +208,14 @@ void Worker::childTerminated(Goal * goal, bool wakeSleepers)
if (i == children.end()) return; if (i == children.end()) return;
if (i->inBuildSlot) { if (i->inBuildSlot) {
if (dynamic_cast<PathSubstitutionGoal *>(goal)) {
assert(nrSubstitutions > 0);
nrSubstitutions--;
} else {
assert(nrLocalBuilds > 0); assert(nrLocalBuilds > 0);
nrLocalBuilds--; nrLocalBuilds--;
} }
}
children.erase(i); children.erase(i);
@ -220,7 +235,9 @@ void Worker::childTerminated(Goal * goal, bool wakeSleepers)
void Worker::waitForBuildSlot(GoalPtr goal) void Worker::waitForBuildSlot(GoalPtr goal)
{ {
debug("wait for build slot"); debug("wait for build slot");
if (getNrLocalBuilds() < settings.maxBuildJobs) bool isSubstitutionGoal = dynamic_cast<PathSubstitutionGoal *>(goal.get());
if ((!isSubstitutionGoal && getNrLocalBuilds() < settings.maxBuildJobs) ||
(isSubstitutionGoal && getNrSubstitutions() < settings.maxSubstitutionJobs))
wakeUp(goal); /* we can do it right away */ wakeUp(goal); /* we can do it right away */
else else
addToWeakGoals(wantingToBuild, goal); addToWeakGoals(wantingToBuild, goal);

View file

@ -88,11 +88,16 @@ private:
std::list<Child> children; std::list<Child> children;
/** /**
* Number of build slots occupied. This includes local builds and * Number of build slots occupied. This includes local builds but does not
* substitutions but not remote builds via the build hook. * include substitutions or remote builds via the build hook.
*/ */
unsigned int nrLocalBuilds; unsigned int nrLocalBuilds;
/**
* Number of substitution slots occupied.
*/
unsigned int nrSubstitutions;
/** /**
* Maps used to prevent multiple instantiations of a goal for the * Maps used to prevent multiple instantiations of a goal for the
* same derivation / path. * same derivation / path.
@ -220,12 +225,16 @@ public:
void wakeUp(GoalPtr goal); void wakeUp(GoalPtr goal);
/** /**
* Return the number of local build and substitution processes * Return the number of local build processes currently running (but not
* currently running (but not remote builds via the build * remote builds via the build hook).
* hook).
*/ */
unsigned int getNrLocalBuilds(); unsigned int getNrLocalBuilds();
/**
* Return the number of substitution processes currently running.
*/
unsigned int getNrSubstitutions();
/** /**
* Registers a running child process. `inBuildSlot` means that * Registers a running child process. `inBuildSlot` means that
* the process counts towards the jobs limit. * the process counts towards the jobs limit.

View file

@ -249,6 +249,17 @@ unsigned int MaxBuildJobsSetting::parse(const std::string & str) const
} }
} }
unsigned int MaxSubstitutionJobsSetting::parse(const std::string & str) const
{
if (str == "auto") return std::max(1U, std::thread::hardware_concurrency());
else {
if (auto n = string2Int<decltype(value)>(str))
return std::max(1U, *n);
else
throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
}
}
Paths PluginFilesSetting::parse(const std::string & str) const Paths PluginFilesSetting::parse(const std::string & str) const
{ {

View file

@ -29,6 +29,21 @@ struct MaxBuildJobsSetting : public BaseSetting<unsigned int>
unsigned int parse(const std::string & str) const override; unsigned int parse(const std::string & str) const override;
}; };
struct MaxSubstitutionJobsSetting : public BaseSetting<unsigned int>
{
MaxSubstitutionJobsSetting(Config * options,
unsigned int def,
const std::string & name,
const std::string & description,
const std::set<std::string> & aliases = {})
: BaseSetting<unsigned int>(def, true, name, description, aliases)
{
options->addSetting(this);
}
unsigned int parse(const std::string & str) const override;
};
struct PluginFilesSetting : public BaseSetting<Paths> struct PluginFilesSetting : public BaseSetting<Paths>
{ {
bool pluginsLoaded = false; bool pluginsLoaded = false;
@ -159,6 +174,18 @@ public:
)", )",
{"build-max-jobs"}}; {"build-max-jobs"}};
MaxSubstitutionJobsSetting maxSubstitutionJobs{
this, 16, "max-substitution-jobs",
R"(
This option defines the maximum number of substitution jobs that Nix
will try to run in parallel. The default is `16`. The minimum value
one can choose is `1` and lower values will be interpreted as `1`. The
special value `auto` causes Nix to use the number of CPUs in your
system. It can be overridden using the `--max-substitution-jobs`
command line switch.
)",
{"substitution-max-jobs"}};
Setting<unsigned int> buildCores{ Setting<unsigned int> buildCores{
this, this,
getDefaultCores(), getDefaultCores(),