hydra-evaluator: deal in jobset IDs

This commit is contained in:
Graham Christensen 2021-01-25 16:08:33 -05:00
parent cb01859718
commit 54341cd9f6
No known key found for this signature in database
GPG key ID: FE918C3A98C1030F

View file

@ -13,7 +13,57 @@
using namespace nix;
typedef std::pair<std::string, std::string> JobsetIdentity;
typedef std::pair<std::string, std::string> 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<JobsetIdentity, Jobset> Jobsets;
std::optional<JobsetIdentity> evalOne;
std::optional<JobsetSymbolicIdentity> 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<JobsetIdentity> seen;
for (auto const & row : res) {
auto name = JobsetIdentity{row["project"].as<std::string>(), row["name"].as<std::string>()};
auto name = JobsetIdentity{row["project"].as<std::string>(), row["name"].as<std::string>(), row["id"].as<int>()};
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<time_t>::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 [<project> <jobset>]");
evaluator.evalOne = JobsetIdentity(args[0], args[1]);
evaluator.evalOne = JobsetSymbolicIdentity(args[0], args[1]);
}
evaluator.run();
}