From 54341cd9f6810258b53503d373352f70d5ddf673 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 25 Jan 2021 16:08:33 -0500 Subject: [PATCH] hydra-evaluator: deal in jobset IDs --- src/hydra-evaluator/hydra-evaluator.cc | 123 +++++++++++++++++-------- 1 file changed, 84 insertions(+), 39 deletions(-) diff --git a/src/hydra-evaluator/hydra-evaluator.cc b/src/hydra-evaluator/hydra-evaluator.cc index b3dec4ac..2c4d3f96 100644 --- a/src/hydra-evaluator/hydra-evaluator.cc +++ b/src/hydra-evaluator/hydra-evaluator.cc @@ -13,7 +13,57 @@ using namespace nix; -typedef std::pair JobsetIdentity; +typedef std::pair JobsetSymbolicIdentity; + +class JobsetIdentity { + public: + + std::string project; + std::string jobset; + int id; + + + JobsetIdentity(const std::string& project, const std::string& jobset, const int id) + : project{ project }, jobset{ jobset }, id{ id } + { + } + + friend bool operator== (const JobsetIdentity &lhs, const JobsetIdentity &rhs); + friend bool operator!= (const JobsetIdentity &lhs, const JobsetIdentity &rhs); + friend bool operator< (const JobsetIdentity &lhs, const JobsetIdentity &rhs); + + + friend bool operator== (const JobsetIdentity &lhs, const JobsetSymbolicIdentity &rhs); + friend bool operator!= (const JobsetIdentity &lhs, const JobsetSymbolicIdentity &rhs); + + std::string display() const { + return str(format("%1%:%2% (jobset#%3%)") % project % jobset % id); + } +}; +bool operator==(const JobsetIdentity & lhs, const JobsetIdentity & rhs) +{ + return lhs.id == rhs.id; +} + +bool operator!=(const JobsetIdentity & lhs, const JobsetIdentity & rhs) +{ + return lhs.id != rhs.id; +} + +bool operator<(const JobsetIdentity & lhs, const JobsetIdentity & rhs) +{ + return lhs.id < rhs.id; +} + +bool operator==(const JobsetIdentity & lhs, const JobsetSymbolicIdentity & rhs) +{ + return lhs.project == rhs.first && lhs.jobset == rhs.second; +} + +bool operator!=(const JobsetIdentity & lhs, const JobsetSymbolicIdentity & rhs) +{ + return ! (lhs == rhs); +} enum class EvaluationStyle { @@ -39,7 +89,7 @@ struct Evaluator typedef std::map Jobsets; - std::optional evalOne; + std::optional evalOne; const size_t maxEvals; @@ -68,7 +118,7 @@ struct Evaluator pqxx::work txn(*conn); auto res = txn.exec - ("select project, j.name, lastCheckedTime, triggerTime, checkInterval, j.enabled as jobset_enabled " + ("select j.id as id, project, j.name, lastCheckedTime, triggerTime, checkInterval, j.enabled as jobset_enabled " "from Jobsets j " "join Projects p on j.project = p.name " "where j.enabled != 0 and p.enabled != 0"); @@ -79,7 +129,7 @@ struct Evaluator std::set seen; for (auto const & row : res) { - auto name = JobsetIdentity{row["project"].as(), row["name"].as()}; + auto name = JobsetIdentity{row["project"].as(), row["name"].as(), row["id"].as()}; if (evalOne && name != *evalOne) continue; @@ -113,7 +163,7 @@ struct Evaluator if (seen.count(i->first)) ++i; else { - printInfo("forgetting jobset ‘%s:%s’", i->first.first, i->first.second); + printInfo("forgetting jobset ‘%s’", i->first.display()); i = state->jobsets.erase(i); } } @@ -122,25 +172,24 @@ struct Evaluator { time_t now = time(0); - printInfo("starting evaluation of jobset ‘%s:%s’ (last checked %d s ago)", - jobset.name.first, jobset.name.second, + printInfo("starting evaluation of jobset ‘%s’ (last checked %d s ago)", + jobset.name.display(), now - jobset.lastCheckedTime); { auto conn(dbPool.get()); pqxx::work txn(*conn); txn.exec_params0 - ("update Jobsets set startTime = $1 where project = $2 and name = $3", + ("update Jobsets set startTime = $1 where id = $2", now, - jobset.name.first, - jobset.name.second); + jobset.name.id); txn.commit(); } assert(jobset.pid == -1); jobset.pid = startProcess([&]() { - Strings args = { "hydra-eval-jobset", jobset.name.first, jobset.name.second }; + Strings args = { "hydra-eval-jobset", jobset.name.project, jobset.name.jobset }; execvp(args.front().c_str(), stringsToCharPtrs(args).data()); throw SysError("executing ‘%1%’", args.front()); }); @@ -154,23 +203,23 @@ struct Evaluator { if (jobset.pid != -1) { // Already running. - debug("shouldEvaluate %s:%s? no: already running", - jobset.name.first, jobset.name.second); + debug("shouldEvaluate %s? no: already running", + jobset.name.display()); return false; } if (jobset.triggerTime != std::numeric_limits::max()) { // An evaluation of this Jobset is requested - debug("shouldEvaluate %s:%s? yes: requested", - jobset.name.first, jobset.name.second); + debug("shouldEvaluate %s? yes: requested", + jobset.name.display()); return true; } if (jobset.checkInterval <= 0) { // Automatic scheduling is disabled. We allow requested // evaluations, but never schedule start one. - debug("shouldEvaluate %s:%s? no: checkInterval <= 0", - jobset.name.first, jobset.name.second); + debug("shouldEvaluate %s? no: checkInterval <= 0", + jobset.name.display()); return false; } @@ -186,16 +235,15 @@ struct Evaluator if (jobset.evaluation_style == EvaluationStyle::ONE_AT_A_TIME) { auto evaluation_res = txn.parameterized ("select id from JobsetEvals " - "where project = $1 and jobset = $2 " + "where jobset_id = $1 " "order by id desc limit 1") - (jobset.name.first) - (jobset.name.second) + (jobset.name.id) .exec(); if (evaluation_res.empty()) { // First evaluation, so allow scheduling. - debug("shouldEvaluate(one-at-a-time) %s:%s? yes: no prior eval", - jobset.name.first, jobset.name.second); + debug("shouldEvaluate(one-at-a-time) %s? yes: no prior eval", + jobset.name.display()); return true; } @@ -214,20 +262,20 @@ struct Evaluator // If the previous evaluation has no unfinished builds // schedule! if (unfinished_build_res.empty()) { - debug("shouldEvaluate(one-at-a-time) %s:%s? yes: no unfinished builds", - jobset.name.first, jobset.name.second); + debug("shouldEvaluate(one-at-a-time) %s? yes: no unfinished builds", + jobset.name.display()); return true; } else { debug("shouldEvaluate(one-at-a-time) %s:%s? no: at least one unfinished build", - jobset.name.first, jobset.name.second); + jobset.name.display()); return false; } } else { // EvaluationStyle::ONESHOT, EvaluationStyle::SCHEDULED - debug("shouldEvaluate(oneshot/scheduled) %s:%s? yes: checkInterval elapsed", - jobset.name.first, jobset.name.second); + debug("shouldEvaluate(oneshot/scheduled) %s? yes: checkInterval elapsed", + jobset.name.display()); return true; } } @@ -352,8 +400,8 @@ struct Evaluator auto & jobset(i.second); if (jobset.pid == pid) { - printInfo("evaluation of jobset ‘%s:%s’ %s", - jobset.name.first, jobset.name.second, statusToString(status)); + printInfo("evaluation of jobset ‘%s’ %s", + jobset.name.display(), statusToString(status)); auto now = time(0); @@ -369,23 +417,20 @@ struct Evaluator jobset from getting stuck in an endless failing eval loop. */ txn.exec_params0 - ("update Jobsets set triggerTime = null where project = $1 and name = $2 and startTime is not null and triggerTime <= startTime", - jobset.name.first, - jobset.name.second); + ("update Jobsets set triggerTime = null where id = $1 and startTime is not null and triggerTime <= startTime", + jobset.name.id); /* Clear the start time. */ txn.exec_params0 - ("update Jobsets set startTime = null where project = $1 and name = $2", - jobset.name.first, - jobset.name.second); + ("update Jobsets set startTime = null where id = $1", + jobset.name.id); if (!WIFEXITED(status) || WEXITSTATUS(status) > 1) { txn.exec_params0 - ("update Jobsets set errorMsg = $1, lastCheckedTime = $2, errorTime = $2, fetchErrorMsg = null where project = $3 and name = $4", + ("update Jobsets set errorMsg = $1, lastCheckedTime = $2, errorTime = $2, fetchErrorMsg = null where id = $3", fmt("evaluation %s", statusToString(status)), now, - jobset.name.first, - jobset.name.second); + jobset.name.id); } txn.commit(); @@ -466,7 +511,7 @@ int main(int argc, char * * argv) else { if (!args.empty()) { if (args.size() != 2) throw UsageError("Syntax: hydra-evaluator [ ]"); - evaluator.evalOne = JobsetIdentity(args[0], args[1]); + evaluator.evalOne = JobsetSymbolicIdentity(args[0], args[1]); } evaluator.run(); }