Fix race between hydra-eval-jobs and hydra-update-gc-roots

If hydra-eval-jobs creates a new root, and hydra-update-gc-roots runs
before hydra-evaluator has had a chance to add the corresponding build
to the database, then hydra-update-gc-roots will remove the root. If
subsequently the Nix garbage collector kicks in, it may remove the
build's .drv file before the build is performed. Since evaluation of
the Nixpkgs and NixOS jobsets nowadays takes a lot of time (e.g. an
hour), the probability of this happening is fairly high.

The quick fix is not to delete roots that are less than a day old. So
long as evaluation doesn't take longer than a day, this should be fine
;-)

Fixes #166.
This commit is contained in:
Eelco Dolstra 2014-07-14 13:18:07 +02:00
parent 114f8a26ee
commit fb5f01097b

View file

@ -2,6 +2,7 @@
use strict; use strict;
use File::Path; use File::Path;
use File::stat;
use File::Basename; use File::Basename;
use Nix::Store; use Nix::Store;
use Hydra::Schema; use Hydra::Schema;
@ -48,9 +49,7 @@ sub keepBuild {
} }
# Read the current GC roots. We need to do that here so that we don't # Read the current GC roots.
# delete roots that were added while we were determining the desired
# roots.
print STDERR "*** reading current roots...\n"; print STDERR "*** reading current roots...\n";
my $gcRootsDir = getGCRootsDir; my $gcRootsDir = getGCRootsDir;
opendir DIR, $gcRootsDir or die; opendir DIR, $gcRootsDir or die;
@ -122,14 +121,23 @@ print STDERR "*** removing unneeded GC roots\n";
my $rootsKept = 0; my $rootsKept = 0;
my $rootsDeleted = 0; my $rootsDeleted = 0;
my $now = time();
foreach my $link (@roots) { foreach my $link (@roots) {
next if $link eq "." || $link eq ".."; next if $link eq "." || $link eq "..";
my $path = "/nix/store/$link"; my $path = "/nix/store/$link";
if (!defined $roots{$path}) { if (!defined $roots{$path}) {
# Don't delete roots that are less than a day old, to prevent
# a race where hydra-eval-jobs has added a root but
# hydra-evaluator hasn't added them to the database yet.
if (lstat($path)->ctime < $now - 24 * 60 * 60) {
print STDERR "removing root $path\n"; print STDERR "removing root $path\n";
$rootsDeleted++; $rootsDeleted++;
unlink "$gcRootsDir/$link" or warn "cannot remove $gcRootsDir/$link"; unlink "$gcRootsDir/$link" or warn "cannot remove $gcRootsDir/$link";
} else {
print STDERR "NOT removing recent root $path\n";
$rootsKept++;
}
} else { } else {
$rootsKept++; $rootsKept++;
} }