forked from lix-project/hydra
hydra-evaluator: deal in jobset IDs
This commit is contained in:
parent
cb01859718
commit
54341cd9f6
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue