hydra-queue-runner: Improved scheduling
[?]
Sep 21, 2013, 2:47 PM
GEADFVZ5LXXFIE3VIP4UJ4AEI2VX57DXER47JA4IHH5BG3QNPAEACDependencies
- [2]
2PWOXJTX - [3]
TFLAR4KAhydra-queue-runner: Start as many builds as possible on each iteration - [4]
TPSCSZKXSpeed up findBuildDependencyInQueue - [5]
I7UELKBVhydra-queue-runner: Don't unlock builds we just started - [6]
3BTJRSU3GitInput.pm: Don't do a chdir to the Git clone - [7]
D7X6XTKQIntegrate the "Job status" and "All jobs" tabs - [8]
Q5HZWFCYAdd support for darcs repositories. - [9]
AMI4DGBKDon't trigger evaluation of disabled jobsets - [10]
6ZB4CIW6Security: Improve checking of build products - [11]
7VWDMKAZhydra-queue-runner: don't clutter the system log with debug message - [12]
ECBA3GQO* Make the schema class names match the case of the SQL table names. - [13]
7YBYT2LQ - [14]
RNNLIVYYRespect SystemTypes if defined - [15]
M4TBFHHJ"limit" -> "rows" - [16]
MREXL5RTWhoops - [17]
NREF6YOA* Don't start more builds concurrently than allowed for each system - [18]
DQD7JMSU* Fix the terminology. - [19]
BDSD2JLV* Speed up manifest generation. - [20]
YTIDBFGUDrop unused "disabled" columns - [21]
D7TT2BNK - [22]
SB2V735VKeep track of the database schema version - [23]
MOX7XJ2EMerge the BuildSchedulingInfo table into the Builds table - [24]
6QRHXIM3* Speed up the jobset index page. Especially the query to get the - [25]
PYTQMQKDHydra/17: in queue runner, prefer builds in the queue that are a dependency of another build (with higher priority) - [26]
AK2UZDS2Jobset page: Add a new tab to show job status in a matrix - [27]
YEXD7CBKFix findBuildDependencyInQueue - [28]
Y4B2MURV - [29]
X27GNHDV* Basic job info in the database. - [30]
FGQPXZIXhydra: make nr of build to keep configurable per jobset - [31]
D6YQQQCN* Don't ignore SIGCHLD after all, Perl doesn't like it. Just do - [32]
R6B5CAFFLet Builds.timestamp refer to the time the build was added - [33]
JAH3UPWASupport revision control systems via plugins - [34]
LZO3C2KI* Hack around those SQLite timeouts: just retry the transaction. - [35]
DTXTS7LN* Speed up findBuildDependencyInQueue by doing only one SQL query for - [36]
OV7F5M3EMerge branch 'queue-17' - [37]
QLOLZHRXAllow a per-jobset check interval - [38]
AFTXA575* $HYDRA_DATA environment variable. - [39]
QTFVCDIFadded hide feature for project/jobset - [40]
PCKLFRT5Support push notification of repository changes - [41]
LZVO64YGMerge in the first bits of the API work - [42]
L2E6EVE2* Merged the Build and Job tables. - [43]
Y6AHH4THRemove the logfile and logSize columns from the database - [44]
3PNG7NIBRemove trailing whitespace - [45]
SA5ZZ3I4hydra-queue-runner: Use nix.machines instead of the SystemTypes table to determine how many build jobs are allowed per system type. - [46]
QZLMDKMU* Queue runner: don't start scheduled builds builds if they belong to - [47]
3E6IP3R3* Add the name of the jobset to ReleaseSetJobs, otherwise we can't - [48]
JM3DPYOMgenerated schema with new dbix class schema loader, grrrrrr - [49]
RFE6T5LG* Store jobset evaluations in the database explicitly. This includes - [50]
QTC3SYBMJobset page: Load the jobs and status tabs on demand - [51]
TWVSALRL* Allow the maximum number of concurrent builds per platform to be - [*]
3HZY24CX* Make jobsets viewable under - [*]
ODNCGFQ5* Improved the navigation bar: don't include all projects (since that - [*]
FPK5LF53* Put the project-related actions in a separate controller. Put the - [*]
2GK5DOU7* Downloading closures. - [*]
OX6NYJDVSplit viewing and editing a jobset - [*]
N22GPKYT* Put info about logs / build products in the DB. - [*]
KN3VYE5P* Cleaned up the foreign key constraints. - [*]
D5QIOJGP* Move everything up one directory.
Change contents
- replacement in src/lib/Hydra/Controller/Jobset.pm at line 53
($c->stash->{latestEval}) = $c->stash->{jobset}->jobsetevals->search({}, { rows => 1, order_by => ["id desc"] });$c->stash->{latestEval} = $c->stash->{jobset}->jobsetevals->search({}, { rows => 1, order_by => ["id desc"] })->single;$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema); - replacement in src/lib/Hydra/Controller/Jobset.pm at line 166
foreach my $b (@builds) {my $jobName = $b->get_column('job');$evals->{$eval->id}->{$jobName} ={ id => $b->id, finished => $b->finished, buildstatus => $b->buildstatus };$jobs{$jobName} = 1;$nrBuilds++;}last if $nrBuilds >= 10000;foreach my $b (@builds) {my $jobName = $b->get_column('job');$evals->{$eval->id}->{$jobName} ={ id => $b->id, finished => $b->finished, buildstatus => $b->buildstatus };$jobs{$jobName} = 1;$nrBuilds++;}last if $nrBuilds >= 10000; - replacement in src/lib/Hydra/Controller/Jobset.pm at line 177
$c->stash->{showInactive} = 1;foreach my $job ($c->stash->{jobset}->jobs->search({ name => { ilike => $filter } })) {next if defined $jobs{$job->name};$c->stash->{inactiveJobs}->{$job->name} = $jobs{$job->name} = 1;}$c->stash->{showInactive} = 1;foreach my $job ($c->stash->{jobset}->jobs->search({ name => { ilike => $filter } })) {next if defined $jobs{$job->name};$c->stash->{inactiveJobs}->{$job->name} = $jobs{$job->name} = 1;} - edit in src/lib/Hydra/Controller/Jobset.pm at line 214
$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema); - edit in src/lib/Hydra/Controller/Jobset.pm at line 293
, schedulingshares => int($c->stash->{params}->{schedulingshares}) - edit in src/lib/Hydra/Controller/Project.pm at line 204
$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema); - replacement in src/lib/Hydra/Helper/Nix.pm at line 23
captureStdoutStderr run grab);captureStdoutStderr run grabgetTotalShares); - edit in src/lib/Hydra/Helper/Nix.pm at line 537
sub getTotalShares {my ($db) = @_;return $db->resultset('Jobsets')->search({ 'project.enabled' => 1, 'me.enabled' => 1 },{ join => 'project', select => { sum => 'schedulingshares' }, as => 'sum' })->single->get_column('sum');} - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 17
=head1 COMPONENTS LOADED - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 20
=over 4=item * L<Hydra::Component::ToJSON>=back=cut__PACKAGE__->load_components("+Hydra::Component::ToJSON"); - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 39
data_type: 'text'is_nullable: 0=head2 branch - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 57
=head2 revcount - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 60
data_type: 'integer'is_nullable: 0 - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 70
"revcount",{ data_type => "integer", is_nullable => 0 }, - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 74
"revcount",{ data_type => "integer", is_nullable => 0 }, - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 84
=item * L</branch> - replacement in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 93
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:fx3yosWMmJ+MnvL/dSWtFA# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-09-20 11:08:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Yl1slt3SAizijgu0KUTn0A - edit in src/lib/Hydra/Schema/CachedDarcsInputs.pm at line 97
# You can replace this text with custom code or comments, and it will be preserved on regeneration - edit in src/lib/Hydra/Schema/Jobsets.pm at line 119
is_nullable: 0=head2 schedulingsharesdata_type: 'integer'default_value: 100 - edit in src/lib/Hydra/Schema/Jobsets.pm at line 160
"schedulingshares",{ data_type => "integer", default_value => 100, is_nullable => 0 }, - replacement in src/lib/Hydra/Schema/Jobsets.pm at line 283
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tsGR8MhZRIUeNwpcVczMUw# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-09-20 12:15:23# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:pD6tGW0Ob3fuA1p0uQnBWw - edit in src/root/edit-jobset.tt at line 3
[% USE format %] - edit in src/root/edit-jobset.tt at line 99
<label class="control-label">Scheduling shares</label> - edit in src/root/edit-jobset.tt at line 101
<div class="input-append"><input type="number" class="span3" name="schedulingshares" [% HTML.attributes(value => jobset.schedulingshares) %]/></div>[% IF totalShares %]<span class="help-inline">([% f = format("%.2f"); f(jobset.schedulingshares / totalShares * 100) %]% out of [% totalShares %] shares)</span>[% END %]</div></div><div class="control-group"><div class="controls"> - edit in src/root/jobset.tt at line 3
[% USE format %] - edit in src/root/jobset.tt at line 126
<th>Scheduling shares:</th><td>[% jobset.schedulingshares %] [% IF totalShares %] ([% f = format("%.2f"); f(jobset.schedulingshares / totalShares * 100) %]% out of [% totalShares %] shares)[% END %]</td></tr><tr> - replacement in src/script/hydra-queue-runner at line 31
if (!defined $lastTime || $build->starttime < $lastTime - 300) {if (!defined $lastTime || $build->starttime < $lastTime - 600) { - replacement in src/script/hydra-queue-runner at line 73
foreach my $system (${$machines}{$machineName}{'systemTypes'}) {foreach my $system (@{${$machines}{$machineName}{'systemTypes'}}) { - replacement in src/script/hydra-queue-runner at line 80
# Cache scheduled by derivation path to speed up# Cache scheduled builds by derivation path to speed up - replacement in src/script/hydra-queue-runner at line 84
foreach $db->resultset('Builds')->search({ finished => 0, enabled => 1 }, { join => ['project'] });foreach $db->resultset('Builds')->search({ finished => 0 }, { join => ['project'] }); - replacement in src/script/hydra-queue-runner at line 88
{ finished => 0, busy => 0, enabled => 1 },{ finished => 0, busy => 0 }, - edit in src/script/hydra-queue-runner at line 91
# Get the total number of scheduling shares.my $totalShares = getTotalShares($db); - replacement in src/script/hydra-queue-runner at line 95
# concurrent build for that system type. Choose the highest# priority builds first, then the oldest builds.# concurrent build for that system type. - replacement in src/script/hydra-queue-runner at line 106[10.288]→[10.892:944](∅→∅),[10.388]→[10.892:944](∅→∅),[10.2781]→[10.892:944](∅→∅),[10.892]→[10.892:944](∅→∅)
$extraAllowed = 0 if $extraAllowed < 0;next if $extraAllowed <= 0; - replacement in src/script/hydra-queue-runner at line 108[10.945]→[10.945:1004](∅→∅),[10.1004]→[10.540:625](∅→∅),[10.625]→[10.407:494](∅→∅),[10.494]→[3.0:79](∅→∅)
# Select the highest-priority builds to start.my @builds = $extraAllowed == 0 ? () : $db->resultset('Builds')->search({ finished => 0, busy => 0, system => $system->system, enabled => 1 },{ join => ['project'], order_by => ["priority DESC", "id"] });print STDERR "starting at most $extraAllowed builds for system ${\$system->system}\n";j: while ($extraAllowed-- > 0) { - replacement in src/script/hydra-queue-runner at line 112[10.1293]→[3.80:109](∅→∅),[3.109]→[10.686:728](∅→∅),[10.8821]→[10.686:728](∅→∅),[10.728]→[10.428:758](∅→∅),[10.758]→[4.493:578](∅→∅),[4.578]→[10.829:864](∅→∅),[10.829]→[10.829:864](∅→∅)
my $started = 0;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;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 $duration = $jobset->builds->search({ },{ where => \ ("(finished = 0 or (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.$duration += $jobset->builds->search({ finished => 0, busy => 1 })->count * 30;my $share = $jobset->schedulingshares;my $delta = ($share / $totalShares) - ($duration / $totalWindowSize);#printf STDERR "%s:%s: %d s, %.3f%%, allowance = %.3f%%\n", $jobset->get_column('project'), $jobset->name, $duration, $duration / $totalWindowSize, $delta;push @res, { jobset => $jobset, delta => $delta }; - replacement in src/script/hydra-queue-runner at line 145
next if $build->busy;foreach my $r (sort { $b->{delta} <=> $a->{delta} } @res) {my $jobset = $r->{jobset};#print STDERR "selected ", $jobset->get_column('project'), ':', $jobset->name, "\n"; - replacement in src/script/hydra-queue-runner at line 150[10.645]→[10.728:790](∅→∅),[10.728]→[10.728:790](∅→∅),[10.790]→[10.1310:1351](∅→∅),[10.790]→[10.60:94](∅→∅),[10.1351]→[10.60:94](∅→∅),[10.60]→[10.60:94](∅→∅),[10.94]→[10.10908:11115](∅→∅),[10.7477]→[10.1104:1149](∅→∅),[10.11115]→[10.1104:1149](∅→∅),[10.1104]→[10.1104:1149](∅→∅)
my $logfile = getcwd . "/logs/" . $build->id;mkdir(dirname $logfile);unlink($logfile);$build->update({ busy => 1, locker => $$, logfile => $logfile, starttime => time()});push @buildsStarted, $build;# 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 allowance = %.3f%%)\n",$build->id, $build->project->name, $build->jobset->name, $build->job->name, $build->system, $r->{delta}; - replacement in src/script/hydra-queue-runner at line 169
last if ++$started >= $extraAllowed;}my $logfile = getcwd . "/logs/" . $build->id;mkdir(dirname $logfile);unlink($logfile);$build->update({ busy => 1, locker => $$, logfile => $logfile, starttime => time()});push @buildsStarted, $build;next j;}} - replacement in src/script/hydra-queue-runner at line 183
if ($started > 0) {print STDERR "system type `", $system->system,"': $nrActive active, $max allowed, started $started builds\n";last; # nothing found, give up on this system type - edit in src/script/hydra-queue-runner at line 192
print "starting build $id (", $build->project->name, ":", $build->jobset->name, ':', $build->job->name, ") on ", $build->system, "\n"; - edit in src/sql/hydra.sql at line 64
schedulingShares integer not null default 100, - file addition: upgrade-21.sql[60.3004]
alter table Jobsetsadd column schedulingShares integer not null default 100;