forked from lix-project/hydra
* Hack around those SQLite timeouts: just retry the transaction.
This commit is contained in:
parent
80691a39f5
commit
97a6011628
|
@ -273,7 +273,7 @@ sub restart : Chained('build') PathPart Args(0) {
|
||||||
|
|
||||||
requireProjectOwner($c, $build->project);
|
requireProjectOwner($c, $build->project);
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
error($c, "This build cannot be restarted.")
|
error($c, "This build cannot be restarted.")
|
||||||
unless $build->finished &&
|
unless $build->finished &&
|
||||||
($build->resultInfo->buildstatus == 3 ||
|
($build->resultInfo->buildstatus == 3 ||
|
||||||
|
@ -304,7 +304,7 @@ sub cancel : Chained('build') PathPart Args(0) {
|
||||||
|
|
||||||
requireProjectOwner($c, $build->project);
|
requireProjectOwner($c, $build->project);
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
error($c, "This build cannot be cancelled.")
|
error($c, "This build cannot be cancelled.")
|
||||||
if $build->finished || $build->schedulingInfo->busy;
|
if $build->finished || $build->schedulingInfo->busy;
|
||||||
|
|
||||||
|
@ -340,7 +340,7 @@ sub keep : Chained('build') PathPart Args(1) {
|
||||||
|
|
||||||
registerRoot $build->outpath if $newStatus == 1;
|
registerRoot $build->outpath if $newStatus == 1;
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
$build->resultInfo->update({keep => int $newStatus});
|
$build->resultInfo->update({keep => int $newStatus});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ sub submit : Chained('jobset') PathPart Args(0) {
|
||||||
requireProjectOwner($c, $c->stash->{project});
|
requireProjectOwner($c, $c->stash->{project});
|
||||||
requirePost($c);
|
requirePost($c);
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
updateJobset($c, $c->stash->{jobset});
|
updateJobset($c, $c->stash->{jobset});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ sub delete : Chained('jobset') PathPart Args(0) {
|
||||||
requireProjectOwner($c, $c->stash->{project});
|
requireProjectOwner($c, $c->stash->{project});
|
||||||
requirePost($c);
|
requirePost($c);
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
$c->stash->{jobset}->delete;
|
$c->stash->{jobset}->delete;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ sub submit : Chained('project') PathPart Args(0) {
|
||||||
requireProjectOwner($c, $c->stash->{project});
|
requireProjectOwner($c, $c->stash->{project});
|
||||||
requirePost($c);
|
requirePost($c);
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
updateProject($c, $c->stash->{project});
|
updateProject($c, $c->stash->{project});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ sub delete : Chained('project') PathPart Args(0) {
|
||||||
requireProjectOwner($c, $c->stash->{project});
|
requireProjectOwner($c, $c->stash->{project});
|
||||||
requirePost($c);
|
requirePost($c);
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
$c->stash->{project}->delete;
|
$c->stash->{project}->delete;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ sub create_submit : Path('/create-project/submit') {
|
||||||
|
|
||||||
my $projectName = trim $c->request->params->{name};
|
my $projectName = trim $c->request->params->{name};
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
# Note: $projectName is validated in updateProject,
|
# Note: $projectName is validated in updateProject,
|
||||||
# which will abort the transaction if the name isn't
|
# which will abort the transaction if the name isn't
|
||||||
# valid. Idem for the owner.
|
# valid. Idem for the owner.
|
||||||
|
@ -127,7 +127,7 @@ sub create_jobset_submit : Chained('project') PathPart('create-jobset/submit') A
|
||||||
|
|
||||||
my $jobsetName = trim $c->request->params->{name};
|
my $jobsetName = trim $c->request->params->{name};
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
# Note: $jobsetName is validated in updateProject, which will
|
# Note: $jobsetName is validated in updateProject, which will
|
||||||
# abort the transaction if the name isn't valid.
|
# abort the transaction if the name isn't valid.
|
||||||
my $jobset = $c->stash->{project}->jobsets->create(
|
my $jobset = $c->stash->{project}->jobsets->create(
|
||||||
|
|
|
@ -146,14 +146,14 @@ sub releases :Local {
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($subcommand eq "submit") {
|
elsif ($subcommand eq "submit") {
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
updateReleaseSet($c, $releaseSet);
|
updateReleaseSet($c, $releaseSet);
|
||||||
});
|
});
|
||||||
return $c->res->redirect($c->uri_for("/releases", $projectName, $releaseSet->name));
|
return $c->res->redirect($c->uri_for("/releases", $projectName, $releaseSet->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($subcommand eq "delete") {
|
elsif ($subcommand eq "delete") {
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
$releaseSet->delete;
|
$releaseSet->delete;
|
||||||
});
|
});
|
||||||
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for('view'), [$project->name]));
|
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for('view'), [$project->name]));
|
||||||
|
@ -181,7 +181,7 @@ sub create_releaseset :Local {
|
||||||
|
|
||||||
if (defined $subcommand && $subcommand eq "submit") {
|
if (defined $subcommand && $subcommand eq "submit") {
|
||||||
my $releaseSetName = $c->request->params->{name};
|
my $releaseSetName = $c->request->params->{name};
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
txn_do($c->model('DB')->schema, sub {
|
||||||
# Note: $releaseSetName is validated in updateProject,
|
# Note: $releaseSetName is validated in updateProject,
|
||||||
# which will abort the transaction if the name isn't
|
# which will abort the transaction if the name isn't
|
||||||
# valid.
|
# valid.
|
||||||
|
|
|
@ -8,7 +8,7 @@ use File::Basename;
|
||||||
our @ISA = qw(Exporter);
|
our @ISA = qw(Exporter);
|
||||||
our @EXPORT = qw(
|
our @EXPORT = qw(
|
||||||
isValidPath queryPathInfo
|
isValidPath queryPathInfo
|
||||||
getHydraPath getHydraDBPath openHydraDB
|
getHydraPath getHydraDBPath openHydraDB txn_do
|
||||||
registerRoot getGCRootsDir gcRootFor
|
registerRoot getGCRootsDir gcRootFor
|
||||||
getPrimaryBuildsForReleaseSet getRelease getLatestSuccessfulRelease );
|
getPrimaryBuildsForReleaseSet getRelease getLatestSuccessfulRelease );
|
||||||
|
|
||||||
|
@ -78,6 +78,21 @@ sub openHydraDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Awful hack to handle timeouts in SQLite: just retry the transaction.
|
||||||
|
# DBD::SQLite *has* a 30 second retry window, but apparently it
|
||||||
|
# doesn't work.
|
||||||
|
sub txn_do {
|
||||||
|
my ($db, $coderef) = @_;
|
||||||
|
while (1) {
|
||||||
|
eval {
|
||||||
|
$db->txn_do($coderef);
|
||||||
|
};
|
||||||
|
last if !$@;
|
||||||
|
die $@ unless $@ =~ "database is locked";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub getGCRootsDir {
|
sub getGCRootsDir {
|
||||||
die unless defined $ENV{LOGNAME};
|
die unless defined $ENV{LOGNAME};
|
||||||
my $dir = "/nix/var/nix/gcroots/per-user/$ENV{LOGNAME}/hydra-roots";
|
my $dir = "/nix/var/nix/gcroots/per-user/$ENV{LOGNAME}/hydra-roots";
|
||||||
|
|
|
@ -65,7 +65,7 @@ sub doBuild {
|
||||||
|
|
||||||
if (/^@\s+build-started\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/) {
|
if (/^@\s+build-started\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/) {
|
||||||
my $drvPathStep = $1;
|
my $drvPathStep = $1;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$build->buildsteps->create(
|
$build->buildsteps->create(
|
||||||
{ stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++)
|
{ stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++)
|
||||||
, type => 0 # = build
|
, type => 0 # = build
|
||||||
|
@ -80,7 +80,7 @@ sub doBuild {
|
||||||
|
|
||||||
elsif (/^@\s+build-succeeded\s+(\S+)\s+(\S+)$/) {
|
elsif (/^@\s+build-succeeded\s+(\S+)\s+(\S+)$/) {
|
||||||
my $drvPathStep = $1;
|
my $drvPathStep = $1;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;
|
my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;
|
||||||
$step->update({busy => 0, status => 0, stoptime => time});
|
$step->update({busy => 0, status => 0, stoptime => time});
|
||||||
});
|
});
|
||||||
|
@ -92,7 +92,7 @@ sub doBuild {
|
||||||
$thisBuildFailed = 1 if $drvPath eq $drvPathStep;
|
$thisBuildFailed = 1 if $drvPath eq $drvPathStep;
|
||||||
my $errorMsg = $4;
|
my $errorMsg = $4;
|
||||||
$errorMsg = "build failed previously (cached)" if $3 eq "cached";
|
$errorMsg = "build failed previously (cached)" if $3 eq "cached";
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
if ($buildSteps{$drvPathStep}) {
|
if ($buildSteps{$drvPathStep}) {
|
||||||
my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;
|
my $step = $build->buildsteps->find({stepnr => $buildSteps{$drvPathStep}}) or die;
|
||||||
$step->update({busy => 0, status => 1, errormsg => $errorMsg, stoptime => time});
|
$step->update({busy => 0, status => 1, errormsg => $errorMsg, stoptime => time});
|
||||||
|
@ -119,7 +119,7 @@ sub doBuild {
|
||||||
|
|
||||||
elsif (/^@\s+substituter-started\s+(\S+)\s+(\S+)$/) {
|
elsif (/^@\s+substituter-started\s+(\S+)\s+(\S+)$/) {
|
||||||
my $outPath = $1;
|
my $outPath = $1;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$build->buildsteps->create(
|
$build->buildsteps->create(
|
||||||
{ stepnr => ($buildSteps{$outPath} = $buildStepNr++)
|
{ stepnr => ($buildSteps{$outPath} = $buildStepNr++)
|
||||||
, type => 1 # = substitution
|
, type => 1 # = substitution
|
||||||
|
@ -132,7 +132,7 @@ sub doBuild {
|
||||||
|
|
||||||
elsif (/^@\s+substituter-succeeded\s+(\S+)$/) {
|
elsif (/^@\s+substituter-succeeded\s+(\S+)$/) {
|
||||||
my $outPath = $1;
|
my $outPath = $1;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
my $step = $build->buildsteps->find({stepnr => $buildSteps{$outPath}}) or die;
|
my $step = $build->buildsteps->find({stepnr => $buildSteps{$outPath}}) or die;
|
||||||
$step->update({busy => 0, status => 0, stoptime => time});
|
$step->update({busy => 0, status => 0, stoptime => time});
|
||||||
});
|
});
|
||||||
|
@ -140,7 +140,7 @@ sub doBuild {
|
||||||
|
|
||||||
elsif (/^@\s+substituter-failed\s+(\S+)\s+(\S+)\s+(\S+)$/) {
|
elsif (/^@\s+substituter-failed\s+(\S+)\s+(\S+)\s+(\S+)$/) {
|
||||||
my $outPath = $1;
|
my $outPath = $1;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
my $step = $build->buildsteps->find({stepnr => $buildSteps{$outPath}}) or die;
|
my $step = $build->buildsteps->find({stepnr => $buildSteps{$outPath}}) or die;
|
||||||
$step->update({busy => 0, status => 1, errormsg => $3, stoptime => time});
|
$step->update({busy => 0, status => 1, errormsg => $3, stoptime => time});
|
||||||
});
|
});
|
||||||
|
@ -169,7 +169,7 @@ sub doBuild {
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$build->update({finished => 1, timestamp => time});
|
$build->update({finished => 1, timestamp => time});
|
||||||
|
|
||||||
my $releaseName;
|
my $releaseName;
|
||||||
|
@ -265,7 +265,7 @@ print STDERR "performing build $buildId\n";
|
||||||
# children (i.e. the build.pl instances) can continue to run and won't
|
# children (i.e. the build.pl instances) can continue to run and won't
|
||||||
# have the lock taken away.
|
# have the lock taken away.
|
||||||
my $build;
|
my $build;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$build = $db->resultset('Builds')->find($buildId);
|
$build = $db->resultset('Builds')->find($buildId);
|
||||||
die "build $buildId doesn't exist" unless defined $build;
|
die "build $buildId doesn't exist" unless defined $build;
|
||||||
die "build $buildId already done" if defined $build->resultInfo;
|
die "build $buildId already done" if defined $build->resultInfo;
|
||||||
|
@ -287,7 +287,7 @@ eval {
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
warn $@;
|
warn $@;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$build->schedulingInfo->update({busy => 0, locker => $$});
|
$build->schedulingInfo->update({busy => 0, locker => $$});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ die "The HYDRA_HOME environment variable is not set!\n" unless defined $hydraHom
|
||||||
|
|
||||||
sub unlockDeadBuilds {
|
sub unlockDeadBuilds {
|
||||||
# Unlock builds whose building process has died.
|
# Unlock builds whose building process has died.
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
my @builds = $db->resultset('Builds')->search(
|
my @builds = $db->resultset('Builds')->search(
|
||||||
{finished => 0, busy => 1}, {join => 'schedulingInfo'});
|
{finished => 0, busy => 1}, {join => 'schedulingInfo'});
|
||||||
foreach my $build (@builds) {
|
foreach my $build (@builds) {
|
||||||
|
@ -54,7 +54,7 @@ sub checkBuilds {
|
||||||
|
|
||||||
my @buildsStarted;
|
my @buildsStarted;
|
||||||
|
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
|
|
||||||
# Get the system types for the runnable builds.
|
# Get the system types for the runnable builds.
|
||||||
my @systemTypes = $db->resultset('Builds')->search(
|
my @systemTypes = $db->resultset('Builds')->search(
|
||||||
|
@ -123,7 +123,7 @@ sub checkBuilds {
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
warn $@;
|
warn $@;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$build->schedulingInfo->busy(0);
|
$build->schedulingInfo->busy(0);
|
||||||
$build->schedulingInfo->locker($$);
|
$build->schedulingInfo->locker($$);
|
||||||
$build->schedulingInfo->update;
|
$build->schedulingInfo->update;
|
||||||
|
|
|
@ -111,7 +111,7 @@ sub fetchInputAlt {
|
||||||
# but if it doesn't change (or changes back), we don't get
|
# but if it doesn't change (or changes back), we don't get
|
||||||
# a new "revision".
|
# a new "revision".
|
||||||
if (!defined $cachedInput) {
|
if (!defined $cachedInput) {
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$db->resultset('CachedPathInputs')->create(
|
$db->resultset('CachedPathInputs')->create(
|
||||||
{ srcpath => $uri
|
{ srcpath => $uri
|
||||||
, timestamp => $timestamp
|
, timestamp => $timestamp
|
||||||
|
@ -122,7 +122,7 @@ sub fetchInputAlt {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$timestamp = $cachedInput->timestamp;
|
$timestamp = $cachedInput->timestamp;
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$cachedInput->update({lastseen => time});
|
$cachedInput->update({lastseen => time});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ sub fetchInputAlt {
|
||||||
|
|
||||||
($sha256, $storePath) = split ' ', $stdout;
|
($sha256, $storePath) = split ' ', $stdout;
|
||||||
|
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$db->resultset('CachedSubversionInputs')->create(
|
$db->resultset('CachedSubversionInputs')->create(
|
||||||
{ uri => $uri
|
{ uri => $uri
|
||||||
, revision => $revision
|
, revision => $revision
|
||||||
|
@ -260,7 +260,7 @@ sub checkJob {
|
||||||
$priority = int($job->{schedulingPriority})
|
$priority = int($job->{schedulingPriority})
|
||||||
if $job->{schedulingPriority} =~ /^\d+$/;
|
if $job->{schedulingPriority} =~ /^\d+$/;
|
||||||
|
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
# Mark this job as active in the database.
|
# Mark this job as active in the database.
|
||||||
my $jobInDB = $jobset->jobs->update_or_create(
|
my $jobInDB = $jobset->jobs->update_or_create(
|
||||||
{ name => $jobName
|
{ name => $jobName
|
||||||
|
@ -326,7 +326,7 @@ sub checkJob {
|
||||||
sub setJobsetError {
|
sub setJobsetError {
|
||||||
my ($jobset, $errorMsg) = @_;
|
my ($jobset, $errorMsg) = @_;
|
||||||
eval {
|
eval {
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$jobset->update({errormsg => $errorMsg, errortime => time});
|
$jobset->update({errormsg => $errorMsg, errortime => time});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -370,9 +370,10 @@ sub checkJobset {
|
||||||
fetchInputs($project, $jobset, $inputInfo);
|
fetchInputs($project, $jobset, $inputInfo);
|
||||||
|
|
||||||
# Evaluate the job expression.
|
# Evaluate the job expression.
|
||||||
die "not supported" if scalar @{$inputInfo->{$jobset->nixexprinput}} != 1;
|
|
||||||
my $nixExprInput = $inputInfo->{$jobset->nixexprinput}->[0]
|
my $nixExprInput = $inputInfo->{$jobset->nixexprinput}->[0]
|
||||||
or die "cannot find the input containing the job expression";
|
or die "cannot find the input containing the job expression";
|
||||||
|
die "multiple alternatives for the input containing the Nix expression are not supported"
|
||||||
|
if scalar @{$inputInfo->{$jobset->nixexprinput}} != 1;
|
||||||
my $nixExprPath = $nixExprInput->{storePath} . "/" . $jobset->nixexprpath;
|
my $nixExprPath = $nixExprInput->{storePath} . "/" . $jobset->nixexprpath;
|
||||||
|
|
||||||
(my $res, my $jobsXml, my $stderr) = captureStdoutStderr(
|
(my $res, my $jobsXml, my $stderr) = captureStdoutStderr(
|
||||||
|
@ -402,7 +403,7 @@ sub checkJobset {
|
||||||
my %failedJobNames;
|
my %failedJobNames;
|
||||||
push @{$failedJobNames{$_->{location}}}, $_->{msg} foreach @{$jobs->{error}};
|
push @{$failedJobNames{$_->{location}}}, $_->{msg} foreach @{$jobs->{error}};
|
||||||
|
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$jobset->update({lastcheckedtime => time});
|
$jobset->update({lastcheckedtime => time});
|
||||||
|
|
||||||
foreach my $jobInDB ($jobset->jobs->all) {
|
foreach my $jobInDB ($jobset->jobs->all) {
|
||||||
|
@ -448,7 +449,7 @@ sub checkJobsetWrapped {
|
||||||
if ($@) {
|
if ($@) {
|
||||||
my $msg = $@;
|
my $msg = $@;
|
||||||
print "error evaluating jobset ", $jobset->name, ": $msg";
|
print "error evaluating jobset ", $jobset->name, ": $msg";
|
||||||
$db->txn_do(sub {
|
txn_do($db, sub {
|
||||||
$jobset->update({lastcheckedtime => time});
|
$jobset->update({lastcheckedtime => time});
|
||||||
setJobsetError($jobset, $msg);
|
setJobsetError($jobset, $msg);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue