Remove hydra-build and the old hydra-queue-runner
[?]
Jun 22, 2015, 1:43 PM
UXRNODRJJU7A33F4PG24WOHKTJFI3XQMZGT2MUOBRAH2MPQ7NZ2ACDependencies
- [2]
MREXL5RTWhoops - [3]
Z4Y3TVEEhydra-queue-{runner,evaluator}: don't clutter the system log with debug messages - [4]
ZNGKTEAIIf a build aborts, mark any remaining active build steps as aborted - [5]
6SGBF7JPSet build status to 1 if the primary build failed - [6]
WMSCRPSCFill in starttime/stoptime for cached builds - [7]
6PG5BATLRemove tabs - [8]
PHNLYPKBCall buildFinished when a cached build is added - [9]
VXTPW6BFhydra-build: Hack to handle timeouts - [10]
FUJ3A66Lhydra-queue-runner: Set the start time properly - [11]
MMUBEIGVhydra-queue-runner: Tweaked the selection method - [12]
IEP2Q6GV*headdesk* - [13]
TSR3RZC4Revert "Add a dependency_lookup configuration option to enable (slow) dependency lookup in queue. This behaviour was disabled temporarily in accefbb79 due to slowness in very large queues, but some people might be dependent on it, so it is configurable until the previous behaviour is implemented more efficiently." - [14]
AWMM5OGVUse delete instead of delete_all - [15]
ZZYTUBG2Fix extreme slowness in hydra-queue-runner - [16]
533PIYC3Prevent a division by zero in hydra-queue-runner - [17]
IS7GUIWYhydra-eval-guile-jobs: Register derivations as GC roots. - [18]
COLZ7V4Ehydra-build: Handle new trace messages - [19]
3YHNO5H2Don't use Perl's -w flag - [20]
UJOSXBZIRecord which build a failed build step was propagated from - [21]
E746D2DRDon't show missing paths in logs - [22]
WJ7IKFXLFix not-null constraint violation inserting build step - [23]
QMC6IMJQSplit timeSpent query into 2 separate queries, as postgresql isn't able to figure out a decent query plan. With 120k jobs in queue, this makes some queries go from 100s to 1-2s. - [24]
6MGFQDR2deleted some old scripts - [25]
QMW24O5SAdd support for Guile & Guix. - [26]
NREF6YOA* Don't start more builds concurrently than allowed for each system - [27]
FV2M6MOThydra: use autoconf/-make - [28]
DBPIYHMAhydra: add nix-prefetch-* to tarball - [29]
QI7MGZAU - [30]
K3EAQY3X* Doh. - [31]
JR76VU3WRemove timeout detection hack - [32]
A22P7HCOhydra: at evaluation, check if path is already built, and mark as built in stead of adding to the queue. - [33]
JK2QWPH6 - [34]
4LR7CSBThydra-build: Give a nicer error message if the derivation is gone - [35]
D5QIOJGP* Move everything up one directory. - [36]
4N5APGRG* Start of a helper tool to evaluate job expressions efficiently. - [37]
KX5L74EYadd nix-prefetch- scripts for now, were externals in svn - [38]
TRFRGOBDRemove Twitter notification support - [39]
TFLAR4KAhydra-queue-runner: Start as many builds as possible on each iteration - [40]
CURCK6C2 - [41]
3PNG7NIBRemove trailing whitespace - [42]
CQJX3RGUhydra-queue-runner: Don't kill builds we just started - [43]
EUWLW7FYDon't use the Switch module - [44]
IW2LHCLLfixed email bug - [45]
B72GLND4 - [46]
CMU3YKOU* Store the release name. - [47]
4SJPAAJXPass failing dependent builds to buildFinished - [48]
M2CFFNJYRemove unused file - [49]
37R34XJO* Negative caching: don't perform a build if a dependency already - [50]
Q6SOGMDQHydra/28: Rename "scheduler" to "evaluator" - [51]
VI32YSGAI should test first - [52]
2KDHXY52Kill builds that produce more than 64 MiB of log output - [53]
2PWOXJTX - [54]
XYBSTHMJfix for buildsteps starting at 0, probably something changed in catalyst - [55]
BOFOHCPKremoved debug print, added last 50 lines in failure emails - [56]
FHF6IZJQ* Basic release management: releases are now dynamically computed as - [57]
A32UT34Uhydra_build.pl: Honor `$build->timeout'. - [58]
KBW3FDZ2Merge remote branch 'remotes/origin/master' - [59]
TJK27WSBOpen the DB using Hydra::Model::DB->new - [60]
6L3ZM55SAdd font for the captcha - [61]
NB2VOKIRInclude names of committers in HipChat notifications - [62]
X27GNHDV* Basic job info in the database. - [63]
MPGVCHVF* Fix an apparent incompatibility with recent DBIx::Class. - [64]
CXRCPDSQ* added support for twitter notification - [65]
I7UELKBVhydra-queue-runner: Don't unlock builds we just started - [66]
OG7BEM57 - [67]
UVNQPK3THydra/56: handle failed builds with result only at build level, not buildsteps - [68]
S3ZLZP3N* Cut off builds after half an hour of apparent inactivity. This - [69]
D7NXMCON* Doh. - [70]
A63IHCMX* Register GC roots properly. - [71]
UMOJJ6DVtime out to 3600 for now - [72]
PKPWUHUX* Idem. - [73]
LSPRR4TKhydra-queue-runner: Disable findBuildDependencyInQueue for now - [74]
D7TT2BNK - [75]
J5UVLXOK* Start of a basic Catalyst web interface. - [76]
6KCP6ODP* Get the URI for use in notification mails from the Hydra config - [77]
VJP6O6WA* Doh. Remove debug statement. - [78]
AS5PAYLI - [79]
GAIBDEZZ* Store the name of the machine that performed a build step in the - [80]
6US6LEC7* Add a NarSize field to Hydra manifests. This allows nix-env - [81]
QZLMDKMU* Queue runner: don't start scheduled builds builds if they belong to - [82]
FHAVPTZ6Hydra/23: added some X-headers with meta info in email notifications, added more descriptive status - [83]
OV7F5M3EMerge branch 'queue-17' - [84]
YTESD75Z* Get rid of zombies. - [85]
NLJJZVHO* Use ->update({...}) properly. - [86]
R6B5CAFFLet Builds.timestamp refer to the time the build was added - [87]
WTUBAA5Qhydra-queue-runner: Handle restarted builds whose derivation is gone - [88]
US27ZTX5* HydraFrontend -> Hydra. - [89]
EFWN7JBV* Added a status page that shows all the currently executing build steps. - [90]
CLXEECMF* Start putting build results in a database. - [91]
G5A7TZVI* Don't discard old build steps when restarting a build. - [92]
7YBYT2LQ - [93]
DGZE7ZRA* Don't log redundant build steps in case of cached failures. - [94]
RWIBJ5L4* Autoflush stdout. - [95]
FYO6NECEhydra - [96]
AMFMXR52Provide a command ‘hydra-init’ to initialise/upgrade the database - [97]
RBHHV7P7* Read logs using logContents function in stead of handling it everywhere separately. - [98]
UGA45FNCAdd a plugin for backing up builds in s3 - [99]
SX5XYD6IWhen propagating failure, propagate the duration and machine - [100]
24BMQDZAStart of single-process hydra-queue-runner - [101]
DQD7JMSU* Fix the terminology. - [102]
R5D7DZPE - [103]
5WCR3MKUtrunk -> master - [104]
3BKF6P72* Use Nix's negative caching. - [105]
3E6IP3R3* Add the name of the jobset to ReleaseSetJobs, otherwise we can't - [106]
WOHG5MJDhydra: catalyst plain view doesn;t work with data => 0, so put some whitespace around it - [107]
Y6AHH4THRemove the logfile and logSize columns from the database - [108]
RNNLIVYYRespect SystemTypes if defined - [109]
JGLE5BRNAdd separate build step status codes for cached failures and timeouts - [110]
GEADFVZ5hydra-queue-runner: Improved scheduling - [111]
MNZ67UXOIf a build step fail, immediately fail all queued builds that depend on it - [112]
X3YLTWJAhydra-queue-runner: Cache the lookup of time spent per jobset - [113]
N22GPKYT* Put info about logs / build products in the DB. - [114]
TWVSALRL* Allow the maximum number of concurrent builds per platform to be - [115]
BLVQGJ4LUse OO-style plugins - [116]
XHOZT4WTAdd a command `hydra-create-user' for managing user accounts - [117]
GZAXDYBQ* Script for initialising the database. - [118]
7DWCXNC7Use the new Nix Perl bindings - [119]
YEXD7CBKFix findBuildDependencyInQueue - [120]
UCVYVMXBUse the same start/stop time for the build steps as for the build - [121]
QAJK5MCERemove obsolete hydra-control script - [122]
D6YQQQCN* Don't ignore SIGCHLD after all, Perl doesn't like it. Just do - [123]
L4AI5YL6Rename hydra_*.pl to hydra-* - [124]
YTIDBFGUDrop unused "disabled" columns - [125]
JUTAEI7RRestore old findBuildDependencyInQueue behaviour - [126]
YFPZ46YK* hydra: added variant of build input type, 'build output (same system)' to allow better continous integration in one jobset for multiple system. it makes sure that the system of the build that is passed as input for a job has the same system as the job. - [127]
GYPHTT4MManual: Remove tabs, indent consistently - [128]
EX4FXA5THandle active build steps of aborted builds properly - [129]
FLPZ3YCKRemove debug line - [130]
IUCHXUJPUse "can" to check whether a plugin supports buildFinished - [131]
D3DIBMOK* For products that are directories (like manuals), allow a default - [132]
A5V7HLIQ* Shut up DBIx::Class. - [133]
2T42QGZD* Register builds as GC roots so they don't get deleted. - [134]
HE3GX5IPOptimize fetch-git. - [135]
AFTXA575* $HYDRA_DATA environment variable. - [136]
T4LLYESZ* Nix expression for building Hydra. - [137]
EKNK5AHQdoc: Augment the "Installation" section with material by Visser & Dolstra. - [138]
J4AJQ6RXAdd a dependency_lookup configuration option to enable (slow) dependency lookup in queue. This behaviour was disabled temporarily in accefbb79 due to slowness in very large queues, but some people might be dependent on it, so it is configurable until the previous behaviour is implemented more efficiently. - [139]
7VWDMKAZhydra-queue-runner: don't clutter the system log with debug message - [140]
HPEG2RHVMerge the BuildResultInfo table into the Builds table - [141]
WHAFVCEI - [142]
64K7R4Y6Forgot to change Nix to Nix::Store in one place - [143]
TPSCSZKXSpeed up findBuildDependencyInQueue - [144]
AZ4LR2GT* Scripts for starting / stopping Hydra. - [145]
IJPTEKRF - [146]
QUTWJR7P* Include more info in notification emails. - [147]
ULHEOJO2* Better timeout. - [148]
DN3VAAP4hydra: remove HYDRA_HOME env var, not used - [149]
2IEFMER5* Add --fallback to prevent problems with obsolete substitutes. - [150]
TULPZ62Y* Perform builds in parallel. - [151]
BD3GRK4B* Get rid of "positive failures" and separate log phases. - [152]
WZ3AEJ67* hydra_update_gc_roots.pl registers build outputs that should be kept - [153]
ARD6Z67TDo incremental SVN checkouts - [154]
CLJQCY2X* Store info about all the build actions and allow them to be - [155]
5EQYVRWEAdd a plugin mechanism - [156]
MOX7XJ2EMerge the BuildSchedulingInfo table into the Builds table - [157]
G3IUM7VLhydra: add support for succeedOnFailure feature in stdenv - [158]
5DSF5KWY* Perl sucks. "getBuildLog $drvPath" doesn't mean the same as - [159]
57Y7RG2AUpdate queryPathInfo calls - [160]
L2E6EVE2* Merged the Build and Job tables. - [161]
WQXF2T3Dhydra-evaluator: Don't require $HYDRA_CONFIG - [162]
UN2KZL3ARename c -> hydra-eval-jobs - [163]
PYTQMQKDHydra/17: in queue runner, prefer builds in the queue that are a dependency of another build (with higher priority) - [164]
WM5WAST3* Monitor for dead builds all the time. - [165]
ZTQEU5QSHydra: Add support for maxSilent meta attribute (also already added timeout, but not implemented the actual timeout for the build yet) - [166]
ERNOO5ZZ* Reorganising. - [167]
LZO3C2KI* Hack around those SQLite timeouts: just retry the transaction. - [168]
5O6E5SU5hydra: store logfile/output path/closure size - [169]
LQNBKF3D - [170]
AHTEIK7G* Added a maintainers field to the Builds table. - [171]
KA45EBF5* Send email if a build fails. - [172]
W5GW4E22Don't install hydra-create - [173]
Y6H7Y3OTCapture the path to `guile', when available. - [174]
ECBA3GQO* Make the schema class names match the case of the SQL table names. - [175]
PMNWRTGJAdd multiple output support - [176]
SA5ZZ3I4hydra-queue-runner: Use nix.machines instead of the SystemTypes table to determine how many build jobs are allowed per system type. - [177]
DTXTS7LN* Speed up findBuildDependencyInQueue by doing only one SQL query for - [178]
66MEE6QGhydra-build: Don't send a giant query to the database - [179]
SIZ6KXPWSet the build status properly for failing local builds - [180]
T7CCJQOF* Revert for now due to Postgres breakage.
Change contents
- replacement in doc/manual/installation.xml at line 103[24.3391]→[24.3391:3473](∅→∅),[24.3035]→[24.1398:1399](∅→∅),[24.3473]→[24.1398:1399](∅→∅),[24.1398]→[24.1398:1399](∅→∅),[24.1399]→[24.3474:3493](∅→∅),[24.3493]→[24.818:877](∅→∅),[24.877]→[24.2:38](∅→∅),[24.38]→[24.915:955](∅→∅),[24.915]→[24.915:955](∅→∅)
Command completion should reveal a number of command-line tools from Hydra:<screen>hydra-build hydra-init hydra-update-gc-rootshydra-eval-jobs hydra-queue-runnerhydra-evaluator hydra-server</screen>Command completion should reveal a number of command-line toolsfrom Hydra, such as <command>hydra-queue-runner</command>. - file deletion: hydra_build.pl Build.pl hydra-build hydra_build.pl hydra_build.pl build.pl hydra_build.pl.in[24.1560]→[24.0:38](∅→∅),[24.38]→[24.2912:2912](∅→∅),[24.1]→[24.22:54](∅→∅),[24.54]→[24.2912:2912](∅→∅),[24.2543]→[24.196:231](∅→∅),[24.231]→[24.2912:2912](∅→∅),[24.1560]→[24.1892:1930](∅→∅),[24.1930]→[24.2912:2912](∅→∅),[24.2543]→[24.2562:2600](∅→∅),[24.2600]→[24.2912:2912](∅→∅),[24.4]→[24.7550:7582](∅→∅),[24.7582]→[24.2912:2912](∅→∅),[24.2543]→[24.2198888:2198929](∅→∅),[24.2198929]→[24.2912:2912](∅→∅)
#! /var/run/current-system/sw/bin/perluse strict;use List::MoreUtils qw(all);use File::Basename;use File::stat;use Hydra::Schema;use Hydra::Helper::Nix;use Hydra::Helper::PluginHooks;use Hydra::Model::DB;use Hydra::Helper::AddBuilds;use Set::Scalar;STDOUT->autoflush();my $db = Hydra::Model::DB->new();my $config = getHydraConfig();my @plugins = Hydra::Plugin->instantiate(db => $db, config => $config);sub addBuildStepOutputs {my ($step) = @_;my $drv = derivationFromPath($step->drvpath);$step->buildstepoutputs->create({ name => $_, path => $drv->{outputs}->{$_} })foreach keys %{$drv->{outputs}};}sub failDependents {my ($drvPath, $status, $errorMsg, $dependents, $startTime, $stopTime, $machine, $propagatedFrom) = @_;# Get the referrer closure of $drvPath.my $dependentDrvs = Set::Scalar->new(computeFSClosure(1, 0, $drvPath));my $time = time();txn_do($db, sub {for my $d (@dependentBuilds) {print STDERR "failing dependent build ", $d->id, " of ", $d->project->name, ":", $d->jobset->name, ":", $d->job->name, "\n";$d->update({ finished => 1, logfile => '', iscachedbuild => 0, buildstatus => $drvPath eq $d->drvpath ? 1 : 2, starttime => $time, stoptime => $time, errormsg => undef});my $step = $d->buildsteps->create({ stepnr => nextFreeStepNr($d), type => 0 # = build, drvpath => $drvPath, busy => 0, status => $status, starttime => $startTime, stoptime => $stopTime, errormsg => $errorMsg});addBuildStepOutputs($step);}});}push @$dependents, $d;, machine => $machine, propagatedfrom => $propagatedFrom->idnext unless $dependentDrvs->has($d->drvpath);my @dependentBuilds = $db->resultset('Builds')->search({ finished => 0, busy => 0 },{ columns => ["id", "project", "jobset", "job", "drvpath", "finished", "busy"] });sub doBuild {my ($build) = @_;my %outputs;$outputs{$_->name} = $_->path foreach $build->buildoutputs->all;my $drvPath = $build->drvpath;my $maxsilent = $build->maxsilent;my $timeout = $build->timeout;my $isCachedBuild = 1;my $outputCreated = 1; # i.e., the Nix build succeeded (but it could be a positive failure)my $startTime = time();my $stopTime = undef;my $buildStatus = 0; # = succeededmy $errormsg = undef;unless (all { isValidPath($_) } values(%outputs)) {$isCachedBuild = 0;# Do the build.my $thisBuildFailed = 0;my $someBuildFailed = 0;# Run Nix to perform the build, and monitor the stderr output# to get notifications about specific build steps, the# associated log files, etc.my $cmd = "nix-store --realise $drvPath " ."--timeout $timeout " ."--max-silent-time $maxsilent " ."--option build-max-log-size 67108864 " ."--keep-going --fallback " ."--no-build-output --log-type flat --print-build-trace " ."--add-root " . gcRootFor($outputs{out} // $outputs{(sort keys %outputs)[0]}) . " 2>&1";my $buildStepNr = nextFreeStepNr($build);my %buildSteps;open OUT, "$cmd |" or die;while (<OUT>) {unless (/^@\s+/) {print STDERR "$_";next;}if (/^@\s+build-started\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/) {my $drvPathStep = $1;txn_do($db, sub {my $step = $build->buildsteps->create({ stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++), type => 0 # = build, drvpath => $drvPathStep, system => $3, busy => 1});addBuildStepOutputs($step);});}elsif (/^@\s+build-remote\s+(\S+)\s+(\S+)$/) {my $drvPathStep = $1;my $machine = $2;txn_do($db, sub {my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;$step->update({machine => $machine});});}elsif (/^@\s+build-remote-start\s+(\S+)\s+/) {my $drvPathStep = $1;txn_do($db, sub {my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;$step->update({starttime => time});});}elsif (/^@\s+build-remote-done\s+(\S+)\s+/) {my $drvPathStep = $1;txn_do($db, sub {my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;$step->update({stoptime => time});});}elsif (/^@\s+build-succeeded\s+(\S+)\s+(\S+)$/) {my $drvPathStep = $1;txn_do($db, sub {my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;$step->update({busy => 0, status => 0, stoptime => time});$step->update({stoptime => time}) unless defined $step->update;});}elsif (/^@\s+build-failed\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {my $drvPathStep = $1;$someBuildFailed = 1;$thisBuildFailed = 1 if $drvPath eq $drvPathStep;my $errorMsg;my $status = 1;if ($3 eq "cached") {$status = 8;} elsif ($3 eq "timeout") {$status = 7;} else {$errorMsg = $4;}my $now = time;my $stepStartTime = $now;my $stepStopTime = $now;my $machine = "";txn_do($db, sub {if ($buildSteps{$drvPathStep}) {my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;$stepStartTime = $step->starttime;$stepStopTime = $now;$machine = $step->machine;$step->update({busy => 0, status => $status, errormsg => $errorMsg, stoptime => $now});}# Don't write a record if this derivation already# failed previously. This can happen if this is a# restarted build.elsif (scalar $build->buildsteps->search({drvpath => $drvPathStep, type => 0, busy => 0, status => 1}) == 0) {my $step = $build->buildsteps->create({ stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++), type => 0 # = build, drvpath => $drvPathStep, busy => 0, status => $status, starttime => $now, stoptime => $now, errormsg => $errorMsg});addBuildStepOutputs($step);}});# Immediately fail all builds that depend on this derivation.failDependents($drvPathStep, $status, $errorMsg, $dependents, $stepStartTime, $stepStopTime, $machine, $build);}elsif (/^@\s+substituter-started\s+(\S+)\s+(\S+)$/) {my $path = $1;txn_do($db, sub {my $step = $build->buildsteps->create({ stepnr => ($buildSteps{$path} = $buildStepNr++), type => 1 # = substitution, busy => 1, starttime => time});# "out" is kinda fake (substitutions don't have named outputs).$step->buildstepoutputs->create({ name => "out", path => $path });});}elsif (/^@\s+substituter-succeeded\s+(\S+)$/) {my $path = $1;txn_do($db, sub {my $step = $build->buildsteps->find({stepnr => $buildSteps{$path}}) or die;$step->update({busy => 0, status => 0, stoptime => time});});}}close OUT;my $res = $?;$stopTime = time();if ($res != 0) {if ($thisBuildFailed) { $buildStatus = 1; }elsif ($someBuildFailed) { $buildStatus = 2; }else { $buildStatus = 3; }}# Only store the output of running Nix if we have a miscellaneous error.$errormsg = undef unless $buildStatus == 3;}done:txn_do($db, sub {if ($buildStatus == 0) {my $size = 0;my $closureSize = 0;my $releaseName;my @closure = computeFSClosure(0, 0, values %outputs);foreach my $path (@closure) {my ($deriver, $hash, $time, $narSize, $refs) = queryPathInfo($path, 0);$closureSize += $narSize;$size += $narSize if grep { $path eq $_ } values(%outputs);}foreach my $path (values %outputs) {$buildStatus = 6 if $buildStatus == 0 && -f "$path/nix-support/failed";$releaseName //= getReleaseName($path);}$build->update({ releasename => $releaseName, size => $size, closuresize => $closureSize});addBuildProducts($db, $build);}$build->update({ finished => 1, busy => 0, locker => '', logfile => '', iscachedbuild => $isCachedBuild, buildstatus => $buildStatus, starttime => $startTime, stoptime => $stopTime // time(), errormsg => $errormsg});});}my $buildId = $ARGV[0] or die "syntax: $0 BUILD-ID\n";print STDERR "performing build $buildId\n";# Lock the build. If necessary, steal the lock from the parent# process (runner.pl). This is so that if the runner dies, the# children (i.e. the build.pl instances) can continue to run and won't# have the lock taken away.my $build;txn_do($db, sub {$build = $db->resultset('Builds')->find($buildId);die "build $buildId doesn't exist\n" unless defined $build;die "build $buildId already done\n" if $build->finished;if ($build->busy != 0 && $build->locker != getppid) {die "build $buildId is already being built";}$build->update({busy => 1, locker => $$});$build->buildsteps->search({busy => 1})->delete;$build->buildproducts->delete;});die unless $build;# Do the build. If it throws an error, unlock the build so that it# can be retried.eval {doBuild $build;print "done\n";};if ($@) {warn $@;txn_do($db, sub {$build->update({busy => 0, locker => $$});});}if ($ENV{'HYDRA_MAIL_TEST'}) {my $build = $db->resultset('Builds')->find($buildId);notifyBuildFinished(\@plugins, $build, []);exit 0;}notifyBuildFinished(\@plugins, $build, $dependents);# Mark any remaining active build steps as aborted.$build->buildsteps->search({ busy => 1 })->update({ busy => 0, status => 4, stoptime => time });elsif (/^@\s+substituter-failed\s+(\S+)\s+(\S+)\s+(\S+)$/) {my $path = $1;txn_do($db, sub {my $step = $build->buildsteps->find({stepnr => $buildSteps{$path}}) or die;$step->update({busy => 0, status => 1, errormsg => $3, stoptime => time});});}else {print STDERR "unknown Nix trace message: $_";}, starttime => time$errormsg .= $_;"--option print-missing false " .if (!isValidPath($drvPath)) {$buildStatus = 3;$errormsg = "derivation was garbage-collected prior to build";goto done;}my $dependents = [];}sub nextFreeStepNr {my ($build) = @_;my $max = $build->buildsteps->find({}, {select => {max => 'stepnr + 1'}, as => ['max']});return (defined $max && defined $max->get_column('max')) ? $max->get_column('max') : 1;use Nix::Store;use Hydra::Plugin; - file deletion: hydra_queue_runner.pl Runner.pl hydra-queue-runner hydra_queue_runner.pl hydra_queue_runner.pl runner.pl hydra_queue_runner.pl.in[24.1560]→[24.38:83](∅→∅),[24.83]→[24.7823:7823](∅→∅),[24.1]→[24.154:187](∅→∅),[24.187]→[24.7823:7823](∅→∅),[24.2543]→[24.485:527](∅→∅),[24.527]→[24.7823:7823](∅→∅),[24.1560]→[24.1973:2018](∅→∅),[24.2018]→[24.7823:7823](∅→∅),[24.2543]→[24.2792:2837](∅→∅),[24.2837]→[24.7823:7823](∅→∅),[24.4]→[24.9347:9380](∅→∅),[24.9380]→[24.7823:7823](∅→∅),[24.2543]→[24.2199118:2199166](∅→∅),[24.2199166]→[24.7823:7823](∅→∅)
#! /var/run/current-system/sw/bin/perluse strict;use Cwd;use File::Basename;use POSIX qw(dup2 :sys_wait_h);use Hydra::Schema;use Hydra::Helper::Nix;use Hydra::Model::DB;use IO::Handle;use Nix::Store;use Set::Scalar;chdir Hydra::Model::DB::getHydraPath or die;my $db = Hydra::Model::DB->new();STDOUT->autoflush();my $lastTime;#$SIG{CHLD} = 'IGNORE';sub unlockDeadBuilds {# Unlock builds whose building process has died.txn_do($db, sub {my @builds = $db->resultset('Builds')->search({finished => 0, busy => 1});foreach my $build (@builds) {my $pid = $build->locker;my $unlock = 0;if ($pid == $$) {if (!defined $lastTime || $build->starttime < $lastTime - 300) {$unlock = 1;}} elsif (kill(0, $pid) != 1) { # see if we can signal the process$unlock = 1;}if ($unlock) {print "build ", $build->id, " pid $pid died, unlocking\n";$build->update({ busy => 0, locker => "" });$build->buildsteps->search({ busy => 1 })->update({ busy => 0, status => 4, stoptime => time });}}});}sub findBuildDependencyInQueue {my ($buildsByDrv, $build) = @_;return undef unless isValidPath($build->drvpath);my @deps = grep { /\.drv$/ && $_ ne $build->drvpath } computeFSClosure(0, 0, $build->drvpath);return unless scalar @deps > 0;foreach my $d (@deps) {my $bs = $buildsByDrv->{$d};next unless defined $bs;return $db->resultset('Builds')->find((@$bs)[0]);}return undef;}sub checkBuilds {# print "looking for runnable builds...\n";my @buildsStarted;my $machines = getMachines;txn_do($db, sub {# Cache scheduled builds by derivation path to speed up# findBuildDependencyInQueue.my $buildsByDrv = {};push @{$buildsByDrv->{$_->drvpath}}, $_->idforeach $db->resultset('Builds')->search({ finished => 0 });# Builds in the queue of which a dependency is already building.my $blockedBuilds = Set::Scalar->new();blockBuilds($buildsByDrv, $blockedBuilds, $_)foreach $db->resultset('Builds')->search({ finished => 0, busy => 1 });# Get the system types for the runnable builds.my @systemTypes = $db->resultset('Builds')->search({ finished => 0, busy => 0 },{ join => ['project'], select => ['system'], as => ['system'], distinct => 1 });# Get the total number of scheduling shares.my $totalShares = getTotalShares($db) || 1;# For each system type, select up to the maximum number of# concurrent build for that system type.foreach my $system (@systemTypes) {# How many builds are already currently executing for this# system type?my $nrActive = $db->resultset('Builds')->search({finished => 0, busy => 1, system => $system->system})->count;(my $systemTypeInfo) = $db->resultset('SystemTypes')->search({system => $system->system});my $max = defined $systemTypeInfo ? $systemTypeInfo->maxconcurrent : $maxConcurrent{$system->system} // 2;my $extraAllowed = $max - $nrActive;next if $extraAllowed <= 0;print STDERR "starting at most $extraAllowed builds for system ${\$system->system}\n";j: while ($extraAllowed-- > 0) {my @runnableJobsets = $db->resultset('Builds')->search({ finished => 0, busy => 0, system => $system->system },{ select => ['project', 'jobset'], distinct => 1 });next if @runnableJobsets == 0;my $windowSize = 24 * 3600;my $totalWindowSize = $windowSize * $max;my @res;foreach my $b (@runnableJobsets) {my $jobset = $db->resultset('Jobsets')->find($b->get_column('project'), $b->get_column('jobset')) or die;my $timeSpent = $timeSpentPerJobset->{$b->get_column('project')}->{$b->get_column('jobset')};if (!defined $timeSpent) {$timeSpent = $jobset->builds->search({ },{ where => \ ("(finished = 0)"), join => 'buildsteps', select => \ "sum(coalesce(buildsteps.stoptime, ${\time}) - buildsteps.starttime)", as => "sum" })->single->get_column("sum") // 0;$timeSpent += $jobset->builds->search({ },{ where => \ ("(me.stoptime >= " . (time() - $windowSize) . ")"), join => 'buildsteps', select => \ "sum(coalesce(buildsteps.stoptime, ${\time}) - buildsteps.starttime)", as => "sum" })->single->get_column("sum") // 0;# Add a 30s penalty for each started build. This# is to account for jobsets that have running# builds but no build steps yet.$timeSpent += $jobset->builds->search({ finished => 0, busy => 1 })->count * $costPerBuild;$timeSpentPerJobset->{$b->get_column('project')}->{$b->get_column('jobset')} = $timeSpent;}my $share = $jobset->schedulingshares || 1; # prevent division by zeromy $used = $timeSpent / ($totalWindowSize * ($share / $totalShares));#printf STDERR "%s:%s: %d s, total used = %.2f%%, share used = %.2f%%\n", $jobset->get_column('project'), $jobset->name, $timeSpent, $timeSpent / $totalWindowSize * 100, $used * 100;push @res, { jobset => $jobset, used => $used };}foreach my $r (sort { $a->{used} <=> $b->{used} } @res) {my $jobset = $r->{jobset};#print STDERR "selected ", $jobset->get_column('project'), ':', $jobset->name, "\n";# Select the highest-priority build for this jobset.my @builds = $jobset->builds->search({ finished => 0, busy => 0, system => $system->system },{ order_by => ["priority DESC", "id"] });foreach my $build (@builds) {# Find a dependency of $build that has no queued# dependencies itself. This isn't strictly necessary,# but it ensures that Nix builds are done as part of# their corresponding Hydra builds, rather than as a# dependency of some other Hydra build.while (my $dep = findBuildDependencyInQueue($buildsByDrv, $build)) {$build = $dep;}next if $build->busy;printf STDERR "starting build %d (%s:%s:%s) on %s; jobset at %.2f%% of its share\n",$build->id, $build->project->name, $build->jobset->name, $build->job->name, $build->system, $r->{used} * 100;my $logfile = getcwd . "/logs/" . $build->id;mkdir(dirname $logfile);unlink($logfile);$build->update({ busy => 1, locker => $$, logfile => $logfile});push @buildsStarted, $build;next j;}}last; # nothing found, give up on this system type}}$lastTime = time();$_->update({ starttime => time() }) foreach @buildsStarted;});# Actually start the builds we just selected. We need to do this# outside the transaction in case it aborts or something.foreach my $build (@buildsStarted) {my $id = $build->id;eval {my $logfile = $build->logfile;my $child = fork();die unless defined $child;if ($child == 0) {eval {open LOG, ">$logfile" or die "cannot create logfile $logfile";POSIX::dup2(fileno(LOG), 1) or die;POSIX::dup2(fileno(LOG), 2) or die;exec("hydra-build", $id);};warn "cannot start build $id: $@";POSIX::_exit(1);}};if ($@) {warn $@;txn_do($db, sub {$build->update({ busy => 0, locker => $$ });});}}while (1) {eval {unlockDeadBuilds;checkBuilds;};warn $@ if $@;# print "sleeping...\n";sleep(5);}# Clean up zombies.while ((waitpid(-1, &WNOHANG)) > 0) { };}if (scalar(@ARGV) == 1 && $ARGV[0] eq "--unlock") {unlockDeadBuilds;exit 0;}$timeSpentPerJobset->{$jobset->get_column('project')}->{$jobset->name} += $costPerBuild;blockBuilds($buildsByDrv, $blockedBuilds, $build);next if $blockedBuilds->has($build->id);my $costPerBuild = 30;my $timeSpentPerJobset;my %maxConcurrent;foreach my $machineName (keys %{$machines}) {foreach my $system (@{${$machines}{$machineName}{'systemTypes'}}) {$maxConcurrent{$system} = (${$machines}{$machineName}{'maxJobs'} or 0) + ($maxConcurrent{$system} or 0)}}sub blockBuilds {my ($buildsByDrv, $blockedBuilds, $build) = @_;my @rdeps = grep { /\.drv$/ && $_ ne $build->drvpath } computeFSClosure(1, 0, $build->drvpath);foreach my $drv (@rdeps) {my $bs = $buildsByDrv->{$drv};next if !defined $bs;$blockedBuilds->insert($_) foreach @$bs;}}# Given a build, return an arbitrary queued build on which this build# depends; or undef if no such build exists. - edit in src/script/Makefile.am at line 7
hydra-build \ - edit in src/script/Makefile.am at line 8
hydra-queue-runner \ - replacement in src/script/hydra-eval-guile-jobs.in at line 107
;; the time 'hydra-build' attempts to build it.;; the time 'hydra-queue-runner' attempts to build it.